How do I? - Part 12

From EDM2
Jump to: navigation, search
How Do I? / Part
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19

by Eric Slaats

Hi all. I had an inspiring month. Lots of changes on my desktop. The first thing that has changed is that I finally completely changed to Warp 4.0. It's certainly a better system than Warp 3.0! The problem, though, is making the Desktop as comfortable as it has grown to be over time in Warp 3.0. You know, just like a real desktop, where you can find all your stuff without blinking.

For some months now I have used both versions side by side but Warp 3.0 is now my backup system and 2.1 is removed permanently. One of the reasons I made this choice is that I decided to put my old 486 DX2 back to work; I nicked two obsolete Ethernet cards at work and created a small network at home. This is great for testing stuff I'm working on right now. I'm in the process of doing an Intranet at work and this setup is great for checking out several packages.

On the server side I've got Windows NT 4 server and Warp 4 installed. It took quite some time to get Warp to talk smoothly with NT. It seems that MS is trying everything to keep IBM off of their network. Of course this fails because the MS networking technology is based on an IBM legacy. (Besides, Warp 4.0 peer to peer works better than NT's!) However, after I used Netscape Communicator on the NT side, I can't wait till the OS/2 version comes along. It's very bulky, not too fast, but it looks good and it has an impressive array of capabilities. If you use it with the Netscape SuiteSpot package as server software, the NT look and feel completely disappears. I'm beginning to understand why MS is so scared of Netscape. <g>

OK, enough of my adventures. The last thing we did last month was discuss some theory. I still feel that's always needed, but having practical knowledge is also important. So now we go back to the programming philosophy I stated for this column. The first part was KISS (Keep It Simple Stupid). The second part was, 'Be Lazy', or never do something yourself that the system can do for you. This month we'll talk more about dialogs, but we're going to look at some very simple constructs and be very lazy!

Until now we have always created frame windows to build our simple applications in. This means we have had to call all kinds of initial functions to make sure the whole contraption works. Here's a sample list of stuff that, in most cases, has had to be done:

  1. WinInitialize
  2. WinCreateMsgQueue
  3. WinRegisterClass
  4. WinCreateStdWindow
  5. while (WinGetMsg (hab, &qmsg, 0, 0, 0)) WinDispatchMsg (hab, &qmsg);
  6. WinDestroyWindow (hwndFrame);
  7. WinDestroyMsgQueue (hmq);
  8. WinTerminate (hab);

The stuff between the lines all has to do with the creation of the window. And believe me this is the simple form! In larger applications, windows for the client area will be created, status lines, toolbars etc., etc. This brings a lot of flexibility, but can also get rather complex.

However, there are a number of applications imaginable in which it isn't necessary to create a frame window. Maybe a dialog window will be enough. This has the big advantage that it can be created with a dialog editor! Applications with a lot of controls in the client area will especially benefit from this approach. SmeHtm (Smalled Html extensions) is an example of an application that is created almost completely in this fashion (through the use of the dialog editor).

The disadvantage to this method is that there is no easy way to attach a menu, scrollbars, an icon, etc. by using the FCF_ flags. However, there are ways around this problem. This month we will take a look at how to create an application that will have a dialog as the main window and we'll attach a menu to make it more complete.

A very simple program

First things first. before we get into the 'difficult' stuff of attaching a menu to a dialog box, let's first examine how a dialog can be used as a main program. In earlier articles we saw that a modal dialog can be started with the WinDlgBox API call. With this call we won't have to worry about message queues, message dispatching, etc. So the following small program will work for any Dialog that the DIALOGID matches for!

void main(void)
	HAB hab = WinInitialize(0);
	HMQ hmq = WinCreateMsgQueue(hab,0);



With this program you could execute any dialog on the Desktop. (Just try it.) The basic dialog functionality can be tested very easily. Of course the dialog won't do much because the WinDefDlgProc call for the window procedure is used, but it works! This means that for simple applications (even ones with a sizing border), we can really simplify our program. Nice, eh?

Now, to add basic functionality with a 'normal' way to handle the things, we must make some shortcuts. In a frame-window we can handle things by using the FCF_ flags. Most of the visual stuff that is done with the FCF_ flags can be done to dialogs with dialog styles. These can easily be applied with dialog editors. (We won't delve into the way dialogs are created right now. This kind of theory will be handled another time.) There are two major things though that can't be handled by dialog editors while writing .RC files. These are adding a menu to the dialog and adding a (system) icon to the applet. We'll attend to both these issues in a minute. But first:

We're to handle the initialization stuff?

What is the best time to attach an icon or attach a menu to our dialog window? In the frame window samples this wasn't an issue because OS/2 took care of that during the window creation. With a dialog, this is different. Of course we can do it right after the dialog is created in the main part of the program. Personally, I don't find this a very elegant method, though. What we would like to do is simulate what happens in a normal frame window creation: handle this stuff during the creation of the window.

Now, every window when it is being created gets a message sent to it notifying it that it is being created (sounds weird, doesn't it?). For frame windows and the like, this was the WM_CREATE message. This is the place to handle all the extra initializing of the window. If you ever try to handle WM_CREATE for a dialog though, you will find that it doesn't work! Just as dialogs have their own default window procedure, they also have a specific 'dialog is being created' message. This is the WM_INITDLG message. So to attach the menu and the icon we will use the event of this message.

Adding an Icon

We live in a visual era. And if I'm not mistaken, things will only get worse! So attaching an icon to your application is probably the minimum you'll have to do. We saw that with frame windows this is really easy. Simply define an icon in your resource file with the right ID and include the FCF_ICON flag when creating the window. Of course the defining of the icon in the resource file still has to be done. If we've done that, we have to start looking for a way to stick the icon to the window. Now this is a quest in itself! I often find that when I want to try something new, I simply don't know where to look. Should I use a function, is the function simply a macro which will call a message, or should we use a message? I found that in most cases if you want to do something to a window (or any control for that matter), it usually pays to browse through the messages. And indeed, there is a message that will set the icon for a window; it's the WM_SETICON message.

NOTE: I had a hard time finding this message in my documentation! When you look it up under "messages", you'll find that it isn't in the index. And searching for WM_SETICON doesn't give the desired results! The strange thing is that the WM_SETICON message pops up when you search on the word "icon". A little bug for the OS/2 team to fix.

The WM_SETICON message accepts only one parameter, the mp1 should contain a handle (pointer) to an icon. (Grunt) Now that again, how do we get the handle of an icon? Yes, we start browsing the help function again, only to bump into another surprise. If you want to load an icon, you should use a function that loads a pointer. A pointer is in essence just an icon with a hot spot. Icons and pointers also both have "shine through" areas. Well, knowing this, we can find the desired function. We're looking for WinLoadPointer. This function returns a HPOINTER type variable.

HWND		hwndDeskTop	//Desktop-window handle.
HMODULE        Resource        //Resource identity containing the pointer definition.
ULONG		idPointer	//Identifier of the pointer to be loaded
HPOINTER	hptr		//Pointer handle return value

hptr = WinLoadPointer(hwndDeskTop,  Resource,  idPointer);

The first parameter of this function is a simple default that OS/2 uses. The HWND_DESKTOP identifier always holds the handle to the Desktop. The Resource is also easy. It's the same as with the earlier frame window examples, the resource is coupled to the .EXE file. This means we don't have to do difficult stuff with load modules and we can use NULLHANDLE here. Since we know the icon ID, we can finally write the code needed to do the magic we want and place it in the window procedure of the dialog.

WinSendMsg(hwndDlg, WM_SETICON, (MPARAM) hptr, 0l);

Adding the menu

For this month's sample (ZIP, 17.7k) I used a menu created for an earlier How Do I? column, so don't be surprised by a somewhat strange menu. The function to load a menu resource is the WinLoadMenu call.

HWND	       hwndFrame       //Owner- and parent-window handle
HMODULE       hmod            //Resource identifier containing the menu definition.
ULONG	       idMenu	       //Menu identifier within the resource file

WinLoadMenu(hwndFrame, hmod, idMenu);

We shouldn't be distracted by the fact that this call mentions the use of a frame window. Don't pay attention to this, and use the dialog window handle in this field. For HMODULE, the menuID goes the same as for the icon. The resource is contained in the .EXE file, so a NULLHANDLE will be used. The MenuID is also known. This leads to the following code line for this month's sample:

WinLoadMenu(hwndDlg, NULLHANDLE, MAIN);

If we try this it doesn't work (another grunt), but why? A frame window, just as a dialog window, is constructed out of a set of windows. There is the client area, the title bar, min, max, close, system menu, etc. Once a frame window is created it holds these windows in a certain order (we'll attend to that in a coming article). What we did was load a menu resource, but we didn't tell the frame window (dialog window in our case) that this menu is one of the frame controls and that it is added. This notification can be given to the frame window by invoking the WM_UPDATEFRAME message. In this message we must pass the FCF_ flag that identifies the added (or removed) frame control. The frame window procedure will update the frame window accordingly and thus show our menu. This leads to the following code in WM_INITDLG:

WinLoadMenu(hwndDlg, NULLHANDLE, MAIN);

To prove this works I added some simple code for the WM_COMMAND message. Every time a menu command is given, the WM_COMMAND sent to the dialog window procedure will produce a beep.

Well, that's it for this month. Next month we'll delve further in the possibilities of dialogs. For now, happy hunting.