MMAPG - OS/2 Multimedia Controls
Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation
This chapter describes how to create and manage your own customized windows, graphic buttons and secondary windows. OS/2 multimedia applications, such as Volume Control shown in the following figure provide you with examples of multimedia control implementations.
The SW.DLL dynamic link library, located in the \MMOS2\DLL subdirectory contains the functions that support creating and manipulating these controls. (The SW.H file is included with the Toolkit and contains the prototypes, constants, and data structures your application needs.) Refer to the PM Guide and Reference for information on creating circular sliders (dials).
Refer to the CUA Guide to Multimedia User Interface Design for more information about creating consistent user interfaces for multimedia controls.
Graphic Buttons
A graphic button is a specialized push button that displays text, or graphics, or both. If a graphic button is defined as a two-state graphic button, it can have an up or down appearance. The two-state button remembers its state and thus can be toggled from up to down and down to up. Unlike a standard push button, the graphic button remains in the changed state after the user has clicked on it.
Individual bitmaps can be displayed along with text while the button is in an up, down, highlighted, or unhighlighted state. A graphic button is highlighted when the mouse select button is held down while the pointer is on the graphic button.
Typically, when a two-state button is up, it is selectable, and when it is down, an action is being processed. The bitmap shown in the up state reflects the action to be processed.
If the button has been defined as an animated graphic button, a series of bitmaps can be displayed to produce an animated effect. If the button is a two-state button, animation can be done for a particular button state, or it can be independent of the change of button state. The drawing of animation bitmaps takes precedence over the current state bitmap.
Any graphic button that draws a bitmap while the button is highlighted or unhighlighted is not intended to be animated. Combining an animation button style with either of these two button styles can produce unpredictable results.
Graphic button text can appear as flat on the button surface, or as three-dimensional, with its z-order raised or recessed, relative to the button surface.
These functional capabilities make graphic buttons ideal to use when designing a multimedia device control panel.
Styles
The following table identifies the graphic button styles that can be specified for a graphic button to increase its functionality. A graphic button with no style bits set has an "up" state and the capability to display bitmaps, or text, or both.
| Style | Description |
|---|---|
| BS_TWOSTATE | Creates a graphic button that has two states: up and down. When the button is in the up state (drawn with its z-order above the owner), this usually indicates it is selectable. When the button is in the down state (drawn with its z-order below the owner), this usually indicates an action is being processed. |
| BS_AUTOTWOSTATE | Creates a two-state graphic button that automatically toggles its state from up to down or down to up whenever the user clicks on it. No messages from the owner are required for the button to toggle its state. |
| BS_ANIMATION | Creates an animated graphic button that displays a series of bitmaps. Display of the series is handled like a circular list; after the last bitmap in the series is displayed, the first bitmap in the series is displayed, and so forth. |
| BS_AUTOANIMATION | Creates an animated graphic button that automatically toggles its animation from off to on or on to off whenever the user clicks on it. Usually, when the button's animation is off, the button is selectable. Conversely, when the animation is on, an action is being processed. |
| BS_HILITEBITMAP | Creates a graphic button that displays a different bitmap when the button is in a highlighted paint state. The highlighted paint state occurs when the user presses the mouse button while the pointer is over the graphic button (or the user holds the spacebar down while the graphic button has the focus). |
| BS_DISABLEBITMAP | Creates a graphic button that displays a different bitmap when the highlighted paint state of the button is disabled. |
| BS_3D_TEXTRECESSED | Creates a graphic button that has three-dimensional text. The text z-order is below the face of the button. The main body of the text is black, and its bottom and right edges are white. |
| BS_3D_TEXTRAISED | Creates a graphic button that has three-dimensional text. The text z-order is above the face of the button. The main body of the text is white, and its bottom and right edges are black. |
Note: Setting both GBS_3D_TEXTRECESSED and GBS_3D_TEXTRAISED is not recommended, because this can cause unpredictable behavior.
Owner Notifications
The owner window of a graphic button is sent a notification code with the WM_CONTROL message whenever the graphic button changes state. The notification code indicates the new state of the button. WM_CONTROL messages are sent with WinSendMsg, which does not return until the application processes the message. These notifications are provided for an application that requires synchronous knowledge of when a graphic button changes state. An example of this type of graphic button is the music scan button shown in the example in section Processing Messages for a CD Player Graphic Button.
At the same time a WM_CONTROL message is sent to the owner, a WM_COMMAND message is also posted to the application message queue with WinPostMsg. The WM_COMMAND informs the application that the graphic button has been selected. If you want to implement a simple graphic button that performs an action, such as a playback operation, you can use code developed for a PM pushbutton, which uses the WM_COMMAND means of notification. The only change you have to make to the code is the window class.
When you create your graphic button, you can take advantage of either of these means of notification.
| Notification Code | Description |
|---|---|
| BN_BUTTONUP | The graphic button is changing to an up paint state from either a down or highlighted paint state. |
| BN_BUTTONDOWN | The graphic button is changing to a down paint state from either an up or highlighted paint state. |
| BN_BUTTONHILITE | The graphic button is changing to a highlighted paint state from either an up or down paint state. |
Control Messages
The following table describes the graphic button control messages that can be used by a graphic button window procedure to manipulate a graphic button:
| Control Message | Description |
|---|---|
| BM_SETGRAPHICDATA | Sets the graphical data (graphic button text, bitmaps) for a graphic button and erases all previous data relating to the state of the button. |
| BM_ANIMATE | Sets the animation of an animated graphic button to start or stop at the first bitmap in the series or at a bitmap within the series. |
| BM_QUERYANIMATIONACTIVE | Sets the animation state of an animated graphic button. |
| BM_SETANIMATIONRATE | Sets, in milliseconds, the period between bitmap updates for an animated graphic button. |
| BM_QUERYANIMATIONRATE | Sets the animation rate that is set for an animated graphic button. |
| BM_SETSTATE | Sets a two-state graphic button to up or down, or toggles its state. |
| BM_QUERYSTATE | Gets the state of a graphic button. Note: For a graphic button that does not have a two-state style, its state is always considered to be "up". |
| BM_SETBITMAPINDEX | Sets the bitmap index to use for the various states of the graphic button; up, down, highlighted, not highlighted, beginning of animation series, end of animation series, current state (refers to either the up or down bitmap). |
| BM_QUERYBITMAPINDEX | Gets the bitmap index used for a particular button state. |
| BM_SETTEXTPOSITION | Sets graphic button text position above or below the bitmap. |
| BM_QUERYTEXTPOSITION | Gets graphic button text position relative to the bitmap. |
Creating Graphic Buttons
The graphic button PM window class WC_GRAPHICBUTTON is similar to the window class of a push button. This window class must be registered with the function WinRegisterGraphicButton before you can create a graphic button.
A graphic button can be created by a CONTROL statement in a dialog resource. A graphic button also can be created by specifying the WC_GRAPHICBUTTON window class parameter of the WinCreateWindow call.
The graphic button should be initialized when its owner receives a WM_INITDLG message. By doing the initializations at this time, the owner has the capability to change the graphic button's bitmaps, text positioning, state, animation rate, and so on, before the button is displayed on the screen.
The GBTNCDATA data structure shown below is the data structure that is allocated to initialize graphic button control data. This structure is required when sending the GBM_SETGRAPHICDATA message.
Note: If you create a graphic button with WinCreateWindow and initialize the GBTNCDATA structure; set the usReserved field to GB_STRUCTURE, rather than GB_RESOURCE, to indicate the structure contains a module handle.
typedef struct _GBTNCDATA
{
USHORT usReserved; /* Reserved */
PSZ pszText; /* Initial graphic button text */
HMODULE hmod; /* Handle of bitmap resource */
USHORT cBitmaps; /* Number of button bitmaps */
USHORT aidBitmap[1]; /* Array of bitmap resource IDs */
} GBTNCDATA;
Graphic button data is set or changed by sending the GBM_SETGRAPHICDATA message with WinSendMsg to the graphic button control window procedure. Using this message to change graphic button data erases any data relating to the button state and sets the button state to the default parameters. The default state of a graphic button is "up." If you want to change only the text of the graphic button without affecting the button state data, use WinSetWindowText.
Mnemonics are supported for graphic button text. As with push buttons, a character in the text is designated as the mnemonic for the button by a preceding tilde (~) character. If the button does not have any text, a null string must be specified.
The number specified for bitmaps associated with the graphic button does not necessarily represent the number of unique bitmaps. A graphic button can have duplicate bitmaps associated with it for animation purposes.
It is assumed that all the bitmaps associated with a particular graphic button are of equal size. The size of a graphic button is determined by the dimensions specified in the CONTROL statement of the dialog resource, as well as the size of the bitmap. If the dimensions in the CONTROL statement are too small to contain the bitmap and the text, the button size is made larger to accommodate the width of the bitmap and the depth of the bitmap plus the text. However, if the width of the text exceeds the width determined by the specified dimensions or the actual bitmap (whichever is greater), the text is truncated.
Animated Graphic Button
The following code fragment is a sample definition for creating an animated graphic button with a CONTROL statement in a dialog resource.
CONTROL "", IDD_TESTPLAY1, 120, 70, 45, 35,
WC_GRAPHICBUTTON,
GBS_AUTOANIMATION | GBS_3D_TEXTRAISED |
WS_VISIBLE | WS_TABSTOP
CTLDATA GB_RESOURCE, "PLAY", 16, ID_PLAY1, ID_PLAY 2, ID_PLAY3,
ID_PLAY4, ID_PLAY5, ID_PLAY6,
ID_PLAY7, ID_PLAY8, ID_PLAY9,
ID_PLAY10, ID_PLAY11, ID_PLAY12,
ID_PLAY13, ID_PLAY14, ID_PLAY15,
ID_PLAY16, 0
The control data for the graphic button provides the text "PLAY" (with no mnemonic) for the button face and associates the names of 16 bitmaps with the button. The bitmaps are assigned indexes 1 through 16, according to the order they appear in the control data.
The list of bitmap IDs is preceded by a number and ends with a zero. The number indicates the total of defined bitmaps. The zero indicates the end of the bitmap array. The number of bitmaps that actually are displayed is determined by the bitmap total or the zero-terminated array, whichever is less. If the number of bitmap IDs is greater than the bitmap total, the extraneous bitmap IDs are ignored. A bitmap cannot have an ID of zero.
Because the style of this graphic button includes GBS_AUTOANIMATION, when the user clicks on this button, it automatically toggles the animation on or off without intervention from the owner window of the graphic button.
An animated graphic button can also be created by specifying the WS_GRAPHICBUTTON window class name as a parameter of the WinCreateWindow call. The following code fragment shows an example of setting up the GBTNCDATA structure with the graphic button data and using the WinCreateWindow call.
HWND hwndGB; /* Graphic button window handle */
PGBTNCDATA pgbtn; /* Pointer to graphic button data */
LONG lSize; /* Size of graphic data */
#define NUMBITMAPS 4 /* Number of bitmaps for button */
lSize = sizeof(GBTNCDATA) + sizeof(USHORT) * (NUMBITMAPS - 1);
pgbtn = (PGBTNCDATA)malloc(lSize);
if (pgbtn)
{
memset(pgbtn, 0, lSize);
pgbtn->pszText = "Text";
pgbtn->cBitmaps = NUMBITMAPS;
pgbtn->aidBitmap[0] = BMP0;
pgbtn->aidBitmap[1] = BMP1;
pgbtn->aidBitmap[2] = BMP2;
pgbtn->aidBitmap[3] = BMP3;
WinRegisterGraphicButton();
/* Create the graphic button. hwnd is the window handle
* of the owning window (for example, client window)
*/
hwndGB = WinCreateWindow (hwnd,
WC_GRAPHICBUTTON,
"", /* No text here; see pgbtn->pszText */
WS_VISIBLE | WS_TABSTOP | WS_POINTSELECT |
GBS_AUTOANIMATION | GBS_3D_TEXTRECESSED,
0,0,80,40,
hwnd,
HWND_TOP,
ID_GB, /* Graphic button identifier */
MPFROMP(pgbtn),
NULL);
WinSendMsg( hwndGB, GBM_SETGRAPHICDATA, MPFROMP(pgbtn), 0);
}
Two-State Graphic Button
The following figure is a sample definition for creating a two-state graphic button in a dialog resource.
CONTROL "", IDD_MP_PAUSE, 65, 10, 40, 30,
WC_GRAPHICBUTTON,
GBS_TWOSTATE | GBS_3D_TEXTRECESSED |
WS_VISIBLE | WS_TABSTOP
CTLDATA GB_RESOURCE, "PAUSE", 3, ID_MP_PAUS0, ID_MP_PAUS1,
ID_MP_PAUS2, 0
The control data for the graphic button provides the text "PAUSE" (with no mnemonic) for the button face and associates the names of three bitmaps with the button. The bitmaps are assigned indexes 0, 1, and 2, according to the order they appear in the control data. In this case, the bitmaps are used to indicate the various states of the button: up, down, and highlighted.
Because the button does not have a GBS_AUTOTWOSTATE style, the owner window must send a GBM_SETSTATE message to the graphic button, requesting the button change its state.
A two-state graphic button can also be created by specifying the WC_GRAPHICBUTTON window class name as a parameter of the WinCreateWindow call. The following figure shows an example of setting up the GBTNCDATA structure with the graphic button data and using the WinCreateWindow call.
HWND hwndTSB; /* Two-state window handle */
PGBTNCDATA pgbtn; /* Graphic button control data */
LONG lSize; /* Size of graphic button control data */
# define NUMBITMAPS 2 /* Number of bitmaps for button */
lSize = sizeof(GBTNCDATA) + sizeof(USHORT) * (NUMBITMAPS - 1);
pgbtn = (PGBTNCDATA)malloc(lSize);
if (pgbtn)
{
memset(pgbtn, 0, lSize);
pgbtn->pszText = "Text";
pgbtn->cBitmaps = NUMBITMAPS;
pgbtn->aidBitmap[0] = BMP0;
pgbtn->aidBitmap[1] = BMP1;
WinRegisterGraphicButton();
/* Create the two-state graphic button. hwnd is the window handle
* of the owning window (for example, client window)
*/
hwndTSB = WinCreateWindow (hwnd,
WC_GRAPHICBUTTON, /* No text here; see pgbtn->pszText */
"",
WS_VISIBLE |, WS_TABSTOP | WS_POINTSELECT
| GBS_AUTOTWOSTATE | GBS_3D_TEXTRECESSED,
0,0,80,40,
hwnd,
HWND_TOP,
ID_TSB, /* Graphic button identifier */
MPFROMP(pgbtn),
NULL);
WinSendMsg(hwndTSB, GBM_SETGRAPHICDATA, MPFROMP(pgbtn), 0);
WinSendMsg(hwndTSB, GBM_SETBITMAPINDEX,
MPFROMSHORT(GB_DOWN), /* Which state */
MPFROMSHORT(GB_INDEX_LAST)); /* Which bitmap */
}
Processing Messages for a CD Player Graphic Button
The following code fragment illustrates a message-handling procedure for a CD player window. This example demonstrates synchronization of a graphic button and control of a CD player.
CD_Player_Message_Proc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
{
switch (msg) {
...
case WM_CONTROL:
/* If the Music Scan GraphicButton sent the notification... */
if (SHORT1FROMMP(mp1) == ID_MUSIC_SCAN) {
switch (SHORT2FROMMP(mp1)) { /* type of notification */
case GBN_BUTTONHILITE: /* button held down */
CD_Music_Scan_Start (...);
break;
case GBN_BUTTONUP: /* button released */
CD_Music_Scan_Stop (...);
break;
...
}
...
}
...
break;
...
}
}
When the music scan button on a physical CD player is held down, it allows the listener to hear the music played at an accelerated rate. In the example shown in the previous figure, the effect of the message procedure is similar. If the user holds the mouse button down and the pointer is over the Music Scan graphic button, the CD_Music_Scan_Start function is called. If the mouse button is released, or the pointer is removed from the Music Scan graphic button, then the CD_Music_Scan_Stop function is called.
Secondary Windows
A secondary window provides a sizeable and scrollable dialog interface. Secondary window functions are compatible with PM dialog window functions, so it is an easy task to make changes to existing code that uses the PM dialog window functions.
The secondary window uses two frame windows, a standard frame and a dialog frame. The standard frame window offers the standard services-moving, sizing, minimizing, maximizing, and closing-as well as a service for resizing the window to a default size.
The window handle returned by WinLoadSecondaryWindow is the handle to the standard frame. This handle is used when associating a help instance, modifying the title bar or system menu, and doing WinSetWindowPos operations.
The window handle that is used to pass messages to the secondary window procedure pointed to by WinLoadSecondaryWindow is the dialog window handle. This handle is used to access controls on the dialog with the WinWindowFromID function. An application can get a handle to the dialog window by passing the outer frame window handle to WinQuerySecondaryHWND. The dialog window is a child of the standard window's FID_CLIENT window.
When the secondary window is opened, the frame window is set to the default size, which accommodates the dimensions of the dialog window. If the user makes the client window smaller by sizing the frame window, the dialog window is clipped and vertical and horizontal scroll bars appear. Selecting Default Size restores the frame window to the optimal size for displaying the dialog. Because the Sizeable Dialog Frame Manager manages the display of the standard window and its scroll bars automatically, the application needs to manage only the dialog window.
The OS/2 multimedia Volume Control application, shown in the beginning of the OS/2 Multimedia Controls section, provides an example of a modeless secondary window implementation.
Compatibility with PM Dialog Window Functions
The design of secondary window functions is very much like the design of dialog window functions. The following table, which lists the secondary window functions, includes the names of PM dialog window equivalent functions.
| Secondary Window Function | PM Dialog Window Equivalent | Description |
|---|---|---|
| WinLoadSecondaryWindow | WinLoadDlg | Creates a modeless secondary window from a dialog template in a resource. |
| WinSecondaryWindow | WinDlgBox | Creates a modal secondary window from a dialog template in a resource DLL and returns the result value established by the WinDismissSecondaryWindow call. WinSecondaryWindow combines the functions of WinLoadSecondaryWindow, WinProcessSecondaryWindow, and WinDestroySecondaryWindow. |
| WinProcessSecondaryWindow | WinProcessDlg | Processes a modal secondary window by dispatching messages while the modal window is displayed. |
| WinDismissSecondaryWindow | WinDismissDlg | Causes modal WinProcessSecondaryWindow or WinSecondaryWindow calls to return. |
| WinDestroySecondaryWindow | WinDestroyWindow | Destroys a secondary window. |
| WinDefSecondaryWindowProc | WinDefDlgProc | Provides the default behavior for a secondary window. A secondary window procedure must reference this function for messages that are not handled explicitly. |
| WinCreateSecondaryWindow | WinCreateDlg | Creates a Secondary Window from a dialog template in the application's executable file. |
| WinSecondaryMessageBox | WinMessageBox | Creates a modal window that can be used to display error messages and ask questions. |
| WinQuerySecondaryHWND | WinQueryWindow | Returns either the handle to the outer frame or inner dialog window of a secondary window, depending on the handle supplied as input. |
| WinDefaultSize | None | Sizes the dialog window to its recommended, optimal size. |
| WinInsertDefaultSize | None | Adds the Default Size item to the system menu of a secondary window. |
Creating a Secondary Window
WinLoadSecondaryWindow and WinSecondaryWindow create secondary windows from dialog templates in a resource file. WinSecondaryWindow creates a modal window and supports the processing and destruction of the modal dialog window. WinSecondaryWindow is equivalent to the the following code sequence.
hwndSW = WinLoadSecondaryWindow(...); usResult = WinProcessSecondaryWindow(hwndSW); WinDestroySecondaryWindow(hwndSW);
WinSecondaryWindow and WinProcessSecondaryWindow functions do not return until WinDismissSecondaryWindow is called. If your secondary window procedure handles WM_COMMAND messages, it must call WinDismissSecondaryWindow after calling the WinSecondaryWindow or the WinProcessSecondaryWindow function. Optionally, your window procedure can pass WM_COMMAND messages to WinDefSecondaryWindowProc, which calls WinDismissSecondaryWindow.
WinDismissSecondaryWindow hides the secondary window and returns a result code for a WM_COMMAND message, causing WinSecondaryWindow and WinProcessSecondaryWindow to return. For example, if the user selects the OK button, your window procedure passes the DID_OK code with WinDismissSecondaryWindow. Although the secondary window is hidden, it still exists. In the case of WinProcessSecondaryWindow, the window procedure must call WinDestroySecondaryWindow. Before WinSecondaryWindow returns, it destroys the secondary window.
Modeless Secondary Window
WinCreateSecondaryWindow or WinLoadSecondaryWindow can be used to create a modeless secondary window. WinCreateSecondaryWindow creates a secondary window from a dialog template that is stored in the application's executable file. WinLoadSecondaryWindow creates a secondary window from a dialog template that is stored in a dynamic link library.
If the template is a resource in a dynamic link library, the application loads the dynamic link library by calling DosLoadModule, and then loads the dialog by calling WinLoadSecondaryWindow (or WinSecondaryWindow, which calls WinLoadSecondaryWindow). A WM_INITDLG message is sent to the secondary window procedure before WinLoadSecondaryWindow returns.
#define INCL_SECONDARYWINDOW /* Secondary window functions */
#include <sw.h>
PDLGTEMPLATE pdlgt;
DosGetResource (NULL, RT_DIALOG, ID_DIALOG, (PVOID) pdlgt);
WinCreateSecondaryWindow ( HWND_DESKTOP, /* Parent window */
NULL /* Owner window */
MyDlgProc /* Dialog procedure */
pdlgt /* Dialog template */
NULL); /* Create parameters */
If the template is a resource in the application's executable file, the application loads the resource by calling DosGetResource (as shown in the previous figure) and then uses the template with WinCreateSecondaryWindow to create a secondary window. This method of using a dialog template gives the application the advantage of reviewing and modifying the template before creating the secondary window.
The difference between a modal and a modeless secondary window is the way the windows handle input. For a modal secondary window, WinSecondaryWindow and WinProcessSecondaryWindow handle all user input to the window with an internal message loop and prevent access to other windows in the application. For a modeless secondary window, the application relies on a normal message loop to dispatch messages to the secondary window procedure and does not use WinSecondaryWindow or WinProcessSecondaryWindow.
Secondary Window Message Box
WinSecondaryMessageBox is analagous to WinMessageBox. Both functions create a modal message box that can be used to display error messages and ask questions.
WinSecondaryMessageBox allows more flexibility than WinMessageBox, because you can define the text that appears on the buttons, rather than choosing from a set of standard buttons with predetermined text (OK, Cancel, and so forth). WinSecondaryMessageBox uses the SMBD and SMBINFO data structures, found in the SW.H file.
The SMBD structure defines the button style, text and ID for each button included in the secondary message box:
typedef struct _SMBD {
CHAR achText[MAX_SMBDTEXT + 1]; /* Text of the button,
for example, '~Cancel'. */
ULONG idButton; /* Button ID returned when user
chooses button. */
LONG flStyle; /* Button style ORed with
internal styles. */
} SMBD;
typedef SMBD * PSMBD;
The SMBINFO structure defines the icon used in the message box, specifies the number of buttons in the message box, and points to the array of button definitions.
typedef struct _SMBINFO {
HPOINTER hIcon; /* Icon handle */
ULONG cButtons; /* Number of buttons */
ULONG flStyle; /* Icon style flags (MB_ICONQUESTION) */
HWND hwndNotify; /* Reserved */
PSMBD psmbd; /* Array of button definitions */
} SMBINFO;
typedef SMBINFO * PSMBINFO;
Adding Default Size to the System Menu
The WinInsertDefaultSize adds the Default Size selection to the system menu of the secondary window. This call should be made during the initialization of the secondary window.
When the user selects Default Size from the system menu, an SC_DEFAULTSIZE system command is sent to the secondary window procedure, which calls WinDefaultSize to size the window to its optimal default size.
#define INCL_SECONDARYWINDOW
#include <sw.h>
HWND hwndFrame
hwndFrame = WinLoadSecondaryWindow (HWND_DESKTOP, /* Parent window */
HWND_DESKTOP, /* Owner window */
MyDlgProc, /* Dialog proc */
NULL, /* Module handle */
ID_DIALOG, /* Resource ID */
NULL); /* Create params */
WinInsertDefaultSize (hwndFrame, "~Default size"); /* Insert
menu item */
WinDefaultSize (hwndFrame); /* Set window to its
default size */
