Keep a Window on Top

Apr 12, 2008
This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

This article covers how to force your main program window to stay at the top of the Z-order. Making child views in an MDI application remain on top of other views is a completely different beast, so do not try to use this code for that purpose.

By using the single line of code below, you can force any dialog window to remain on top of all other windows (in the Z-order):

SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);

The first parameter passed to the SetWindowPos() function is a pointer to a window that will precede the current CWnd object in the Z-order. Since I want my window to be top most, a pointer to the value wndTopMost does the trick.

Forcing a frame window for an SDI or MDI application to remain on top uses a similar process, with one slight modification:

CWnd* pMainWnd = AfxGetMainWnd();
pMainWnd->SetWindowPos(&wndTopMost, 0, 0, 0, 0,
                       SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);

Note that we first have to get a pointer to the main window frame, then use that pointer to call the frame window's SetWindowPos() method.

Restoring Windows to Normal

To restore a dialog window back to a normal state, use the following code:

SetWindowPos(&wndNoTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);

In this case, a pointer to the value wndNoTopMost is used. Likewise, the following code will work with a frame window:

CWnd* pMainWnd = AfxGetMainWnd();
pMainWnd->SetWindowPos(&wndNoTopMost, 0, 0, 0, 0,
                       SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);

In order to fully understand the other parameters used in this method, you should check out the online documentation for the SetWindowPos() method at the MSDN library.

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

The CListView class (a simple variant of the CListCtrl class) is a useful way to provide a resizable, list control application. But you may have noticed that the columns in "report" mode do not resize themselves when the user resizes the program window. So how can we make the columns fit nicely in the list view, without the need for a horizontal scroll bar? The answer is simpler than you may have thought!

Sizing the Columns

By overriding the WM_SIZE event for the view class, we can handle sizing the columns neatly. The following example will have four columns total, two of which will be proportional to the window size, and two of which will be a fixed size (no matter how large the window gets). Here is the code:

void CPaperplusView::OnSize(UINT nType, int cx, int cy) 
{
    CListView::OnSize(nType, cx, cy);

    if(m_List->GetSafeHwnd() != NULL)
    {
        // Get the list control size
        CRect r;
        m_List->GetClientRect(&r);

        // Figure out how large the column interval should be
        int scrollwidth = GetSystemMetrics(SM_CXVSCROLL);
        int listwidth = r.Width() - scrollwidth - 80 - 80;
        int col_interval = listwidth / 10;

        m_List->SetColumnWidth(0, col_interval * 6);
        m_List->SetColumnWidth(1, col_interval * 4);
        m_List->SetColumnWidth(2, 80);
        m_List->SetColumnWidth(3, 80);

        m_List->ShowScrollBar(SB_HORZ, FALSE);
    }
}

The first method call that appears in this code block was inserted by ClassWizard and is simply a call to the base class (to allow the normal processing to get handled). I then make sure that the pointer to my list control (m_List in the above code block) is not an illegal value. This prevents some hairy problems when the program is first starting up.

Understanding the Code

First, I get the client size of the list control and store it in a CRect variable. The next call is to the GetSystemMetrics() function, into which I pass the SM_CXVSCROLL parameter. This method call is used to get the width of the Windows vertical scroll bar. We need to provide space for this scroll bar so that the columns will fit nicely when the vertical scroll bar does indeed show up. I next determine how much space I have left over to work with. After subtracting out the scroll bar width, as well as the two fixed size columns (both of which are 80 pixels wide), I have the size available to the first two, proportional columns. I then divide that remaining space into 10 parts, for use with the variable-sized columns.

Next, I actually set the column widths. You will note that the first column will take up 60% of the available space (excluding the vertical scroll bar and fixed-size columns). Likewise, the second column will take up the remaining 40% of "free" space. After those two columns are dealt with, I make sure that the last two columns are sized appropriately (to their fixed values of 80 pixels).

The final step is to make sure that the horizontal scroll bar never shows up. I'm not 100% sure if we only need to make this call one time, but I figure that it doesn't hurt to turn off the horizontal scroll bar every time a size event gets called. Why is this a necessary line of code if we are going to great pains to size things correctly? Well, for some strange reason, when a window is horizontally sized smaller than its initial value, the scroll bar still shows up, even though there is nothing that is scrolled off screen to either the left or right! There is definitely a strange bug at work here, so I took this means of 'fixing' it.

You may notice that even though we have done a great deal of work to make things fit nicely, there will occasionally be some left over space in the list control header. This space always shows between the vertical scroll bar and the last column. Why isn't this space used like it should be? The simple answer is that we are doing integer division when dividing up the list size into "available" space. So, that little bit that was truncated from that integer division problem constitutes for this remainder. The easiest fix for this is to simply add the "left over" pixel width to the final column. Then your columns will always fit perfectly!

So there you have it. Dynamic column sizing is a quick addition to any program that utilizes the CListView and is sure to make your program look that much more professional.

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

Snapping a window to the screen edge has long been an interesting effect. Winamp is probably the best application to use as an example, as all of its associated windows can snap to the screen edge (and to each other). So how can you make your application do the same thing? It's actually not too difficult!

You will find that some "window snap" code snippets require that the "Show Window Contents While Dragging" option be turned on in Windows. Thankfully, the following code doesn't have that restriction. This article will use a dialog based application as the example, and will refer to the "dialog's class" as the class to add code to. This same effect can be added to document-view based applications (SDI / MDI), adding the necessary code to the appropriate frame class (e.g., CMainFrame).

Making the window snap to the screen requires that we take control of a few messages that are normally sent by the application to the operating system. We won't prevent the messages from reaching the OS, but we will be the first to manipulate them. Here's how to do it:

Override the Default WindowProc Method

First, we need to override the standard WindowProc() message in the dialog's class. Add this override via ClassWizard, and then add the following line of code to the corresponding method:

OnMessage(this, message, wParam, lParam);

The OnMessage() line we added is how we will "hijack" the messages being sent to the OS, and the return line is the code that actually sends the messages onward to the OS.

Trapping the Necessary Messages

Manually add the OnMessage() method to your dialog's class (make sure you put the declaration in the .h file). The method should have code similar to the following:

LRESULT CPaperplusDlg::OnMessage(CWnd* wnd, UINT msg, WPARAM wp, LPARAM lp)
{
    switch(msg)
    {
        case WM_MOVING : OnMoving(wnd, (LPRECT) lp); break;
        case WM_ENTERSIZEMOVE : OnEnterSizeMove(wnd); break;
    };
    return 0;
}

Handling the Trapped Messages

As you can see, the two messages we need to trap are the WM_MOVING message (which indicates that the user is currently moving the window), and the WM_ENTERSIZEMOVE message (which indicates that the window has just entered the modal loop that deals with the window moving). We will route these two messages through their respective methods (which we will write) shown in the code above; OnMoving() for the moving message, and OnEnterSizeMove() for the other one. To begin, manually add the OnEnterSizeMove() method, which should look like the following:

void CPaperplusDlg::OnEnterSizeMove(CWnd* wnd)
{
    POINT cur_pos;
    CRect rcWindow;
    wnd->GetWindowRect(rcWindow);
    GetCursorPos(&cur_pos);
    cx = cur_pos.x - rcWindow.left;
    cy = cur_pos.y - rcWindow.top;
}

Again, don't forget to add the declaration of this method to the class .h file. This code first gets the rectangle associated with the window we want to move (in this example the window is our program's main dialog). It then gets the mouse cursor position, and stores the x and y values in two member variables, cx and cy. Declare two variables in your class .h file that look like this:

int cx;
int cy;

Now that we have that method taken care of, and the mouse coordinate member variables are in place, we can add the OnMoving() method:

BOOL CPaperplusDlg::OnMoving(CWnd* wnd, LPRECT rect)
{
    POINT cur_pos;
    RECT wa;
    GetCursorPos(&cur_pos);
    OffsetRect(rect, cur_pos.x - (rect->left + cx),
               cur_pos.y - (rect->top + cy));
    SystemParametersInfo(SPI_GETWORKAREA, 0, &wa, 0);

    if(isClose(rect->left, wa.left))
        OffsetRect( rect, wa.left - rect->left, 0 );
    else if(isClose(wa.right, rect->right))
        OffsetRect( rect, wa.right - rect->right, 0 );

    if(isClose(rect->top, wa.top))
        OffsetRect(rect, 0, wa.top - rect->top);
    else if(isClose(wa.bottom, rect->bottom))
        OffsetRect(rect, 0, wa.bottom - rect->bottom);

    return TRUE;
}

Before we discuss the previous block of code, there is one point of confusion that should be cleared up. You may have noticed that we removed the values "cx" and "cy" in the OnEnterSizeMove() method and are adding them back in this method. Clearly this seems like a wasted effort, right? Wrong! These calculations are necessary to "un-snap" the window from the edge of the screen. If you play around with the code by commenting out the first OffsetRect() call above, you will notice that the window does some strange stuff.

Testing the Window's Location

The OnMoving() block of code is a bit more involved. We are getting the cursor position again (as we move the window), and are offsetting the main dialog rectangle appropriately. We then get the rectangle of the "work area" (using the SystemParametersInfo() call), which basically retrieves the rectangle that is defined by the screen edges (and the start bar edge). We then call a method that we have yet to write, isClose(), which basically just tests to see if we've gotten close enough to snap to the edge of the screen. Let's now take a look at that method:

BOOL CPaperplusDlg::isClose(int a, int b)
{
    return abs(a-b) < m_SnapWidth;
}

This method is very simple. All it does is return whether or not the difference in the passed parameters is less than the width that we want to snap by. As you can see in the code example above, I use a variable in my Paper Plus program that allows the user to set that snap distance: m_SnapWidth. But any value can be used here (so you can hard-wire a value in, without allowing the user a choice of a snap distance).

Now you're done! That wasn't too bad, was it? Just make sure you have all the function declarations in place, and you should be set to go. Test out the snapping, and see how well it works.

System Menu Icon Bug

Apr 12, 2008
This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

Within the code generated by Microsoft's MFC AppWizard, is a means of displaying the system menu icon (the little icon that appears in the upper left corner of all Windows programs). This code contains an annoying bug, however. The 16x16 icon you create for your project does not get used at all! Instead, Windows squishes the 32x32 icon down to fit into a 16x16 region, often times producing nasty results. So how can we fix this problem? First, we need to make a change in our main dialog's class definition (.h) file. Here is the line in question:

HICON m_hIcon;

Replace that line of code with the following two lines:

HICON m_hIcon32x32;
HICON m_hIcon16x16;

Note that we are essentially creating two icon handles instead of the one which we had before. As you can see from their names, one will be for our 32x32 icon, while the other one will be used for our 16x16 icon. Now that we have those two handles available, we need to change the code responsible for actually loading the icon. This code can be found in the constructor for your application's main dialog (in the class .cpp file):

m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

The LoadIcon() method only deals with icons which are SM_CXICON pixels wide by SM_CYICON pixels high, which almost always results in icons of size 32x32. Since this method uses these system constants (thereby displaying the wrong icon), we should make use of the LoadImage() method instead. Change the line of code above into the following two lines:

m_hIcon32x32 = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_hIcon16x16 = (HICON)LoadImage(AfxGetInstanceHandle(),
                MAKEINTRESOURCE(IDR_MAINFRAME), IMAGE_ICON,
                16, 16, LR_DEFAULTCOLOR);

First, we load up the 32x32 icon using the same LoadIcon() method. Then we load the 16x16 icon using the more robust LoadImage() method. Once both icons have been loaded, we must fix two other problems. The first is located in the default OnPaint() method. In the automatically generated code, you will find the following two lines:

// Draw the icon
dc.DrawIcon(x, y, m_hIcon);

Change the m_hIcon variable in that method call to m_hIcon16x16 instead. The result will look like this:

// Draw the icon
dc.DrawIcon(x, y, m_hIcon16x16);

Likewise, we need to change the icon name in the OnQueryDragIcon() method. The m_hIcon variable will again become m_hIcon16x16, resulting in the following code:

return (HCURSOR) m_hIcon16x16;

Once these changes have been made, your small icon will appear properly, and the larger icon will also be used as it should. A simple solution to an annoying problem!

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

The UpdateCommandUI functionality is great for disabling menu items when data is set to a certain value. Adding this functionality to a dialog based application is a little tricky, but it can be done.

We first need to add the OnInitMenuPopup() member function to our dialog box class. Here is the code that should get generated by default for this event:

void CTestDlg::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
    CDialog::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
}

Adding the Necessary Code

We need to change this code, so delete (or comment out) the single line of code in this function, replacing it with the following:

ASSERT(pPopupMenu != NULL);
CCmdUI state; // Check the enabled state of various menu items
state.m_pMenu = pPopupMenu;
ASSERT(state.m_pOther == NULL);
ASSERT(state.m_pParentMenu == NULL);

// Is the menu in question a popup in the top-level menu? If so,
// set m_pOther to this menu. Note that m_pParentMenu == NULL
// indicates that the menu is a secondary popup.

HMENU hParentMenu;
if(AfxGetThreadState()->m_hTrackingMenu == pPopupMenu->m_hMenu)
    // Parent == child for tracking popup.
    state.m_pParentMenu = pPopupMenu;
else if((hParentMenu = ::GetMenu(m_hWnd)) != NULL)
{
    CWnd* pParent = this;
    // Child windows don't have menus--need to go to the top!
    if(pParent != NULL && (hParentMenu = ::GetMenu(pParent->m_hWnd)) != NULL)
    {
        int nIndexMax = ::GetMenuItemCount(hParentMenu);
        for (int nIndex = 0; nIndex < nIndexMax; nIndex++)
        {
            if(::GetSubMenu(hParentMenu, nIndex) == pPopupMenu->m_hMenu)
            {
                // When popup is found, m_pParentMenu is containing menu.
               state.m_pParentMenu = CMenu::FromHandle(hParentMenu);
               break;
            }
        }
    }
}

state.m_nIndexMax = pPopupMenu->GetMenuItemCount();
for(state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++)
{
    state.m_nID = pPopupMenu->GetMenuItemID(state.m_nIndex);
    if(state.m_nID == 0)
        continue; // Menu separator or invalid cmd - ignore it.
    ASSERT(state.m_pOther == NULL);
    ASSERT(state.m_pMenu != NULL);
    if(state.m_nID == (UINT)-1)
    {
        // Possibly a popup menu, route to first item of that popup.
        state.m_pSubMenu = pPopupMenu->GetSubMenu(state.m_nIndex);
        if(state.m_pSubMenu == NULL ||
          (state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 ||
           state.m_nID == (UINT)-1)
        {
            continue; // First item of popup can't be routed to.
        }
        // Popups are never auto disabled.
        state.DoUpdate(this, TRUE);
    }
    else
    {
        // Normal menu item.
        // Auto enable/disable if frame window has m_bAutoMenuEnable
        // set and command is _not_ a system command.
        state.m_pSubMenu = NULL;
        state.DoUpdate(this, FALSE);
    }

    // Adjust for menu deletions and additions.
    UINT nCount = pPopupMenu->GetMenuItemCount();
    if(nCount < state.m_nIndexMax)
    {
        state.m_nIndex -= (state.m_nIndexMax - nCount);
        while(state.m_nIndex < nCount &&

              pPopupMenu->GetMenuItemID(state.m_nIndex) == state.m_nID)
        {
            state.m_nIndex++;
        }
    }
    state.m_nIndexMax = nCount;
}

The code presented above comes from the MFC Documentation, and is adapted from CFrameWnd::OnInitMenuPopup() in WinFrm.cpp. With this code in place, you can now call the UpdateCommandUI interface for an embedded menu control. A handy addition to any dialog box that contains a menu.

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

Enabling tool tips on dialog controls can be extremely useful, especially when a large number of controls are used. In this scenario, they could be used to describe what a particular control does, as well as any corresponding shortcut keys. Adding this feature is a relatively simple procedure. First we need to enable the tool tips by calling the following line of code in our OnInitDialog() method:

EnableToolTips(TRUE);

This function simply enables the tool tip control in our dialog window. We now need to add the following line of code to the message map in our dialog class. Note that the text OnToolTip in the code below is the name of the method that will be associated with this message. Feel free to change the name of this method if you like. For the purposes of this article, we will stick with using OnToolTip as the method name.

ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTip)

Handling the Tool Tips

Next, add the OnToolTip() method declaration to the dialog class header file:

afx_msg BOOL OnToolTip(UINT id, NMHDR* pTTTStruct, LRESULT* pResult);

After we have declared this method, we need to add its code to the dialog class CPP file. The definition for this method is shown below:

BOOL CYourDlg::OnToolTip(UINT id, NMHDR* pNMHDR, LRESULT* pResult)
{
    TOOLTIPTEXT *pTTT = (TOOLTIPTEXT*)pNMHDR;
    UINT nID = pNMHDR->idFrom;
    if(pTTT->uFlags & TTF_IDISHWND)
    {
        // idFrom is actually the HWND of the tool
        nID = ::GetDlgCtrlID((HWND)nID);
        if(nID)
        {
            pTTT->lpszText = MAKEINTRESOURCE(nID);
            pTTT->hinst = AfxGetResourceHandle();
            return(TRUE);
        }
    }
    return(FALSE);
}

Now that our code is in place, we need to add the tool tip strings to the string table resource. In the resource editor, open the string table (insert a new one if your project doesn't already have a string table). Now add one string for each control that you want to show a tool tip. Each string's value should be set to the text that you want to appear in the tool tip. Most importantly, each string's ID should be set to the same ID of the corresponding control.

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

To add accelerator key functionality to a dialog based program, we first must insert an accelerator resource into the project. In Visual Studio, this can be accomplished by right clicking the top-most folder in the resource view, and selecting the Insert... option. Make sure you give this new resource a unique ID. For this example, I will be using an ID value of IDR_MY_ACCELERATOR.

Next, we need to add a member variable to our main dialog class. This variable is a handle to the accelerator table, and will be used several places later on. It should look like the following:

HACCEL m_hAccel;

Loading the Table

Now add the following lines of code to the OnInitDialog() function. As you can see, we initialize our accelerator table handle by calling the LoadAccelerators() method. Also, note that the text IDR_MY_ACCELERATOR in the code below should be the ID that you gave to your accelerator resource.

// Load accelerator keys...
m_hAccel = ::LoadAccelerators(AfxGetInstanceHandle(),
                              MAKEINTRESOURCE(IDR_MY_ACCELERATOR));
ASSERT(m_hAccel);

Translating the Accelerator Keys

Now, add the PreTranslateMessage message to your main dialog class. Then place the following lines of code in the newly created method:

if(m_hAccel)
{
    if(::TranslateAccelerator(m_hWnd, m_hAccel, pMsg))
        return(TRUE);
}

return CDialog::PreTranslateMessage(pMsg);

All that's left at this point is to add the key mappings to the accelerator resource that we inserted into the project at the beginning. Make sure you give each accelerator key the same ID as the command you want the key to execute.

As an example, let us assume that our program has a “Refresh View” menu item. The corresponding ID value is ID_VIEW_REFRESH, and we want the [F5] key to mimic this menu command. To do this, we simply add the [F5] key to the accelerator resource and set its ID value to ID_VIEW_REFRESH, just as it is for the menu item. Now, whenever the [F5] key is pressed, the “Refresh View” menu item will be invoked, just as we want it to be.

A Cleaner MRU List

Apr 12, 2008
This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

The default means of adding a most recently used (MRU) list to a program is through a call to the LoadStdProfileSettings() method. One call to this method takes care of creating the MRU and populating it with stored data. But this method call contains one minor annoyance: a single query to a registry value that may or may not already exist under your program's registry branch. Should the latter scenario be the case (the registry item does not exist), it's parent branch will be created; regardless of whether or not you will ever use it. The branch that gets created is entitled "Settings", which can be a useful place to store your program specific registry values. But suppose you would rather use other branch names and never have the "Settings" heading appear in your program's registry branch? The solution is surprisingly easy. Our first course of action is to remove the call to the LoadStdProfileSettings() method. This call is usually found in your application's InitInstance() method. The simplest way to remove the call is to simply comment it out.

Creating the Most Recently Used List

The next step involves a call to the RecentFileList class constructor. A "hidden" variable exists in your CWinApp derived class (the same class that included the InitInstance() method), and has the name m_pRecentFileList. This variable is simply a pointer to a CRecentFileList object. So we can create a new one with a call like the following (add this code to your InitInstance() method):

CString strSection = "Recent File List";
CString strEntryFormat = "File%d";

m_pRecentFileList = new CRecentFileList(0, strSection, strEntryFormat, 4);

The constructor here takes four parameters: the first is an offset for the numbering in the most recently used list. It's best to just leave this value at 0. Next is a string which points to the name of the section in the registry where the MRU is read and written. In this example, I've used the default "Recent File List" heading. The third parameter given to the constructor points to a format string, which is used in creating the names of the entries stored in the registry. Here, I've used the value "File%d". So, as files get added to the MRU list, they will follow the format of "File0", "File1", etc. The final parameter is the number of files to keep track of. As you can see, I've used a value of 4 in this example. This value must range between 0 and 16 (inclusive), with a value of zero meaning that no MRU list should be kept at all.

Reading the List

One final call is needed to do the actual reading of the stored values. It should look like the following and, again, be placed in the InitInstance() method, right after the code we inserted above:

m_pRecentFileList->ReadList();

Once this call has been made, you will be able to use your MRU list just as in any other program. There is no need to worry about saving the values, as the application will take care of that for you when it closes. Pretty nice!

I ran across a story at BBC News (via Digg) that is just plain depressing. It seems that Knut the polar bear killed 10 live carp in front of zoo visitors. The seemingly natural act has caused some controversy, with critics saying that the fish shouldn't have been there in the first place. German media claims that the fish were there to clean up algae, and they quickly point out that we are talking about a polar bear, the most carnivorous bear species in the world.

Won't someone think of the children?!?

Last night, I spent several frustrating hours trying to untangle a problem with the update manifest for my Firefox extensions. After releasing my updated extensions, I noted that while CoLT automatically updated without any problems, Googlebar Lite did not. I double-checked, triple-checked, and even quadruple-checked everything I was doing. Everything I had done for CoLT was exactly what I was doing for Googlebar Lite. But the automatic update consistently failed.

As past experiences have taught me, simply walking away from the problem is occasionally all that is needed. Taking a mental break is a surprisingly effective way of going directly to a problem's solution. And this is exactly what happened last night.

Being well past my usual bed time, and having made no progress towards a solution, I decided to call it a night, and so headed to the bathroom to take a shower and brush my teeth. As I stepped into the shower, the solution instantly occurred to me! I was incorrectly using a wild card in the minVersion value in my installer manifests (2.0.0.* instead of 2.0). Immediately after getting out of the shower, I rushed to test the solution. Lo and behold, it worked.

Sometimes, all it takes is walking away.

At work, I'm in charge of 20 individual build systems for one of our larger software project (18 Linux systems and 2 Windows systems). Every machine is connected to a private network that cannot see the outside world. As you might expect, the occasional "clock skew" warning would be thrown by gcc, since some of the source files had date stamps in the future. To fix this, I set out to learn about configuring NTP on a private network. As is typical of the Linux world, there was little useful documentation to be found. After gleaning little bits of information from a number of sources, I figured out how to do it, and I'm writing it down for everybody's benefit.

Creating the NTP Master Server

Our first step is to create a 'server' machine from which all of our other machines will get the time. In this tutorial, our master machine is a Linux box. Here's how to set things up:

Step 1: Edit ntp.conf
The first course of action is to edit the ntp.conf file in the /etc directory. Place the following lines in this file, removing any others that may exist (you may want to back up your existing ntp.conf file just in case):
# Use the local clock
server 127.127.1.0 prefer
fudge  127.127.1.0 stratum 10
driftfile /var/lib/ntp/drift
broadcastdelay 0.008

# Give localhost full access rights
restrict 127.0.0.1

# Give machines on our network access to query us
restrict 192.1.1.0 mask 255.255.255.0 nomodify notrap
You'll want to replace the highlighted sections with the private network address range that should be allowed to query this server, along with the corresponding mask, respectively. In this example, any machine on the 192.1.1.xxx network can query the master server for time updates.
Step 2: Restart the NTP daemon
Once the above changes have been made, the NTP daemon needs to be restarted. How to do this unfortunately differs among Linux distributions. Here are a few commands that I figured out for the OS families I care about:
  • RedHat: service ntpd restart
  • SLES 8/9: /etc/init.d/xntpd restart
  • SLES 10: /etc/init.d/ntp restart
Step 3: Configure NTP to start on reboot
Perhaps the most important step in this process is configuring the NTP daemon to start up on a reboot. Here's how to do it:
  • RedHat: chkconfig ntpd on
  • SLES 8/9: chkconfig xntpd on
  • SLES 10: chkconfig ntp on

Configuring the Client Machines

Step 1: Edit ntp.conf
Client machines must also have their ntp.conf file updated (again, in the /etc directory). Place the following lines in the configuration file:
# Point to our network's master time server
server 192.1.1.100

restrict default ignore
restrict 127.0.0.1
restrict 192.1.1.100 mask 255.255.255.255 nomodify notrap noquery

driftfile /var/lib/ntp/drift
Both of the highlighted sections above should point to the master server (in this case, with an IP address of 192.1.1.100).
Step 2: Stop running NTP daemons
Use one of the following to stop any running NTP daemons:
  • RedHat: service ntpd stop
  • SLES 8/9: /etc/init.d/xntpd stop
  • SLES 10: /etc/init.d/ntp stop
Step 3: Force an update
We must force the client to update now, in case the offset is larger than 1000 seconds (the largest offset NTP allows). To do this, issue the following command:
ntpdate -u 192.1.1.100
Again, replace the highlighted section with the IP address of the master server. Note that you may need to do this forced update two or three times to make sure things are synchronized.
Step 4: Start the NTP daemon
Now that we've synced the client, we should restart the daemon:
  • RedHat: service ntpd start
  • SLES 8/9: /etc/init.d/xntpd start
  • SLES 10: /etc/init.d/ntp start
Step 5: Configure NTP to start on reboot
Finally, we need to make sure the daemon starts up at boot time (like we did for the server):
  • RedHat: chkconfig ntpd on
  • SLES 8/9: chkconfig xntpd on
  • SLES 10: chkconfig ntp on

Once you've set this up, all the client machines will keep their clocks synchronized with the master. If you update the master server time, the clients should follow (as long as the update isn't larger than 1000 seconds). I believe you can even point Windows systems to the master, though I have yet to try that.

I have now migrated this website to WordPress 2.5. Better yet, my installs are now managed with Subversion, which will make future updates as easy as issuing a single command from my web-server command line. This all comes thanks to a handy article in the WordPress Codex that gives step-by-step instructions.

This new install was done in a virgin folder (for subversion's sake), and I had to copy existing files over to the new location. If you spot any breakage, please let me know and I'll fix it.

WordPress 2.5

Mar 30, 2008

The latest release of WordPress is now available. I've installed it in a sandbox, and I have to admit that I really like the new look of the admin area, courtesy of the folks at Happy Cog (Jeffrey Zeldman, Jason Santa Maria, and Liz Danzico specifically). Thankfully, my theme still works in the new version, as does Spam Karma and Official Comments.

I want to spend a little more time with the sandbox, but I'll probably migrate this site to 2.5 in the next few days.

Google recently enabled "Search Suggest" at their official home page. I find this feature annoying, and I wanted a way to disable it. Thankfully, the solution was very simple:

  1. Visit the Search Preferences page
  2. Set the Query Suggestions option to "Do not provide query suggestions in the search box"
  3. Save your preferences

I wish Google had made disabling this a little clearer, rather than quietly adding the preference to the preferences page.

For some time now, Firefox has supported an experimental CSS technique for rounding border corners (-moz-border-radius). The rendering engine in Firefox 2 does a barely acceptable job with this, though the rounded corners don't appear to be uniformly sized, nor are they anti-aliased. Cairo, which drives the rendering engine in Firefox 3, does a much better job at handling the rounded corners, and the results are quite nice.

As such, I'm offering some 'eye-candy' to those users who visit this site with either Minefield or a Firefox 3 beta build. Those users will now note that code blocks (pre elements), as well as comment blocks, have nicely rounded edges. The end result looks great, and I hope you agree.

By default, Windows Explorer opens up in the "My Documents" folder, which is far from useful (assuming you don't store all your documents there). Just today, I figured out how to get Windows Explorer to open in a folder that you specify. Here's how to do it:

  1. Right click the Windows Explorer shortcut and select Properties.
  2. Make sure you are on the "Shortcut" tab.
  3. Clear the Start in: field. Contrary to what you might think, Windows Explorer seems to ignore whatever you type here (which seems stupid to me).
  4. Change the Target: field to the following:
    %SystemRoot%\explorer.exe /n,/e,{Desired_Path}. For example: %SystemRoot%\explorer.exe /n,/e,C:\. Note that the commas are required!
  5. Accept your changes.

Now, each time you open Windows Explorer, it will point to your desired location. This is an incredibly useful tip that will now save me two clicks for every explorer window that I open!

Playing in a Minefield

Mar 13, 2008

For the most part, I haven't spent much time with Minefield. Firefox 2 works well enough for me that I haven't had much desire to play around with the new stuff, especially seeing as many portions are inevitably either incomplete or broken. However, the recent beta 4 release prompted me to take it for a spin around the web. Here are a few thoughts on the latest build I've tested as of this writing (2008031205):

New Look
The new skin is interesting, but portions of it will definitely take some getting used to. Surely it's not final (these things never are until the thing is actually released), so I'm hopeful there will still be a few tweaks. When using large toolbar buttons, the back button appears round, while the forward button is much smaller and rectangular; an odd pairing which is reminiscent of Internet Explorer 7. I tend to use the small toolbar buttons, so this change doesn't affect me too much. The forward and back history menus have been consolidated into one menu (again, like IE7), which I think is a nice improvement. The new URL bar looks nice and provides a lot more information than the previous one did. A little star icon at the far right of the bar provides a quick means of bookmarking the current page, which is handy.
Improved Memory Usage
I put the new build through its paces at Google Maps, dragging around, zooming in and out, and generally trying to run up memory consumption (which I did successfully). After closing the corresponding tab, I noted that memory usage dropped considerably, and continued to decrease over time. The new garbage collection and memory defragmentation that has been implemented is clearly a big improvement. Firefox is still a hog, but it's heading in the right direction.
Faster JavaScript
The JavaScript improvements which I recently mentioned are immediately noticeable. GMail and Google Maps feel a lot faster than they typically do, which is super great.
URL Bar Autocompletion
Autocompletion in the URL bar is now handled in a new (and exciting) way. As you type, matches are offered based on all text associated with a link. The page title, the URL itself, and bookmark keywords are all searched. Matched text can appear anywhere in the string, which is really handy.
Password Manager Prompts
The password manager now slides down from the top (like the information bar), so it's not quite as intrusive. However, the handy keyboard shortcuts are no longer the same. In order to quickly answer the 'Not Now' choice, you have to press Alt+N instead of just N. This will take some getting used to.
Page Zoom
The new page zoom feature works really well. Images are magnified, as is the text on the page. There's even an option to only zoom the text, leaving images alone. Pretty neat!

There are plenty of other changes in Minefield, so I recommend checking it out. I am starting to work on adding FF3 support to CoLT and Googlebar Lite, but it's turning out to be a little more difficult than I initially thought. A host of code changes are needed in Googlebar Lite, since I'm currently using interfaces that are now deprecated. Hopefully I can get things updated in the near future.

SlickEdit 2007 Rocks!

Mar 7, 2008

My license for SlickEdit at work was renewed recently, so I upgraded to SlickEdit 2007, the latest release of this already amazing program. A boat-load of new features are included in this new release, but my absolute favorite is the new dynamic surround feature. Check out this demo of the feature in action (be sure to turn up your speakers; the sound is a little low). How super cool is that? I have actually wanted this particular feature for some time, so I'm very excited that it has actually been implemented. You can even unsurround things, should you choose to do so!

There are plenty of other great new features to be had:

  • Improved XML / HTML formatting
  • Export documents to HTML (preserving all syntax-highlighting ... how great is this?!?)
  • Copy and paste in color
  • Drag and drop support in KDE and Gnome
  • Get live errors in Java as you type (similar to the corresponding functionality in Eclipse, I assume)
  • And more!

You can check out the complete list [PDF] of new features (all 5 pages worth) at the SlickEdit website. I'm seriously considering upgrading my license at home, though the $139 upgrade price is pretty steep. If you are in the market for a good code editor, I strongly recommend SlickEdit.

Never before I have felt so connected to a Penny Arcade comic. I recently picked up Professor Layton and the Curious Village, a puzzle-adventure game for the Nintendo DS. After roughly 10 hours of game play, I've completed the game, so I thought I'd post some thoughts on it. Before we get to that, however, I'd like to explain how this game works.

Professor Layton is one of those hybrid titles like Puzzle Quest. It is neither an adventure game, nor is it a puzzle game; it's somewhere right in the middle. Layton himself is a private detective of sorts who is hired to figure out a puzzling will left behind by the late Baron Reinhold. Along with his assistant Luke, Layton quickly finds himself in an ever-increasing mysterious situation. There are twists and turns all over the place, and plenty of mysteries to be solved in the process.

Each person you meet will give you clues to the ever increasing list of mysteries you encounter, but only if you solve a puzzle for them. And by puzzle, I mostly mean 'brain teaser.' The puzzle difficulties are all over the place in this game. Some puzzles are easy to solve, while others will have you banging your head against a hard surface in frustration. There's a hint system in the game that offers you three hints per puzzle, which is often enough to help you figure things out, but sometimes the hints are themselves quite cryptic. Getting a hint costs you a 'hint coin,' of which there are a limited amount (though plenty are hidden throughout the game world). When you solve a puzzle successfully, you are awarded a number of 'picarats' (essentially points). Each puzzle is worth so many of these picarats, with harder puzzles being worth more. Answer incorrectly and the value awarded goes down. I'm not sure what this point system is good for. At one point you learn that if you get enough of these picarats, something special happens. I never saw anything happen as a result of my score, so I must not have gotten enough. But enough about these details. Let's jump into my review.

The Good

Gorgeous Graphics
Professor Layton is drawn in an anime-style, with a distinctly European vibe. This particular title feels a little like something Hayao Miyazaki and his friends at Studio Ghibli would do, much in the vein of Howl's Moving Castle (a great movie, by the way). Not only does the static art look great, but there are a number of animated cut-scenes which are stunningly nice to watch. Hopefully more games will make use of this visual style!
Interesting Story
The storyline is quite unique, which is refreshing. I really felt as if I were playing through a movie, and the twists and turns throughout the story were entertaining. Much of the writing is top-notch, and a few genuinely funny moments made me laugh out loud.
Plenty of Puzzles
There are a total of 120 puzzles in this game, which offers plenty of game-play time. I only found about 100 of the puzzles (some of them are hidden in various parts of the village), so I plan on playing through one more time to make sure I find everything. Several mini-games (or mini-puzzles, whichever you may prefer) are also made available to you as you play through. You must build a device with 'gizmos' that you find, repair a painting via scraps that you find, and furnish the living quarters of both the Professor and Luke. The publisher even offers weekly downloads of new puzzles, though I haven't tried out that feature.
Great Voice Acting
A few of the animated cut-scenes include voice acting, the quality of which is excellent. The characters sound believable, and it's clear that a lot of work went into giving each one a unique personality.

The Bad

Difficulty Extremes
One of the most frustrating aspects of this game is the wildly varying difficulty levels between puzzles. Some are very straightforward, while others are ultimate mind-benders. A few of the puzzles had fairly low difficulty ratings in game, but I found myself stuck, indicating to me that not all of them are rated as accurately as they should be.
Little Punishment for Failure
There is very little punishment for failure on any given puzzle. Suppose you have a puzzle that's worth 50 picarats. Each time you fail, the value comes down by 5. But this deduction only ever happens 3 times. So, regardless of how many times you try this particular puzzle, you will score at least 35 picarats. This essentially means that you can brute force each puzzle, especially those that offer multiple choice answers. This feels like a cheap way to beat the system (though it's a handy way to get past those truly difficult puzzles).
Repetitive Music
The music, while not gratingly annoying like in Puzzle Quest, is very repetitive. To my knowledge, there's no way to turn it off either. You could turn down the DS speaker volume, but you might miss the audio in a cut-scene as a result (and you definitely don't want to do that). An option to disable the game music would be very welcome.
Over-sensitive Handwriting Recognition
Handwriting recognition is used throughout the game for you to enter answers to various puzzles. It seems a little too sensitive to me, and doesn't give you nearly long enough to write some characters that require two or more strokes (the letters T and F for example). As a result, some trial-and-error is required in order to answer correctly.
Not Enough Voice Acting
As impressed as I was with the animated cut-scenes and voice acting, I was disappointed that there wasn't more of it. The little snippets we get are truly high quality, but more would have been great.
Little Replay Value
Once you've solved all the puzzles, there's very little to draw you back to this title. The weekly puzzle releases from the publisher are interesting, but I doubt I'll keep up with them.

The Verdict

This was a fairly fun game, and I really loved the art direction. While each puzzle is unique, they get a little monotonous after completing 75 or so. I have to admit that I was really tired of the game by the end, but my desire to solve each mystery I uncovered kept me going. At least one sequel is planned for Professor Layton. Unless the gameplay is tweaked, I'm not sure I'll pick up any subsequent titles. But this initial offering was entertaining, at least for a little while. The eye candy and unique story are worth the price of admission. My final verdict: B

The Acid3 test for web browsers has been released. Drunken Fist has a number of screenshots that show the failure rate among the various top browsers. There are some really interesting results from the tests:

  • Safari 3: 39% success (latest nightlies are up to 87%)
  • Firefox 3: 59% success
  • Firefox 2: 50% success
  • Opera 9: 46% success
  • IE 7: 12% success
  • IE 6: 11% success

Safari is the surprising top dog in the list, but what I find most interesting is that Firefox 3 (which passes the Acid2 test) only hits 59% in the new test. I would have guessed that being Acid2 compliant would mean being nearly Acid3 compliant. Apparently, that isn't the case. It looks like web browsers still have a long way to go in the standards race.