Self-Sufficiency and the Store

I’ve written quite a few times about the health and monetary benefits of self-sufficiency at any level (see my Health Benefits of Self-Sufficiency and Health Benefits of Self-Sufficiency (Part 2) posts for details). It’s even possible to put a dollar amount on your self-sufficiency endeavors (see Calculating an Hourly Wage for details). Of course, not everyone can produce as much of their own food as we do, but I encourage you to produce as much as you can. I’ve been reading more and more articles online, like the one entitled “8 Food frauds on your shopping list” that tell me that your local store may be undermining your efforts to eat well. In this article, you discover that the milk you just bought could actually be causing problems like kidney stones because it contains melamine. In addition, there are all sorts of problems with food contamination. Reports such as “How safe is your grocery store?” give me pause when considering any sort of food purchase. Certainly, reading articles like this have significantly changed our buying habits for the small number of luxury items we purchase each year. They should affect your buying habits too.

At one time, most stores received the majority of their products from local sources. If you bought a steak, it probably came from a beef cow that was butchered and processed locally—perhaps from a neighbor who raised beef cattle. The same held true for all sorts of items—vegetables and fruits alike. The items you purchased from the store had a lower carbon footprint because they didn’t involve transportation over long distances. In addition, these items were fresher; many of them were never even frozen. During this time, it was actually possible to trace a source of problem food directly to the grower because the grocery knew the grower and what kinds of products the grower produced. Our food was considerably safer as a result.

Three things have happened to make the day of locally grown food pass into history. First, people have developed a taste for a wider variety of foods. This isn’t necessarily a bad idea because variety does tend to help create a better diet, but the cost has been felt on a global scale in the increase of everything from pesticide use to increased greenhouse gasses. I’ll leave it to you to decide whether breathing contaminated air and drinking poisoned water is really worth the health benefits of a diverse diet. Add to your calculations the chances that the food you’re eating is something other than advertised because your store may misrepresent the product to you in order to make a sale.

Second, people have placed price as their first (and sometimes only) criterion for making a food purchase. Locally grown food is sometimes more expensive than the food you can obtain from another location. Gone is the day where the grocery store owner knew precisely who grew what in the store. Today you can ask your butcher where a piece of meat comes from and you’ll receive a shrug (at best) for your efforts. The apple you’re eating could come from Washington, but it could just as easily come from some other country. There really isn’t a good way to know.

Third, governments have placed an emphasis on the global economy for various reasons. You get goods from overseas in many cases because the government has decided that you’ll get those goods for political reasons. A trade pact may require America to buy so many items from China in exchange for some other concession. I’ve written before about the global economy (see Considering the Inefficiency of a Global Economy for details). From my perspective, unless another country truly does own the market for a particular product, using locally grown or produced items makes far more sense and using these local items (even if they cost more) would significantly reduce our global pollution problems, not to mention making the local economy work better.

Whether you agree with me on these issues or not, it’s still a matter of choosing whether you’ll continue to buy products of dubious quality and origin. Besides becoming more self-sufficient, you can take control of your food sources by going to a local farmer’s market or choosing to spend more at smaller stores where the grocer actually does know the source of the food that the store sells. Yes, you’re going to pay more for the food you buy this way and it may not always be convenient to obtain the variety of food that you want, but the food you eat will be higher quality and less susceptible to contamination. When you do encounter a problem with your food, you can actually do something about it.

When is the last time you chose quality over price? Are you spending time thinking about what you eat and whether the food is actually good for you? Let me know at John@JohnMuellerBooks.com.

 

Exploring the TypingBuddy Application (Part 11)

The TypingBuddy application is finally nearing completion. During the previous post, Exploring the TypingBuddy Application (Part 10), you discovered how the timing works within the application for controlling typing time. This week’s post will complete the basic TypingBuddy application. It consists of a few remaining event handlers for controls on frmMain. The three buttons in question, Save Settings (btnSet), Minimize Window (btnMinimize), and Change Messages (btnChangeMessages) have the following event handlers attached to their Click events.

private void btnSet_Click(object sender, System.EventArgs e)
{
   // Set the controls.
   TimeLeft = (Int32)TypingInterval.Value * 60;
 
   // Obtain the current settings.
   Settings.DisplayMessageIcon = chkShowIcon.Checked;
   Settings.PlaySound = chkPlaySound.Checked;
   Settings.RestInterval = (Int32)RestingValue.Value;
   Settings.TypingInterval = (Int32)TypingInterval.Value;
 
   // Save the settings.
   TBMessages.SaveSettings(Settings);
 
   // Start the timer.
   TypeTimer.Start();
}
 
private void btnMinimize_Click(object sender, System.EventArgs e)
{
   // Minimize the form.
   WindowState = FormWindowState.Minimized;
   ThisFrmMain.Hide();
}
 
private void btnChangeMessages_Click(object sender, EventArgs e)
{
   // Create the required form.
   frmMessages ManageMessages = new frmMessages();
 
   // Display the form.
   ManageMessages.ShowDialog(this);
 
   // Upon return from the form, make sure you reload the settings.
   Settings = TBMessages.LoadSettings();
}

When the user clicks Save Settings, the application begins by updating the amount of time left to the new time setting the user has provided. The application then obtains updates for all of the required properties in Settings, and then saves Settings to disk. Remember the Settings is a global variable that contains the application settings and also contains a list of custom messages that the user wants to use with the application. After Settings is saved, the application starts the typing timer by calling TypeTimer.Start().

One of the mistakes that developers make when creating an application that resides in the Notification Area is to hide the form. The btnMinimize_Click() event handler shows the proper way to handle things. You minimize the form and then hide it from view. This technique ensures that the user doesn’t see anything in the Taskbar or on screen.

Previous posts have described the entire process for creating and managing messages in detail. However, you still haven’t seen the call that starts the entire process. When the user clicks Change Messages, the application calls on the btnChangeMessages_Click() event handler. The event handler creates an instance of frmMessages, ManageMessages, and displays the form on screen. Remember that for reliability purposes, this process is pretty much self-contained. When the user closes the form, the event handler calls TBMessages.LoadSettings() to update Settings with the message changes that the user had made.

At this point, you have a copy of TypingBuddy as it exists today. Of course, application developers are always tweaking their applications to make them better. What sorts of changes can you suggest for TypingBuddy? Let me know at John@JohnMuellerBooks.com.

 

Self-Sufficiency and Technology

One of the things I’ve been curious about lately is how much of a role technology plays in current self-sufficiency efforts. For example, there has been a strong emphasis on heirloom (heritage) plants and animals, rather than using plants and animals that science has helped to produce, because these older varieties offer things that modern science can’t. For many people, the main reason to rely on heirloom varieties is that they always produce the same thing. If you plant an heirloom seed, you get the expected plant, rather than something mysterious that results from hybridization. Of course, there are people of the opposite persuasion who feel that that older varieties lack the benefits that science can provide, such as increased yield or better taste.

Technology also affects technique. Some people eschew modern machines and do all of the work required to meet their self-sufficiency requirements by hand. The benefits are that the carbon footprint of such efforts is incredibly small, costs are low, and the results often better. Using technology makes things faster and easier. Just how much technology you use depends on the size of your work area and the results you expect. Many people use a combination of hand and machine techniques.

Most people recognize that the use or lack of technology has a significant impact on the outcome of self-sufficiency efforts. In addition, the choices we make affect our neighbors and the planet to some degree. Choosing the best options for one scenario often lead to problems in another. That’s why there is growing debate over just how much technology is good for those who engage in self-sufficiency as I do. I’m constantly looking for a better answer—one that produces good results with a minimum of effort, but is also good for the planet.

The question that I have pondered most as of late is how technology affects the presentation of information.  The problem for anyone writing about self-sufficiency is that no one really knows for sure just how people get self-sufficiency information. For example, do you rely heavily on questions you ask online to obtain information? Would you purchase e-books instead of the paper variety in order to reduce the cost of the information, while also reducing the effects of producing paper on the planet? In order to do a good job of providing information to you, I need to know how you communicate. Let me know your ideas on the topic at John@JohnMuellerBooks.com.

 

Finishing the Chicken Coop Structure

Last week you saw the roof raised on the chicken coop (see Raising the Chicken Coop Roof). There are still a number of steps to accomplish before the chicken coop is ready for occupancy. This week, you’ll see some of the finishing steps performed to make the chicken coop more habitable before adding the exterior treatment.

Of course, one of the more important tasks is to create a set of stairs to get into the coop. As things stand now, you need mighty long legs to get into the coop. Kevin puzzled over the dimensions of the stairs for a while and then came up with steps with a 7-inch rise.

ChickenCoop0601

This is one of the only places where we used new wood, partly because we didn’t have any wood the right size. The stringer (the part that goes from the top of the coop to the ground and holds the treads) has to be strong enough to hold up under the conditions in which it will be used. We relied on pressure treated lumber in this case because it’s the best option available in this case. The stairs rely on 8″ treads. As you can see from the picture, there is a back support for strength.There aren’t any risers in this case because they would probably get in the way during the winter when trying to clear the treads of snow. After the steps were finished, we put them in place.

ChickenCoop0602

To make things safer, Kevin also added a handrail and post. In order to get the steps to fit properly, I needed to level the ground out, which required a bit of digging. The black earth shown in the picture will be added to the garden. At some point, I’ll even things out more and add some gravel to create a non-slip pathway for the winter and wet days. The stairs will receive a coat of primer, and then a coat of non-skid paint.

Remember that the roof is at an angle and the ceiling joists are flat. So, there is currently no way to nail any sort of cladding to the exterior of the coop. In order to provide a nailing surface, Kevin and I both built triangles to fit into the space between the ceiling joist and the roof like this one here (Kevin’s is much better than the one I created, but he also has a lot more experience than I do).

ChickenCoop0603

We each added a couple of studs to the triangle to make it easier to nail things in place. The coop also requires a window. The coop we took down had three windows, so we chose the best looking of the three and used it for the new coop. The window required a little work, but it now slides as it should and will provide both light and ventilation for the chickens.

ChickenCoop0604

Kevin had also gotten up early this particular morning and put the tar paper on the roof. Actually, the coop isn’t quite ready for tar paper yet. We’re still putting the cladding in place. Most of the cladding is plywood that we obtained from the old coop. In a few places, we also used oriented-strand board (OSB) because the old coop had plenty of it as well.

One of the items that I managed not to get a picture of is the window in the back of the coop. We put on up near the top of the coop in the extension used to hold the roof in place. You’ll see a couple different pictures of this addition later in the process. For now, just keep in mind that we built a window back there to allow cross flow during the summer months. The window seals tightly during the winter months to help keep things warmer in the coop.

Once we got all of the cladding in place, it was time to complete the tar paper. We used staples to hold the tar paper in place. When putting the tar paper on, you start at the bottom and move up. That way, the overlaps work with gravity to keep water from getting under the tar paper. The completed coop looks like this:

ChickenCoop0605

Next week we’ll take a bit of a sidetrack. The coop is eventually going to be completely covered with corrugated metal. The long lasting covering will never require replacement and will help keep the coop daft free. Unfortunately, we needed to bend the corrugated metal, which is easier said than done. You’ll learn the technique we used to accomplish the task. In the meantime, let me know if you have any questions about this stage of the coop building process at John@JohnMuellerBooks.com.

 

A History of Microprocessors

Every once in a while, someone will send me a truly interesting link. Having seen a few innovations myself and possessing a strong interest in history, I read the CPU DB: Recording Microprocessor History on the Association for Computing Machinery (ACM) site with great interest. The post is a bit long, but essentially, the work by Andrew Danowitz, Kyle Kelley, James Mao, John P. Stevenson, and Mark Horowitz does something that no other site does, it provides you with a comprehensive view of 790 different microprocessors created since the introduction of Intel’s 4004 in November 1971. The CPU DB is available for anyone to use and should prove useful for scientist, developer, and hobbyist alike.

Unlike a lot of the work done on microprocessors, this one hasn’t been commissioned by a particular company. In fact, you’ll find processors from 17 different vendors. The work also spans a considerable number of disciplines. For example, you can discover how the physical scaling of devices has changed over the years and the effects of software on processor design and development.

A lot of the information available in this report is also available from the vendor or a third party in some form. The problem with vendor specification sheets and third party reports is that they vary in composition, depth, and content—making any sort of comparison extremely difficult and time consuming. This database makes it possible to compare the 790 processors directly and using the same criteria. A researcher can now easily see the differences between two microprocessors, making it considerably easier to draw conclusions about microprocessor design and implementation.

Not surprisingly, it has taken a while to collect this sort of information at the depth provided. According to the site, this database has been a work in progress for 30 years now. That’s a long time to research anything, especially something as esoteric as the voltage and frequency ranges of microprocessors. The authors stated their efforts were hampered in some cases by the age of the devices and the unavailability of samples for testing. I would imagine that trying to find a usable copy of a 4004 for testing would be nearly impossible.

You’ll have to read the report to get the full scoop of everything that CPU DB provides. The information is so detailed that the authors resorted to using tables and diagrams to explain it. Let’s just say that if you can’t find the statistic you need in CPU DB, it probably doesn’t exist. In order to provide a level playing field for all of the statistics, the researchers have used standardized testing. For example, they rely on the Standard Performance Evaluation Corporation (SPEC) benchmarks to compare the processors. Tables 1 and 2 in the report provide an overview of the sorts information you’ll find in CPU DB.

This isn’t a resource I’ll use every day. However, it is a resource I plan to use when trying to make sense of performance particulars. Using the information from CPU DB should remove some of the ambiguity in trying to compare system designs and determine how they affect the software running on them. Let me know what you think of CPU DB at John@JohnMuellerBooks.com.

 

Choosing the GNU C++ Compiler

A number of readers have written to ask me about the reason I chose the GNU C++ computer for C++ All-In-One Desk Reference For Dummies. After all, there are many different C++ compilers on the market today. Actually, my coauthor, Jeff Cogswell, was the one who made that decision, but after listening to his reasons and researching a few of my own, I had to agree that the GNU C++ Compiler was the best choice at the time (and still is from many perspectives):

 

  • Standards Adherence: From what I’ve read and seen in my own coding efforts, the GNU C++ compiler adheres a bit better to the current standards. There are other compilers, such as Microsoft’s Visual C++, that include a host of special additions and exceptions that don’t adhere to the standard. Given the audience for this book, using a compiler that’s strong on the standards is a must. (This book doesn’t use any of the GNU C++ extensions.)
  • Cross-Platform Compatibility: This book has a mixed audience. I’ve received so many e-mails from Macintosh readers that I’ve provided a number of blog posts just for this group. Linux developers also like this compiler and have used my book to learn how to use it. Because there are so many different platforms that this compiler works on, I can reach a much broader audience with the book and help more people write applications in C++ as a result.
  • CodeBlocks Support: In order to write good C++ code, you really do need a good IDE. CodeBlocks is a free compiler that works well on Linux, Macintosh, and Windows machines. If we had chosen another compiler, it may not have been possible to provide great support for all three platforms and some readers would have been left out.
  • Community Support: Both the GNU C++ compiler and CodeBlocks enjoy a broad range of support from the open source community. Getting help with either product is relatively easy and normally free.
  • Cost: Many of the readers of this book are students of limited means or are hobbyists learning to write C++ applications on a shoestring. Using the GNU C++ compiler coupled with CodeBlocks offers a method of teaching C++ programming that doesn’t require any investment by the reader. If cost hadn’t been a factor, there are probably other compilers on the market that might be a little better choice than using GNU C++.


Yes, we could have used some other compiler when writing our book, but at the time, GNU C++ seemed to be the best choice available and I still think it’s the best choice today. Of course, it’s always nice to hear about alternatives. If you think there is a strong competitor for GNU C++ that’s free and runs on all three of the target platforms for this book, let me know at John@JohnMuellerBooks.com. Make sure you provide me with complete information, including a URL for the compiler’s site.

 

Exploring the TypingBuddy Application (Part 10)

In the previous installment of this series, Exploring the TypingBuddy Application (Part 9), you discovered how the Notification Area menu works in Typing Buddy. The main purpose of this menu is to control the timer used to determine when it’s time to stop typing. Given the purpose of TypingBuddy, keeping you from working so long as to cause repetitive stress injuries, the most important code is that used by the timer to display the stop message.

The TypeTimer control relies on the Timer_Tick() event handler to respond to Tick events once each second. The Timer_Tick() method must accomplish the following tasks:

 

  • Update the counter.
  • Check for an end of typing time condition.
  • Choose a message.
  • Ensure the message falls within the required parameters for display.
  • Display the message on screen.


With these criteria in mind, it’s time to look at the method code. The following code shows how the Timer_Tick() method performs its assigned tasks.

private void Timer_Tick(object sender, System.EventArgs e)
{
   frmMessage MyMessage;   // Message dialog box.
   DialogResult RetVal;    // Return value from dialog.
   Int32 Count = 0;        // Number of messages.
   Random Rand;            // A random number generator.
   Int32 Selection = -1;   // The message selection.
 
   // Set the timer value.
   TimeLeft--;
 
   // See if the timer has expired.
   if (TimeLeft == 0)
   {
      // Determine whether there are any messages to use.
      if ((Settings.MessageList != null) &&
         (Settings.MessageList.Length > 0))
      {
         // Obtain the current number of messages.
         Count = Settings.MessageList.Length;
 
         // Randomize a specific message.
         Rand = new Random(DateTime.Now.Millisecond);
         Selection = Rand.Next(Count);
 
         // Ensure that the message isn't one that has a date set.
         if (Settings.MessageList[Selection].UseDates)
 
            // When the message does have the date set,
            // ensure the date is in the right timeframe.
            Selection = VerifySelection(
               Settings.MessageList, Selection);
      }
 
      // Create the new dialog box.
      if (Selection == -1)
      {
         if (chkShowIcon.Checked)
            MyMessage = new frmMessage(
               (Int32)RestingValue.Value * 60,
               chkShowIcon.Checked);
         else
            MyMessage = new frmMessage(
               (Int32)RestingValue.Value * 60);
      }
      else
      {
         if (Settings.MessageList[Selection].ShowTitle)
         {
            if (chkShowIcon.Checked)
               MyMessage = new frmMessage(
                  (Int32)RestingValue.Value * 60,
                  Settings.MessageList[Selection].Title,
                  Settings.MessageList[Selection].MessageText,
                  chkShowIcon.Checked);
            else
               MyMessage = new frmMessage(
                  (Int32)RestingValue.Value * 60,
                  Settings.MessageList[Selection].Title,
                  Settings.MessageList[Selection].MessageText);
         }
         else
         {
            if (chkShowIcon.Checked)
               MyMessage = new frmMessage(
                  (Int32)RestingValue.Value * 60,
                  Settings.MessageList[Selection].MessageText,
                  chkShowIcon.Checked);
            else
               MyMessage = new frmMessage(
                  (Int32)RestingValue.Value * 60,
                  Settings.MessageList[Selection].MessageText);
         }
      }
       
      // Reset the controls.
      TimeLeft = (Int32)TypingInterval.Value * 60;
      TypeTimer.Stop();
 
      // Play the required sound when necessary.
      if (chkPlaySound.Checked)
         if (Selection != -1)
         {
            switch (Settings.MessageList[Selection].Sound)
            {
               case TBMessages.SystemSoundList.None:
                  break;
               case TBMessages.SystemSoundList.Asterisk:
                  System.Media.SystemSounds.Asterisk.Play();
                  break;
               case TBMessages.SystemSoundList.Beep:
                  System.Media.SystemSounds.Beep.Play();
                  break;
               case TBMessages.SystemSoundList.Exclamation:
                  System.Media.SystemSounds.Exclamation.Play();
                  break;
               case TBMessages.SystemSoundList.Hand:
                  System.Media.SystemSounds.Hand.Play();
                  break;
               case TBMessages.SystemSoundList.Question:
                  System.Media.SystemSounds.Question.Play();
                  break;
            }
         }
         else
            System.Media.SystemSounds.Beep.Play();
 
      // Display the stop typing message.
      RetVal = MyMessage.ShowDialog(this);
 
      if (RetVal == DialogResult.OK)
         // Restart the timer.
         TypeTimer.Start();
      else
         // Exit the application.
         Close();
   }
   else
   {
      // Update the time left indicator.
      txtTypingTime.Text = TimeLeft.ToString();
      ThisNotifyIcon.Text = "Time Left in Seconds: " +
                         TimeLeft.ToString();
   }
}

Let’s start with the simple decision. Most of the time, the code will decrement TimeLeft and find that it still isn’t 0. When this condition occurs, the code updates the Time Left (in Seconds) field of frmMain to display the new typing time left value. In addition, you might remember that the the user can also see the amount of time left by hovering the mouse over the icon in the Notification Area. In order to accomplish this task, the code updates the ThisNotifyIcon.Text property value.

Of course, the alternative to updating the timer is to display a message. The Selection variable contains the number of the message to display. It begins with a value of -1 to indicate there is no message to display. When the user has configured custom messages, the code chooses a random message number from that collection and places it in Selection. However, the selection process must consider whether the selected message is only usable on a specific date. When this occurs, the code calls VerifySelection() with the list of available messages and the current selection from that list. Don’t worry about the inner workings of this method for now; you’ll see it in operation later.

When the user hasn’t configured any custom messages, Selection remains at -1 and the application displays the standard message that’s provided as part of the frmMessage configuration. The only decision left to make at this point is which constructor to call. When the user wants to display an icon, the code passes the value of chkShowIcon.Checked to the constructor. Otherwise, all it passes is the amount of resting time that the message should use.

Creating the message box when working with custom messages becomes a little more complex because the code must now decide whether the user wants the message title displayed or not (along with the decision about displaying an icon). When the user wants to display both the title and a custom message, the code uses constructors that pass along Settings.MessageList[Selection].Title and Settings.MessageList[Selection].MessageText; otherwise, the code simply passes the message text to the constructor.

Now that the application has a message box ready to display, it’s time to reset the count and stop the timer. The user may want to hear a sound when the message displays. To accommodate this need, the code looks at chkPlaySound.Checked. When this property is true, it then checks for a standard or custom message and plays the appropriate sound. Note that when the custom message has TBMessages.SystemSoundList.None selected, the application doesn’t play a sound even when chkPlaySound.Checked is true.

Everything is ready to display the message to the end user. The user either waits for the rest time counter to count down or clicks Quit to exit TypingBuddy. When the user waits for the rest period to end and clicks Continue, the code restarts the timer, which starts a new typing cycle. Otherwise, the application ends.

These are the basics of how the timer handling works. However, there is another issue to consider, verifying that the message is within the correct date span. The VerifySelection() method handles this requirement as shown here.

private Int32 VerifySelection(TBMessage[] List, Int32 Selection)
{
   // Check the date range.
   if ((DateCompare(Settings.MessageList[Selection].StartDate) <= 0) &&
      (DateCompare(Settings.MessageList[Selection].EndDate) >= 0))
 
      // The Selection value is in the correct date range.
      return Selection;
 
   // Obtain the message list count.
   Int32 Count = List.Length;
 
   // When the selected message isn't in the right timeframe, look for
   // a message that does fit within the required timeframe starting
   // from the current selection.
   for (Int32 NewSel = Selection + 1; NewSel < Count; NewSel++)
 
      // Check to see if the message has a date usage requirement.
      if (List[NewSel].UseDates)
      {
 
         // Check the date range.
         if ((DateCompare(List[NewSel].StartDate) <= 0) &&
            (DateCompare(List[NewSel].EndDate) >= 0))
 
            // Return a new selection.
            return NewSel;
      }
      else
 
         // This entry doesn't use a date, so return it.
         return NewSel;
 
   // We've gotten to the end of the list without finding a message.
   // Let's start over from the beginning of the list.
   for (Int32 NewSel = 0; NewSel < Selection; NewSel++)
 
      // Check to see if the message has a date usage requirement.
      if (List[NewSel].UseDates)
      {
 
         // Check the date range.
         if ((DateCompare(List[NewSel].StartDate) <= 0) &&
            (DateCompare(List[NewSel].EndDate) >= 0))
 
            // Return a new selection.
            return NewSel;
      }
      else
 
         // This entry doesn't use a date, so return it.
         return NewSel;
 
   // There aren't any messages in the database that will work in
   // the current date range. In short, even if there are messages
   // in the list, it's as if the list is empty because none of the
   // message are useful.
   return -1;
}

The code begins by verifying that the date is within correct time span. However, you don’t want to compare years, just the month and day, so the code relies on a special method, DateCompare(), to perform this task. You’ll see how DateCompare() works in a few moments, so don’t worry about it for now. All you really need to know is that DateCompare() returns -1 when the date supplied as part of the custom message is less than the current date, 0 when the month and day are precisely the same as the current date, and 1 when the custom message date is greater than the current date. When the selected message dates fall within the current date, then the code returns the current selection.

There are going to be times when the custom message dates won’t include the current date. When that happens, the application begins with the current message and looks at each message in the list until it gets to the end of the list looking for a message that either doesn’t have the dates set (which means that the reader would like to see the message at any time) or the current date falls within the message date. The application uses a simple for loop to perform the task.

It could happen that none of the custom messages starting with the current message to the end of the list are useable. In this case, the code starts looking for a useable message from the beginning of the list to the original selected message location. The criteria are the same—the code looks for a message that has no dates set or one that has a range that includes the current date.

The application has reviewed the entire list of custom messages at this point. It could happen that the application gets to the end of the list and still doesn’t find a useable message. When that situation occurs, VerifySelection() returns -1. If you review the Timer_Tick() code, you’ll see that this condition automatically selects the default message, even if the user has created custom messages.

The .NET Framework doesn’t provide an easy method for comparing just the month and the day of two dates. You usually compare the entire date or a specific date element. In order to perform the correct level of comparison, the example uses the DateCompare() method shown here.

private Int32 DateCompare(DateTime Date)
{
   // Create variables to hold the checked date information.
   Int32 Month = Date.Month;
   Int32 Day = Date.Day;
 
   // Create variables to hold the current date information.
   Int32 CMonth = DateTime.Now.Month;
   Int32 CDay = DateTime.Now.Day;
 
   // Compare the month and the day.
   if (Month < CMonth)
 
      // When the month specified by the user is less
      // than the current month, return -1.
      return -1;
   else if (Month > CMonth)
 
      // When the month specified by the user is greater
      // than the current month, return 1.
      return 1;
 
   // The months are equal, so look at the dates.
   else
   {
      if (Day < CDay)
 
         // When the day specified by the user is less than
         // the current day, then reutrn -1.
         return -1;
      else if (Day > CDay)
 
         // When the day specified by the user is greater
         // than the current day return 1.
         return 1;
      else
 
         // Both the month and the day are equal, so
         // return 0.
         return 0;
   }
}

As you can see, this method first compares the month values. When the two months are precisely equal, it then compares the day values. When the two days are also equal, the method returns a value of 0. Otherwise, the method returns either -1 or 1 depending on whether the custom message date is greater or less than the current date.

Now you have the code for the entire application except for the event handlers in frmMain itself. Next week you’ll receive this last bit of code. In the meantime, let me know if you have any questions at John@JohnMuellerBooks.com. You can see the next post in this series at Exploring the TypingBuddy Application (Part 11).

 

Considering the Fracus Over E-book Pricing

There is currently a debate raging over e-book pricing. The charge is one of price fixing and the debate affects all book publishers. Even though the press has focused on Apple as the target of the DOJ investigation, the lawsuit also affects five other publishers:
Hachette, HarperCollins, Macmillan, Penguin and Simon & Schuster. Of these six defendants, three have settled with the DOJ: Harper Collins, Simon & Schuster and Hachette. Any time vendors collude to set prices at a specific level in order to improve profits its price fixing. The law is blind as to the reasons behind the price fixing, engaging in this practice is simply against the law for any reason.

The supposed point of the lawsuit is to restore competition to the market, but more than a few people see the opposite happening if the DOJ is successful. There is a fly in the ointment—Amazon. It seems that Amazon has actually been pricing e-book content at a loss in order to promote the Kindle, which has placed the publishers in an unenviable position of losing money in order to remain competitive. Healthy debate on the topic is probably the best course of action until all of the facts come to light, but debate of this sort seldom occurs. Some industry pundits are wondering whether Amazon should be part of the lawsuit as well. A few have come outright as said that Amazon is more of a problem than the publishers for these reasons:

 

  • The lawsuit will hurt professional authors financially.
  • A decrease in compensation will end up closing smaller bookstores.
  • After Amazon buries the competition, buyers will be hurt by the resulting monopoly.
  • Lower book prices will adversely affect the quality of content available to readers.
  • Amazon already uses the same model as the publisher do, but hasn’t been named in the lawsuit.

At issue here is one of fairness and eventual results. It’s not fair for the DOJ to pursue price fixing for some actors in this situation and not others. If the DOJ is serious about fixing the current problems with competition in the e-book market, then it really does need to do something about Amazon. Otherwise, the result will be a monopoly that will hurt everyone and prove incredibly expensive to fix. More importantly, professional authors already make little money for their efforts—squeezing them further will definitely result in a loss of the incredible wealth of professional authors available today. Fixing the problems in the market will prove considerably more easy than trying to reestablish the author base used to write books when they become completely unprofitable.

Perhaps this will eventually work out in an unexpected way. This may be yet one more nail in the coffin that is currently killing books as a means for exchanging information. The interactive tools that are being developed for use with books may eventually take over and everyone will use them for all sorts of training. However, these tools are currently in their infancy and authors like me are only now beginning the build the expertise required to use them. In the meantime, it’s important that the DOJ take appropriate actions to ensure true competition in the marketplace.

What is your take on this issue? Do you even read books anymore to obtain information or do you rely on some other source? Let me know at John@JohnMuellerBooks.com.

 

Raising the Chicken Coop Roof

If you’ve been following along with this series of posts, you saw the walls go up in last week’s post, “Adding Walls to the Chicken Coop.” This week, you’ll see how we put a roof on the chicken coop.

Getting a roof put on was a bit of a challenge because we had no heavy equipment with which to work. In addition, the chicken coop isn’t on flat ground—in fact, the slope in the front of the coop is significant. So, with a lack of flat ground and no heavy equipment, trying to get the roof in place was an experiment in leverage and ingenuity.

To begin with, we built the roof section on the ground.  One end, the end that will rest directly on the ceiling joists, is closed off. The other end, the one that will eventually rest on an extension, has been cut at an angle and is open.

The roof section is two feet wider than the floor to account for the roof slope. In addition, the roof is sloped toward the south so that the sun will hit it full force during the winter months and keep the snow off. In addition, sloping the roof toward the south, the same direction as the slope of the hill on which the chicken coop is built, keeps the water from any rains from flowing under the chicken coop and possibly washing out the supports.

To begin the process of raising the roof, we tilted it against the back of the building. Three of us raised the back end at that point.

ChickenCoop0501

After the roof was in place, the three of us worked at squaring the front of the roof with the front of the chicken coop.  Notice the 2 X 4 sticking out of the top of the right side of the front of the roof.

ChickenCoop0502

When the roof was squared, we added another 2 X 4 to the other end of the front of the coop and then moved the roof out the amount of space required for the overhang. The two 2 X 4 pieces are then attached to the roof using a single screw so that the entire assembly acts as a hinge we can use to keep the roof in place while creating the slope at the back of the roof.

ChickenCoop0503

At this point, we attached two long 2 X 4s to each end of the back of the roof. The 2 X 4s are precisely the same length. We used them to raise the back of the roof up and then hold it in place.

ChickenCoop0504

With the roof raised, Kevin added an extension to the back wall of the chicken coop. We secured the extension to the ceiling joist of the back wall and then lowered the roof onto it. The back of the roof was then secured to the extension and then we secured the front of the roof to the ceiling joist of the front of the chicken coop. The result looks like this:

ChickenCoop0505

At this point, the roof is in place. Next week I’ll talk about a number of items required to finish the basic coop structure. In the meantime, please let me know if you have any questions at John@JohnMuellerBooks.com.

 

Updating Application Code

At some point, every application requires update. It’s impossible to write the perfect application that never requires one. Something will happen to cause an issue that requires attention. If you happen to work at the same place for a long time, you may be the one to update your own code, but in reality, that rarely happens. It’s far more likely that you’re going to inherit code written by someone else. That’s why I wrote “How to Inherit Somebody Else’s Code” for Software Quality Connection (SQC). The article was posted yesterday and provides you with some interesting ways to make your application update easier.

My basic motivation in writing the article for SQC was to provide some insights into a process that most developers abhor. Code written by someone else often appears poorly written because the updater would have used a different technique. Any comments will appear nonsensical or useless because it’s hard to understand the thought process the other developer used to create the code. The problem is one of communication. You may not have any contact with the developer who wrote the application, so the only information you have is the documentation and code that the developer left behind. There is no one there to answer your questions, making delving into the code frustrating.

Updating another developer’s code need not be a painful experience. In many cases, once you understand the application, it does appear well (or at least adequately) written and the comments begin to make sense. My article helps you get to that point faster. Rather than waste time thinking about how much of an idiot your predecessor is, you can spend more time getting the update done early and then knocking off the weekend. In many respects, updating other developer’s code is a good experience—it exposes you to new coding techniques and helps you see things in ways that you might not have thought about on your own.

Of course, I’m always looking for ways to make my writing better. If you have any insights you’d like to share or have some ideas for content you’d like to see, leave a comment here or contact me at John@JohnMuellerBooks.com. You can also leave comments on SQC and I’ll be sure to see them.