Toolbars and Tooltips for Tools

Introduction

Well, since we've had no new and / or exciting articles recently, I realised twas down to me to bridge the gap, so to speak. Nothing new here at all and I wasn't going to bother, but as a friend told me: "some people haven't nutted this stuff out yet."
Oh, and we'll be using lcc (see the archive) since I hate gui based stuff. You can grab my example sources here.

As most people are aware, Miscreantsoft provided a bunch of standard (boring,ugly) common controls intended to supplement the standard controls. There are quite a few of 'em, but in this article we'll be looking at toolbars and tooltips ('cos they're easy peasy and as such won't take too long to write about =] )

Before We Begin

Prior to using common controls, we need to include the common control header file, commctrl.h, in your program. Any program that uses common controls must call the api function InitCommonControls() beforehand. This ensures that the correct dll is loaded and initialised.

The last thing we have to do is make sure we link the common controls library at, urm, link time =]

Here We Go...

All of the common controls are child windows. We can create them in much the same way we would for a regular window, i.e with: CreateWindow(), CreateWindowEx(), or a specific api call for that control.

Toolbar Icons

A toolbar is basically a graphical version of a menu, therefore we're gonna need graphics. Fire up your favourite image editor and create the pictures for your toolbar buttons. There are a few restrictions here :

Basically you need to decide upon button size, then create a bitmap that holds all of them, side by side. For our example, we'll be using buttons you'll probably recognise, I simply ripped them from a screen grab. Each button is 20x19 pixels. Which makes for image dimensions of 80x19 pixels.

#define-ing Stuff

Now we have our button bitmap for the toolbar I'm sure we have an idea what each button should do, so lets #define them. This is toolbar.h

#define ID_NEWFILE 100
#define ID_OPENFILE 101
#define ID_SAVEFILE 102

Ok, that was easy enough. Now we add another couple of defines, one for the toolbar itself, and another for the toolbar bitmap (continuing toolbar.h)

#define ID_TOOLBAR 200
#define ID_TBAR_BMP 300

Now we add our crap to the resource file toolbar.rc:

#include <windows.h>
#include "toolbar.h"

ID_TBAR_BMP BITMAP "tbicons.bmp"

Now lets do some 'coding'...

The Toolbar

Before we can create the toolbar, we need to set up an array for the buttons. Funnily enough this is an array of structure TBBUTTON. The structure is defined as follows :

typedef struct
{
    int iBitmap;     // Number of Picture in the Bitmap, Starting at zero
    int idCommand;   // ID of the button which is sent when pressed
    BYTE fsState;    // Initial State of the button
    BYTE fsStyle;    // The Style of the button
    DWORD dwData;    // User-defined Data, Should be zero if unneeded
    int iString;     // Index of optional string associated with the button
} TBBUTTON;

Each button needs to be individually configured to reflect how it should act, e.g Enabled, Disabled, Toggle-able etc. Lets look at our first button:

TBBUTTON tbut[NUM_BUTTONS];

tbut[0].iBitmap = 0;
tbut[0].idCommand = ID_NEWFILE;
tbut[0].fsState = TBSTATE_ENABLED;
tbut[0].fsStyle = TBSTYLE_BUTTON;
tbut[0].dwData = 0L;
tbut[0].iString = 0;

We do this for each of the buttons in the toolbar. fsState can take of the following values:

TBSTATE_CHECKEDButton is pressed
TBSTATE_ENABLEButton is active
TBSTATE_HIDDENButton is inactive and hidden
TBSTATE_INDETERMINATEButton is greyed out and inactive
TBSTATE_PRESSEDButton is pressed
TBSTATE_WRAPWrap following buttons to next line

fsStyle, again can take any valid combination of the following values:

TBSTYLE_BUTTONYour bog-standard button
TBSTYLE_CHECKButton toggles between on / off when clicked
TBSTYLE_CHECKGROUP         Check button -part of a mutually exclusive group
TBSTYLE_GROUPStandard button -part of mutually exclusive group
TBSTYLE_SEPButton seperator ( see below )

Now that we've defined our buttons, we can finally create our toolbar. We do this with the CreateToolbarEx() function
( bet you could never have guessed ) Much the same way we'd create any other window really. Prototype:

HWND CreateToolbarEx( HWND ParenthWnd,DWORD dwStyle,WORD ID,int NumButtons,HINSTANCE hInst,
              WORD BPID,LPCTBBUTTON ButtonHeight,int BMPWidth,int BMPHeight,UINT Size );

For a detailed explanation of the parameters check your win32 programmers reference, but they should be fairly self explanatory. Lets create our toolbar...

tbarhWnd = CreateToolbarEx( hWnd,WS_VISIBLE ¦ WS_CHILD ¦ WS_BORDER, ID_TOOLBAR, NUM_BUTTONS,
             hInst,ID_TBAR_BMP,tbut,NUM_BUTTONS,0,0,20,19,sizeof(TBBUTTON));

A Note on Seperators

Looking at the sample bitmap, you'll see that the last icon is empty. This is intentional, it will serve as our 'seperator' and is essentially for aesthetic purposes. With this we can group related buttons together on the toolbar.

We define a seperator just as we would a normal button, except we would set the fsStyle to TBSTYLE_SEP.
idCommand should be set to zero.

Modifying After Creation

As with other windows, we can change the status of the bits 'n' bobs of the toolbar by sending it messages. We use the standard api function, SendMessage(). We can send the following messages:

In all cases, wParam should contain the ID of the button, e.g ID_OPENFILE.

TB_CHECKBUTTONPress/Clear Button, lParam != 0 to Press, 0 to clear
TB_ENABLEBUTTONEnable/Disable Button, lParam != 0 to enable, 0 to disable
TB_HIDEBUTTONHide/Show Button, lParam != 0 to hide, 0 to show

For example, say we wanted to disable the Save File button (Disk Icon) We'd write something like this:

SendMessage( tbarhWnd,TB_ENABLEBUTTON,ID_SAVEFILE,0 );

What About When the Button Is Clicked?

Heh, same as with any control. A WM_COMMAND message is sent to your window function, where the lower word of wParam contains the ID of the control that sent the message. Intercept it and act accordingly. See my lame source.

Moving Right Along, Adding Tooltips.

This bit really is piss easy. In case you don't know, tooltips are the lil thingy's that popup to tell you what a button does if you leave the mouse over a control. Firstly we need to add the TBSTYLE_TOOLTIPS style when we create the toolbar. This tells wincrash to send WM_NOTIFY messages to our program whenever the mouse is left over a toolbar button.

Once this message arrives at our doorstep, lParam points to a TOOLTIPTEXT structure. Looks a lil like this:

typedef struct
{
    NMHDR hdr;
    LPSTR lpszText;
    char szText[80];
    HINSTANCE hinst;
} TOOLTIPTEXT;

The NMHDR structure looks a bit like this:

typedef struct
{
    HWND hwndFrom;
    UINT idFrom;
    UINT code;
} NMHDR;

When we are sent this structure (via WM_NOTIFY) we need to examine hdr.code to see if it equals TTN_NEEDTEXT.
If it does we need to look at hdr.idFrom to see which button on the toolbar is requesting this info.

Finally we have a few ways which we can supply the tooltip text, we'll look at the easiest -making lpszText point to the string.

As always we'll try to clear this up, with some more source code :

LPTOOLTIPTEXT TtipText;

    case WM_NOTIFY :
        TtipText = (LPTOOLTIPTEXT)lParam;
        if( TtipText->hdr.code == TTN_NEEDTEXT )
           switch( TtipText->hdr.idFrom )
           {
              case ID_NEWFILE :
              TtipText->lpszText = "Create a New File";
              break;

And so on and so forth.

The End of Another Lame Article

Well, we're done, again. Thanks for reading, hope you've learnt something new. Once again, feel free to make use of any of the stuff on CoRNSouP... as a coder, it's your site too. As always, comments / essays / bugs etc are more than welcome. Mail us... lates.

--NRoC 17/02/99 22:20