Results by Bing
|Developer Centers|Library|Downloads|Code Center|Subscriptions|MSDN Worldwide
Search for

Advanced Search
MSDN Home > MSJ > December 1997
December 1997

Microsoft Systems Journal Homepage

The Visual Programmer

Code for this article: VP.exe (39KB)
George Shepherd develops mapping software for Orbital Software and delivers training courses for DevelopMentor. Scot Wingo is cofounder of Stingray Software, producing MFC extension class libraries. George and Scot wrote MFC Internals (Addison-Wesley, 1996).

IntelliMouse® support was added to the CScrollView class in version 4.21 of MFC, the version that ships with Visual C++® 5.0. If you look at the implementation of the IntelliMouse support in the MFC source code (src\viewscrl.cpp), you will see this comment:
 BOOL CScrollView::OnMouseWheel(
          UINT fFlags, short zDelta,
           CPoint point)
     // we don't handle anything
    // but scrolling just now
     if (fFlags & (MK_SHIFT |
         return FALSE;
Then the CScrollView handles the message and implements IntelliMouse scrolling by mapping the turns of the mouse wheel to scrolls in the CScrollView.
But what about the MK_SHIFT and MK_CONTROL settings that CScrollView::OnMouseWheel is ignoring? A quick search of the IntelliMouse guidelines at http://www.microsoft.com/hardware/mouse/intellimouse/sdk/ yields the following table that details the support required to obtain Office 97 compatibility certification.

Mouse Control Operation
Wheel rotation Scrolling
Ctrl+wheel rotation Zooming
Shift+wheel rotation Datazoom
Wheel button click and drag Panning
Wheel button click Autoscroll

You've probably already heard of Scrolling and zooming, but what about datazoom, panning, and autoscroll? Microsoft suggests using datazoom to explore hierarchical data. For example, Word and PowerPoint® use datazoom in outline view to expand and contract nodes. Microsoft Internet Explorer (IE) 4.0 uses datazoom to go back and forth between Web pages.

IntelliMouse Panning

Panning is a really nice feature. You move around your document by holding down the mouse wheel and moving the mouse to "pan" the document in the direction of the mouse movement. In Microsoft Excel, panning lets you zip to the bottom-right corner of your spreadsheet very quickly without ever having to click a scroll bar. The speed of the pan is controlled by the distance of the cursor from where you started panning. Try panning in Microsoft Excel or Word and you'll see what we mean.
As part of the IntelliMouse specification, Microsoft also defines a variety of cursors to be used in IntelliMouse panning. For example, when a user clicks the mouse wheel to initiate scrolling or moves the cursor back to the neutral zone (the area around the panning origin), one of three standard cursors appears, telling the user what level of panning support is provided.
Indicates when two-dimensional panning is supported.
Indicates that vertical-only, one-dimensional panning is available.
Indicates that horizontal-only, one-dimensional panning is available

During panning there are four cursors (eight if you support diagonal panning) that show the direction of the panning:
West East
North South
Figure 1 Panning in IE 4.0
Figure 1 Panning in IE 4.0

The panning support in many Microsoft IntelliMouse-compatible applications is dramatically different. For example, Microsoft Excel and IE 4.0 implement panning by creating the panning origin wherever the mouse happens to be (see Figure 1). Word, on the other hand, implements only vertical panning and moves the panning origin relative to the vertical scroll bar (see Figure 2). If you plan on supporting only vertical scrolling, it may be a good idea to create a CScrollBar derivative that handles the panning and scrolling so you can isolate the support from your application. It seems like this is the approach taken by the Word team.

Figure 2 Panning in Word
Figure 2 Panning in Word

IntelliMouse AutoScroll

In the IntelliMouse specification, an application should enter Autoscroll mode when the user clicks the mouse wheel but doesn't drag it. IE 4.0 has the smoothest implementation of this. It basically lets the user start the windows scrolling and then continues to scroll until the user moves the mouse or clicks another button. This is useful for applications like Word where the user may want to read large volumes of vertical text, but does not want to scroll manually.
CScrollView meets only one requirement of the IntelliMouse specification—that the view scrolls when the user moves the mouse wheel. This is largely because CScrollView does not support zooming or panning. This month we will implement a CScrollView derivative called MSJSuperView that supports zooming and panning with the IntelliMouse. Before we can add IntelliMouse zooming and panning support, however, we need to add zooming and panning support to the CScrollView.

CScrollView Zooming

The most common method for implementing GDI-level zooming is via mapping mode and viewport manipulations. Implementing complex zooming is much easier in MM_ ANISOTROPIC mapping mode because of origin placement and viewport flexibility. MSJSuperView requires that the view be drawn in ANISOTROPIC mode, which won't affect most applications unless they have a dependency on another mapping mode.
MSJSuperView implements simple zooming by adding the members shown in Figure 3 to a vanilla CScrollView derivative. The declaration and implementation of these MSJSuperView zooming members can be found in Figure 4.

CScrollView Panning

The key to adding panning support is implementing a scrolling routine that knows how to scroll the view in a given direction by a given amount. We implemented a MSJSuperView member function called DoScroll that takes a directional flag (based on a simple enumeration for up/down/left/right) and an amount to scroll in the given direction. This method calculates the new x and y of the view and the new scroll range and then performs a scroll operation on the view. Figure 5 has the source code for the panning implementation.
Now that we have basic zooming and panning in MSJSuperView, let's map the IntelliMouse actions for zooming and panning.

IntelliMouse Zooming

To add zooming, you first need to override the OnMouseWheel message that is already partially implemented by CScrollView. In our OnMouseWheel handler, we need to add zooming support. Once zooming is implemented, this maps to our zooming functions with only a couple of lines of code:

 BOOL MSJSuperView::OnMouseWheel(UINT nFlags,
                          short zDelta, CPoint ptMouse)
     //Special logic for CONTROL and mousewheel
    // which means to zoom.

     if (nFlags == MK_CONTROL)
         float fCurrentZoom = m_fZoomScale;
         if (zDelta > 0)
             ZoomIn(&ptMouse,.10f );
         return TRUE;
     return CScrollView::OnMouseWheel(nFlags, zDelta,
If the control flag is set, we intercept the message and call either ZoomIn or ZoomOut depending on whether the zDelta message argument is positive or negative. Notice that the mouse position is passed into the ZoomIn/ZoomOut call so that the zoom will take place exactly where the mouse is positioned. Finally, we give a zooming delta of .10, or 10 percent for every click of the mouse wheel. This gives a pretty smooth zooming action, but it could be customized.
6 Zooming in Action 6 Zooming in Action
Figure 6 Zooming in Action

Figure 6 shows the before and after zooming effects of a sample application that draws circles and uses MSJSuperView for zooming with the IntelliMouse. The window on the left is at 100 percent zoom and the pane on the right is at 400 percent zoom.

IntelliMouse Panning

The tricky part about implementing IntelliMouse panning is the display of the panning cursors. You have to display both the origin and directional cursors while the user is panning. Since Windows
® has only one cursor, this is pretty daunting. To solve this problem, let's use the normal Windows cursor for the directional panning and then create a special window to display the origin.
The origin window is called MSJMouseWheelOriginWnd (see Figure 7). It is basically a very small CWnd derivative that implements some custom painting to draw a transparent origin bitmap so that the bitmap can be seen through and doesn't affect scrolling.
The MSJMouseWheelOrigin takes the resource ID for the bitmap to draw at the origin. It also implements a CreateWnd that creates a borderless window. The meat of the class lives in the OnPaint handler, which does some basic geometry calculations and then calls the MSJDrawTransparentBitmap helper function.
MSJDrawTransparentBitmap (see Figure 8) is a utility function that every Windows programmer should have in their library. We found it long ago in KnowledgeBase. It takes a bitmap and turns it into a mask so that it is transparently drawn over the painting surface. A very handy function to have when you're implementing panning.
Now that we have the cursor situation all straightened out, we need to add some handlers to take care of starting, performing, and stopping the pan.
The first handler starts the pan when the user presses the IntelliMouse mouse wheel, which generates a WM_MBUTTONDOWN message. Figure 9 shows the implementation of our OnMButtonDown handler.
After verifying that the middle button is indeed down by checking the flags passed, the OnMButtonDown handler captures the mouse and calculates where to draw the origin window. Next, the handler stores the starting cursor position in the m_ptMouseWheelOrg data member and sets m_bMouseWheelDrag to TRUE so that other member functions know we are in a drag state. Then OnMButtonDown sets a timer to fire every 10 milliseconds (good value for smooth panning). The ID of the timer is stored in the m_nMouseWheelTimer data member. Finally, OnMButtonDown creates, places, and shows an instance of our handy MSJMouseWheelOriginWnd window that draws the origin bitmap at the position where the cursor should be.
When the user moves the mouse with the mouse wheel depressed to pan, instead of handling every mouse move message, we perform panning in the handler for the timer: MSJSuperView::OnTimer. Figure 10 shows the OnTimer handler. It first validates that it has received a timer with the same ID we set in OnMButtonDown and that m_bMouseWheel Drag is set to TRUE. If this is the case, we are handling an IntelliMouse pan, so OnTimer gets the current cursor position and calculates an offset from the last cursor position stored in m_ptMouseWheelOrg. Once the offset has been calculated, OnTimer uses this to calculate the direction and amount of the pan.
For example, if the user moved a short distance from the original position cursor, then the panning is slow. If the user moved the mouse far away from the original position, then the panning speeds up. This gives the user a variable panning speed similar to that implemented in IE 4.0. After OnTimer calculates the direction and amount of the scroll, it calls MSJSuperView::DoScroll to actually pan the window, then it moves the MSJMouseWheelOriginWnd to the new origin location so it doesn't get scrolled off the screen.
Panning stops when the user releases the mouse wheel; this is handled in the MSJSuperView OnMButtonUp handler (see Figure 11). OnMButtonUp verifies that panning is active by again checking m_bMouseWheelDrag, and then deletes the MSJMouseWheelOriginWnd object. Next, it sets m_bMouseWheelDrag to FALSE, indicating that the drag operation is over. Finally, OnMButtonUp releases the mouse capture, kills the timer, and sets the cursor back to the original cursor type and position.
Figure 12 Panning Support
Figure 12 Panning Support

Figure 12 shows the final MSJSuperView IntelliMouse panning support at work in the circle-drawing sample.


MSJSuperView provides complete IntelliMouse support that you can start using and extending immediately. We have included the source code for the circle sample with the download files so you can experiment with your IntelliMouse and MSJSuperView.

From the December 1997 issue of Microsoft Systems Journal. Get it at your local newsstand, or better yet, subscribe.

© 1997 Microsoft Corporation. All rights reserved. Legal Notices.

© 2015 Microsoft Corporation. All rights reserved. Contact Us |Terms of Use |Trademarks |Privacy & Cookies