TweetFollow Us on Twitter

May 98 - Getting Started

Volume Number: 14 (1998)
Issue Number: 5
Column Tag: Getting Started

Handling Modeless Dialogs

by Dave Mark

Looking deeper into the Dialog Manager

Over the last few columns, we've explored the mysteries of the Dialog Manager via a program called Dialogger. As promised, the next few columns will go deeper into the Dialog Manager, presenting a program called Modeless. Modeless implements a modeless dialog, a dialog that looks and acts like a regular window, but retains the benefits of being managed by the Dialog Manager.

Modeless dialogs require a change in the usual modal programming strategy. Modal dialog handling code is usually pretty self-contained. Modeless code tends to be larger and more spread out, as you'll see when we get to the code.

Creating the Modeless Resources

Most of the resources we'll need for this month's column can be copied from last month's resource file, Dialogger.rsrc. Start by creating a new folder in your Development folder named Modeless. Next, duplicate the file Dialogger.rsrc and drag the copy into the Modeless folder. Change the name of this file to Modeless.rsrc.

Next, launch ResEdit by double-clicking the file Modeless.rsrc. The Modeless.rsrc window should look similar to the one shown in Figure 1.

Figure 1. Modeless.n.rsrc, before surgery.

Your first mission is to edit the ALRT, replacing any references to Dialogger with the appropriate reference to Modeless. Double-click on the ALRT icon. When the ALRT-picker window appears, double-click the only ALRT listed, the one with a resource ID of 129. When the ALRT editor appears, double-click the ALRT window so the DITL editor appears. Finally, double-click the static text item and, when the static text editor appears, make your changes. Mine are shown in Figure 2.

Figure 2. A new version of the About... ALRT.

When you're happy with your About message, close all the windows until you are back at the main window. Next, you'll change the About Dialogger item in the Apple menu to read About Modeless.

Double-click the MENU icon to bring up the MENU picker window. Next, double-click the Apple MENU, bringing up the MENU editing window. Click the About Dialogge. item and change it so it says About Modeless, as shown in Figure 3.

Figure 3. A new version of the MENU resource.

Close the Apple MENU editing window, then double-click the File menu, bringing up the File MENU editing window. Click the Settings item and then click in the Cmd-key: field. Type the letter S in the Cmd-key: field. This ties the command-key equivalent S to the Settings item in the File menu. My File MENU editing window is shown in Figure 4.

Figure 4. A new version of the File MENU resource.

Once you are done, close all the windows leaving just the main window open. Our final chore is to edit the DLOG and DITL resources that made up Dialogger's modal dialog, changing them to reflect the look of a modeless dialog.

Double-click the DLOG icon to bring up the DLOG-picker window. Next, double-click the only DLOG listed, the one with the resource id of 128. The Settings DLOG editing window should appear. First, click the Initially visible check box, making sure it is unchecked.

Next, click the second window icon from the left, at the top of the editing window. The DLOG window should change to reflect your new selection. Modeless dialogs don't have the traditional double-border of their modal cousins. Instead, they look like regular windows. In this case, we want to give our dialog window a close box, so make sure the Close box check box is checked.

We also want to give our dialog window a title. Select Set 'DLOG' Characteristics from the DLOG menu. When the 'DLOG' Characteristics dialog appears, type the text Fred Settings in the Window title: field and click OK. Your new title should be reflected in the DLOG editing window. Figure 5 shows my DLOG editing window at this point in the process.

Figure 5. The DLOG editing window, at the half-way point.

Next, we'll edit the DITL associated with this DLOG. Double click the DLOG window (within the editing window) so the DITL editing window appears. You are now going to delete all the items in the DITL except the Pick one: static text item and the three radio buttons.

Click the OK button and press the delete key. Click the Cancel button and press the delete key. Click the Show preview check box and press the delete key. Finally, click the user item (the grey rectangle) and press delete.

Next, you'll change the positioning of the remaining four items. Double-click the Pick one: static text item and change its coordinates to the ones shown in Figure 6. Close the window when you are done.

Figure 6. New coordinates for the Pick one: static text item.

Double-click the Afghan radio button and change its coordinates to the ones shown in Figure 7. Close the window when you are done.

Figure 7. New coordinates for the Afghan radio button.

Double-click the Elephant radio button and change its coordinates to the ones shown in Figure 8. Close the window when you are done.

Figure 8. New coordinates for the Elephant radio button.

Double-click the Squirrel radio button and change its coordinates to the ones shown in Figure 9. Close the window when you are done.

Figure 9. New coordinates for the Squirrel radio button.

Now we're almost done. Close the DITL editing window, leaving you back in the DLOG editing window. The last thing left to do is to resize the DLOG to reflect its slimmed down and rearranged DITL. Change the Bottom: to 123 and the Right: to 234. The final version of our DLOG is shown in Figure 10.

Figure 10. The final version of our DLOG.

Quit ResEdit, being sure to save your changes. Now we're ready to enter our source code.

Creating the Modeless Project

Launch CodeWarrior and create a new project based on the MacOS:C/C++:Basic Toolbox 68k stationary. Turn off the Create Folder check box. Name the project Modeless.mcp and place it in your Modeless folder. Remove SillyBalls.c and SillyBalls.rsrc from the project; we will not be using these files. From the Finder, drag and drop your Modeless.rsrc file into the project window. You also can remove the ANSI Libraries group from the project, because we won't need them, either.

Select New from the File menu to create a new window. Save it with the name Dialogger.c in your Modeless folder. Select Add Window from the Project menu to add Dialogger.c to the project. Your project window should look something like Figure 11.

Figure 11. Dialogger project window.

Rather than print the code here twice, we'll go straight to the walk-through. You can type in the code as we discuss it below and you will end up with the complete program, or you can save your fingers some effort and get the complete project from MacTech's ftp site ftp://ftp.mactech.com/src/mactech/volume14_1998/14.05.sit.

Walking Through the Source Code

Much of the Modeless source code will look familiar to you from earlier programs. As usual, Modeless starts off #including necessary header files, then it begins a series of #defines. The first three define the base resource ID, and the resource IDs for the ALRT and DLOG resources.

#include <Controls.h>
#include <Dialogs.h>
#include <Menus.h>
#include <Quickdraw.h>
#include <Sound.h>

#define kBaseResID        128
#define kAboutALRTid      129
#define kDialogResID      128

kVisible, kMoveToBack, kMoveToFront, and kNoGoAway are used in the calls to NewWindow() and GetNewDialog(). kSleep, as usual, is passed to WaitNextEvent().

#define kVisible          true
#define kMoveToBack       NULL
#define kMoveToFront      (WindowPtr)-1L
#define kNoGoAway         false
#define kSleep            7L

kOn and kOff are passed to SetControlValue() to turn a radio button on and off.

#define kOn                 1
#define kOff                0

These three #defines define the item IDs for the three radio buttons that appear in the modeless dialog.

#define iAfghan             1
#define iElephant           2
#define iSquirrel           3

kLeftMargin and kTopMargin determine the position of the My Pet Fred window on the screen.

#define kLeftMargin         5
#define kTopMargin         40

kFirstRadio defines the ID of the first radio button in the modeless dialog. kLastRadio defines the ID of the last radio button in the set.

#define kFirstRadio        1
#define kLastRadio         3

The remainder of the #defines represent the Modeless menus and menu items.

#define mApple             kBaseResID
#define iAbout             1

#define mFile              kBaseResID+1
#define iSettings          1
#define iQuit              3

Modeless makes use of four global variables. gDone is set to true until the program is ready to exit. gCurrentPICT contains the ID of the current My Pet Fred PICT. gSettingsDLOG is a pointer to the modeless dialog. We made this a global so we could keep the modeless dialog settings around, even if we close the dialog. gFredWindow points to the My Pet Fred window. We'll take advantage of this pointer when we delete the My Pet Fred window and create a new one.

Boolean     gDone;
short       gCurrentPICT = kBaseResID;
DialogPtr   gSettingsDLOG = NULL;
WindowPtr   gFredWindow = NULL;

As always, we created a function prototype for each of the Modeless functions.

void         ToolBoxInit( void );
PicHandle    LoadPICT( short picID );
void         CreateWindow( void );
void         MenuBarInit( void );
void         EventLoop( void );
void         DoEvent( EventRecord *eventPtr );
void         DoDialogEvent( EventRecord *eventPtr );
void         HandleMouseDown( EventRecord *eventPtr );
void         HandleMenuChoice( long menuChoice );
void         HandleAppleChoice( short item );
void         HandleFileChoice( short item );
void         DoUpdate( EventRecord *eventPtr );
void         CreateDialog( void );
void         FlipControl( ControlHandle control );
void         SwitchPICT( void );

main() starts off by initializing the Toolbox. Next, the menu bar is set up and the My Pet Fred window is created. Finally, we begin the main event loop.

/********** main **********/

void   main( void )
{
   ToolBoxInit();
   MenuBarInit();
   
   CreateWindow();
   
   EventLoop();
}

Nothing new about ToolBoxInit().

/********** ToolBoxInit **********/

void   ToolBoxInit( void )
{
   InitGraf( &qd.thePort );
   InitFonts();
   InitWindows();
   InitMenus();
   TEInit();
   InitDialogs( NULL );
   InitCursor();
}

Just like its Dialogger counterpart, LoadPICT() takes a resource ID as an input parameter, loads the specified PICT resource, then returns a handle to the PICT.

/********** LoadPICT **********/

PicHandle   LoadPICT( short picID )
{
   PicHandle   pic;
   
   pic = GetPicture( picID );

If the PICT could not be loaded for some reason, the program beeps once, then exits.

   if ( pic == NULL )
   {
      SysBeep( 10 );   /* Couldn't load the PICT resource!!! */
      ExitToShell();
   }

   return( pic );
}

CreateWindow() creates a new My Pet Fred window.

/********** CreateWindow **********/

void   CreateWindow( void )
{
   PicHandle   pic;
   Rect            r;

First, LoadPICT() is called to load the PICT specified by gCurrentPICT.

   pic = LoadPICT( gCurrentPICT );

Next, the PICT's bounding rectangle is stored in the local variable r.

   r = (**pic).picFrame;

OffsetRect() is called to normalize the Rect, keeping it the same size as the PICT, but moving its upper left corner to the position specified by kLeftMargin and kTopMargin. Basically, we're setting up the bounding rectangle for the new My Pet Fred window.

   OffsetRect( &r, kLeftMargin - r.left,
               kTopMargin - r.top );

This rectangle is passed to NewWindow(). The new window is made visible. Notice that kMoveToBack is passed instead of our normal kMoveToFront. Why? We want the window to appear behind the modeless dialog window, if the dialog is currently visible.

   gFredWindow = NewWindow( NULL, &r, "\pMy Pet Fred", 
         kVisible, noGrowDocProc, kMoveToBack, kNoGoAway, 0L );

If the window couldn't be created for some reason, beep once, then exit.

   if ( gFredWindow == NULL )
   {
      SysBeep( 10 );   /* Couldn't load the WIND resource!!! */
      ExitToShell();
   }

Finally, make the window visible (this line is unnecessary because we created the window with kVisible, but it's a good habit to get into) and make it the current port. We'll draw the current Fred by responding to an update event.

   ShowWindow( gFredWindow );
   SetPort( gFredWindow );
}

MenuBarInit() loads the MBAR resource, and makes it the current menu bar.

/********** MenuBarInit **********/

void   MenuBarInit( void )
{
   Handle            menuBar;
   MenuHandle      menu;
   
   menuBar = GetNewMBar( kBaseResID );
   SetMenuBar( menuBar );

Next, a handle to the Apple menu is retrieved and the desk accessories added to the menu.

   menu = GetMenuHandle( mApple );
   AppendReMenu( menu, 'DRVR' );

Finally, the menu bar is redrawn.

   DrawMenuBar();
}

EventLoop() looks much the same. As you'd expect, the program exits when gDone is set to true.

/********** EventLoop **********/

void   EventLoop( void )
{      
   EventRecord      event;
   
   gDone = false;
   while ( gDone == false )
   {
      if ( WaitNextEvent( everyEvent, &event, kSleep, NULL ) )
         DoEvent( &event );
   }
}

DoEvent() is slightly different than previous incarnations.

/********** DoEvent **********/

void   DoEvent( EventRecord *eventPtr )
{
   char      theChar;

The first difference lies in our call of IsDialogEvent(). IsDialogEvent() takes a pointer to an EventRecord as a parameter and returns true if the event is associated with a modeless dialog. Note that our code calls IsDialogEvent() whether or not a modeless dialog is currently open. Since we aren't that concerned with efficiency here, this is just fine. In a more complex program, you might want to check to see if any of your modeless dialogs are open before you call IsDialogEvent(). Obviously, if your program contains no modeless dialogs, you shouldn't call IsDialogEvent().

   if ( IsDialogEvent( eventPtr ) )
   {

If IsDialogEvent() returns true, the event is associated with our modeless dialog box and we'll pass it along to DoDialogEvent() for processing.

      DoDialogEvent( eventPtr );
   }

If the event is not associated with a modeless dialog, we'll process the event as we always did, using a switch statement.

   else
   {
      switch ( eventPtr->what )
      {

A mouseDown is handled by HandleMouseDown().

         case mouseDown: 
            HandleMouseDown( eventPtr );
            break;

keyDown and autoKey events are turned into characters and turned from command-key equivalences (if appropriate) into menu selections by MenuKey(). The menu selections are handled by HandleMenuChoice().

         case keyDown:
         case autoKey:
            theChar = eventPtr->message & charCodeMask;
            if ( (eventPtr->modifiers & cmdKey) != 0 ) 
               HandleMenuChoice( MenuKey( theChar ) );
            break;

updateEvts are handled by DoUpdate(). Since the update events for our modeless dialog will have been diverted to DoDialogEvent(), any updateEvts passed to DoUpdate() will be for the My Pet Fred window.

         case updateEvt:
            DoUpdate( eventPtr );
            break;
      }
   }
}

All events for the modeless dialog are passed to DoDialogEvent().

/********** DoDialogEvent **********/

void   DoDialogEvent( EventRecord *eventPtr )
{
   short         itemHit;
   short         itemType;
   Handle        itemHandle;
   Rect          itemRect;
   short         curRadioButton, i;
   char          theChar;
   Boolean       becomingActive;
   MenuHandle    menu;
   DialogPtr     dialog;

We'll start off by fetching a handle to the File menu. Why? We're going to dim the Settings item if the modeless dialog is up front. Remember, we're doing this just to demonstrate how it's done, not because it's needed.

   menu = GetMenuHandle( mFile );

Notice that we process keyDown and autoKey events in two different places. If the modeless dialog is up front, DoDialogEvent() will get all keyDowns and autoKeys. If the modeless dialog is not up front or is not open, the normal event handling mechanism will get the keyDowns and autoKeys.

   switch ( eventPtr->what )
   {
      case keyDown:
      case autoKey:
         theChar = eventPtr->message & charCodeMask;
         if ( (eventPtr->modifiers & cmdKey) != 0 ) 
            HandleMenuChoice( MenuKey( theChar ) );
         break;

If the modeless dialog window is either being activated or deactivated, DoDialogEvent() will get an activateEvt. In that case, we'll use the activeFlag to determine whether becomingActive should be set to true or false.

      case activateEvt:
         becomingActive = ( (eventPtr->modifiers & activeFlag)
                              == activeFlag );

If the modeless dialog window is becoming active, we'll use HiliteControl() to enable all the radio buttons, from kFirstRadio to kLastRadio, then we'll disable the File menu's Settings item.

         if ( becomingActive )
         {
            for ( i=kFirstRadio; i<=kLastRadio; i++ )
            {
               GetDialogItem( gSettingsDLOG, i, &itemType,
                     &itemHandle, &itemRect );
               HiliteControl( (ControlHandle)itemHandle, 0 );
            }
            DisableItem( menu, iSettings );
         }

If the modeless dialog window is being deactivated, we'll dim all the radio buttons and enable the Settings item. When the user goes to the File menu and sees that Settings is dimmed, they'll have a clue that the modeless dialog is already up front and ready to use.

         else
         {
            for ( i=kFirstRadio; i<=kLastRadio; i++ )
            {
               GetDialogItem( gSettingsDLOG, i, &itemType,
                     &itemHandle, &itemRect );
               HiliteControl( (ControlHandle)itemHandle, 255 );
            }
            EnableItem( menu, iSettings );
         }
         break;
   }

The previous chunk of code accomplished two things. First, it made sure that command-key equivalents were supported. Second, it made any user interface adjustments that were not normally handled by the Dialog Manager. We decided to dim the radio buttons when the modeless window is deactivated and dim the Settings item when the modeless window is activated. These user interface adjustments are window trimmings that we decided to add. The program would still work without them.

Next, we're going to the things that absolutely must be done. DialogSelect() is the modeless version of ModalDialog(). DialogSelect() takes the event pointer and maps it to a specific dialog and a specific item in the dialog. DialogSelect() returns true if we need to do some processing (if an item was actually hit).

   if ( DialogSelect( eventPtr, &dialog, &itemHit ) )
   {

Since our dialog was relatively simple, we know that itemHit is going to be one of iAfghan, iElephant, or iSquirrel.

      switch ( itemHit )
      {
         case iAfghan:
         case iElephant:
         case iSquirrel:

Before we process the radio button click, we'll first calculate which radio button should be currently lit.

            curRadioButton = gCurrentPICT - 
                     kBaseResID + kFirstRadio;

If the button that was clicked in is not the current radio button, we've got some work to do.

            if ( curRadioButton != itemHit )
            {

First, we'll turn off the current radio button.

               GetDialogItem( dialog, curRadioButton, &itemType,
                     &itemHandle, &itemRect );
               FlipControl( (ControlHandle)itemHandle );

Next, we'll turn on the radio button that was just clicked. Remember, always turn off a radio button before you turn on a new one.

               GetDialogItem( dialog, itemHit, &itemType,
                     &itemHandle, &itemRect );
               FlipControl( (ControlHandle)itemHandle );

Next, update curRadioButton to reflect the newly clicked radio button.

               curRadioButton = itemHit;

Next we check to see if the current PICT is still up to date.

               if ( gCurrentPICT != curRadioButton +
                     kBaseResID - kFirstRadio )
               {

If not, we'll set it to its new value, then call SwitchPICT() to update the My Pet Fred window.

                  gCurrentPICT = curRadioButton +
                        kBaseResID - kFirstRadio;
                  SwitchPICT();
               }
            }
            break;
      }
   }
}

HandleMouseDown() works much the same as always.

/********** HandleMouseDown **********/

void   HandleMouseDown( EventRecord *eventPtr )
{
   WindowPtr      window;
   short          thePart;
   long           menuChoice;
   MenuHandle     menu;

First, we call FindWindow() to find out what window the mouseDown was in and where in the window the mouseDown occurred.

   thePart = FindWindow( eventPtr->where, &window );

If the mouseDown was in the menu bar, pass the menu selection on to HandleMenuChoice(). A mouseDown in inSysWindow gets passed on to SystemClick().

   switch ( thePart )
   {
      case inMenuBar:
         menuChoice = MenuSelect( eventPtr->where );
         HandleMenuChoice( menuChoice );
         break;
      case inSysWindow : 
         SystemClick( eventPtr, window );
         break;

A mouseDown inContent causes a call to SelectWindow() to bring the clicked-in window to the front.

      case inContent:
         SelectWindow( window );
         break;

Note that this line will get executed even if the mouse click was in the modeless dialog, but only if the modeless dialog was in the back when it was clicked in. Want to prove this? Try adding this line after the call of SelectWindow() but before the break:

if ( window == gSettingsDLOG ) SysBeep( 20 );

If the mouseDown was inDrag, call DragWindow() to drag the window around on the screen.

      case inDrag : 
         DragWindow( window, eventPtr->where, 
               &qd.screenBits.bounds );
         break;

This next case is interesting. If the mouseDown was inGoAway, call TrackGoAway() to ensure that the mouse button was released inside the close box.

      case inGoAway:
         if ( TrackGoAway( window, eventPtr->where ) )

If so, verify that the click was in the modeless dialog's close box (the only window with a close box, by the way).

            if ( window == gSettingsDLOG )
            {

If so, make the modeless dialog window invisible. Why not close the window? We want to keep the dialog around so if the user brings it back up, it will have the same settings and will be in the same position, without us having to keep track of all that stuff.

               HideWindow( window );

Once the window is hidden, we'll enable the File menu's Settings item.

               menu = GetMenuHandle( mFile );
               EnableItem( menu, iSettings );
            }
         break;
   }
}

HandleMenuChoice() does what it always has, dispatching menu selections to the Apple and File menus.

/********** HandleMenuChoice **********/

void   HandleMenuChoice( long menuChoice )
{
   short      menu;
   short      item;
   
   if ( menuChoice != 0 )
   {
      menu = HiWord( menuChoice );
      item = LoWord( menuChoice );
      
      switch ( menu )
      {
         case mApple:
            HandleAppleChoice( item );
            break;
         case mFile:
            HandleFileChoice( item );
            break;
      }
      HiliteMenu( 0 );
   }
}

HandleAppleChoice() puts up an alert if the first item on the Apple menu was selected. An alert is like a dialog, but is not interactive (other than dismissing it). The alert comes up and is then dismissed, usually by a button click. Read the section on alerts in Inside Macintosh: Macintosh Toolbox Essentials. Alerts are not complicated.

/********** HandleAppleChoice **********/

void   HandleAppleChoice( short item )
{
   MenuHandle   appleMenu;
   Str255       accName;
   short        accNumber;
   
   switch ( item )
   {
      case iAbout:
         NoteAlert( kAboutALRTid, NULL );
         break;
      default:
         appleMenu = GetMenuHandle( mApple );
         GetMenuItemText( appleMenu, item, accName );
         accNumber = OpenDeskAcc( accName );
         break;
   }
}

HandleFileChoice() handles the two items in the File menu.

/********** HandleFileChoice **********/

void   HandleFileChoice( short item )
{

If Settings was selected, we first check to see if the modeless dialog was created yet. If not, we create it by calling CreateDialog().

   switch ( item )
   {
      case iSettings:
         if ( gSettingsDLOG == NULL )
            CreateDialog();

If the dialog does exist, we make it visible and bring it to the front. How does the Settings... item get dimmed, you might ask. When the dialog is brought to the front, an activateEvt gets posted. The activateEvt handling code takes care of that.

         else
         {
            ShowWindow( gSettingsDLOG );
            SelectWindow( gSettingsDLOG );
         }
         break;

As always, when Quit is selected, we set gDone to true and drop out of the program.

      case iQuit:
         gDone = true;
         break;
   }
}

Since events for the modeless dialog are handled elsewhere, the only update event this code will see is for the My Pet Fred window.

/********** DoUpdate **********/

void   DoUpdate( EventRecord *eventPtr )
{
   PicHandle   pic;
   WindowPtr   window;
   Rect            r;

We'll retrieve the WindowPtr from eventPtr->message and load the current Fred PICT using LoadPICT.

   window = (WindowPtr)eventPtr->message;
   
   pic = LoadPICT( gCurrentPICT );

Next, we make the window the current port, call BeginUpdate(), then draw the newest version of Fred and call EndUpdate().

   SetPort( window );
   
   BeginUpdate( window );
   
   r = window->portRect;
   DrawPicture( pic, &r );
   
   EndUpdate( window );
}

Just as we'd do with a modal dialog, we load the modeless DLOG resource by calling GetNewDialog().

/********** CreateDialog **********/

void   CreateDialog( void )
{
   short      itemType;
   Handle      itemHandle;
   Rect         itemRect;
   short      curRadioButton;

   gSettingsDLOG = GetNewDialog( kDialogResID, 
         NULL, kMoveToFront );

If the DLOG couldn't be loaded, beep once then exit.

   if ( gSettingsDLOG == NULL )
   {
      SysBeep( 10 );   /* Couldn't load the DLOG resource!!! */
      ExitToShell();
   }

Once the DLOG is loaded, make the dialog window visible and the current port.

   ShowWindow( gSettingsDLOG );
   SetPort( gSettingsDLOG );

Next, turn the current radio button on.

   curRadioButton = gCurrentPICT - kBaseResID + kFirstRadio;
   GetDialogItem( gSettingsDLOG, curRadioButton, &itemType,
            &itemHandle, &itemRect );
   SetControlValue( (ControlHandle)itemHandle, kOn );
}

FlipControl() turns a radio button or check box that's on, off and one that's off, on.

/********** FlipControl **********/

void   FlipControl( ControlHandle control )
{
   SetControlValue( control, ! GetControlValue( control ) );
}

SwitchPICT() calls DisposeWindow() to free up the memory used by the specified window (this closes the window as well) and then calls CreateWindow() to create a My Pet Fred window that reflects or most current choice of pet.

/********** SwitchPICT **********/

void   SwitchPICT( void )
{
   DisposeWindow( gFredWindow );
   
   CreateWindow();
}

Running Modeless

Save your source code and you're ready to run. Select Run from the Project menu to compile and run your program. When the program runs, the My Pet Fred window will appear, showing PICT 128. Pull down the Apple menu and verify that the first item reads About Modeless. Select About Modeless. from the Apple menu and check out your About box. Are your changes all there? Click the OK button to dismiss the About box.

Hold down the mouse in the File menu and verify that the S command-key equivalent was added to the Settings item. Select Settings. The Settings dialog box should appear (Figure 12).

Figure 12. The Settings... modal dialog box.

Click the My Pet Fred window, bringing it to the front and sending the modeless dialog to the back. Notice that the radio buttons are dimmed when the dialog is no longer the front-most window (Figure 13).

Figure 13. The radio buttons are dimmed when the dialog box is not in front.

Now type the command-key equivalent S to bring the Settings dialog to the front again; the radio buttons should be enabled. Click the Elephant radio button. Notice that the My Pet Fred window changes appropriately, leaving the Settings window in front. Click a few more radio buttons. While you are at it, click the File menu. Notice that the Settings item has been dimmed. Though this doesn't help us much in this program, it's important to be able to disable and enable certain menu items when a modeless dialog is in front.

When you are satisfied with your pet selection, drag the Settings window to another part of your screen. Now click the close box. The Settings window disappears. Select Settings from the File menu. The Settings window reappears at the position it was in when it disappeared and with the same radio button settings.

Finally, type the command-key equivalent Q to exit the program.

Till Next Time...

Well, that's about it for modeless dialogs. Be sure to read the Inside Macintosh: Macintosh Toolbox Essentials chapters that pertain to dialogs and alerts. In a future column we'll spend some time with a filterProc, a procedure that you provide to the Toolbo, and that the Toolbox calls for you. The filterProc allows you to control how the user interact with a dialog box, including what text the user can type. You can read about filterProc in Inside Macintosh: Macintosh Toolbox Essentials in the description of the Dialog Manager routine ModalDialog().

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Vivaldi 2.6.1566.49 - An advanced browse...
Vivaldi is a browser for our friends. We live in our browsers. Choose one that has the features you need, a style that fits and values you can stand by. From the look and feel, to how you interact... Read more
Capo 3.7.4 - Slow down and learn to play...
Capo lets you slow down your favorite songs so you can hear the notes and learn how they are played. With Capo, you can quickly tab out your songs atop a highly-detailed OpenCL-powered spectrogram... Read more
Daylite 6.7.3.1 - Dynamic business organ...
Daylite helps businesses organize themselves with tools such as shared calendars, contacts, tasks, projects, notes, and more. Enable easy collaboration with features such as task and project... Read more
Firefox 68.0.1 - Fast, safe Web browser.
Firefox offers a fast, safe Web browsing experience. Browse quickly, securely, and effortlessly. With its industry-leading features, Firefox is the choice of Web development professionals and casual... Read more
Dashlane 6.1927.0 - Password manager and...
Dashlane is an award-winning service that revolutionizes the online experience by replacing the drudgery of everyday transactional processes with convenient, automated simplicity - in other words,... Read more
BetterTouchTool 3.153 - Customize multi-...
BetterTouchTool adds many new, fully customizable gestures to the Magic Mouse, Multi-Touch MacBook trackpad, and Magic Trackpad. These gestures are customizable: Magic Mouse: Pinch in / out (zoom)... Read more
calibre 3.46.0 - Complete e-book library...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital librarian... Read more
DEVONthink Pro 3.0beta4 - Knowledge base...
DEVONthink Pro is your essential assistant for today's world, where almost everything is digital. From shopping receipts to important research papers, your life often fills your hard drive in the... Read more
Adobe Creative Cloud 4.9.0.512 - Access...
Adobe Creative Cloud costs $20.99/month for a single app, or $52.99/month for the entire suite. Introducing Adobe Creative Cloud desktop applications, including Adobe Photoshop CC and Illustrator CC... Read more
Chromium 75.0.3770.142 - Fast and stable...
Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all Internet users to experience the web. Version 75.0.3770.142: Release notes were... Read more

Latest Forum Discussions

See All

Void Tyrant guide - Tips and tricks for...
Void Tyrant continues to get a lot of play in these parts. Probably because the game is just so deep and varied. The next stop on our guide series for Void Tyrant is class-specific guides. First up is the Knight, as it’s the first class anyone has... | Read more »
Summon beasts and battle evil in epic re...
Imagine a tale of conlict between factions of good and evil, where rogueish heroes summon beasts to aid them in them in warfare and courageously battle dragons over fields of scorched earth and brimstone - that's exactly the essence of epic fantasy... | Read more »
Upcoming visual novel Arranged shines a...
If you’re in the market for a new type of visual novel designed to inform and make you think deeply about its subject matter, then Arranged by Kabuk Games could be exactly what you’re looking for. It’s a wholly unique take on marital traditions in... | Read more »
TEPPEN guide - The three best decks in T...
TEPPEN’s unique take on the collectible card game genre is exciting. It’s just over a week old, but that isn’t stopping lots of folks from speculating about the long-term viability of the game, as well as changes and additions that will happen over... | Read more »
Intergalactic puzzler Silly Memory serve...
Recently released matching puzzler Silly Memory is helping its fans with their intergalactic journeys this month with some very special offers on in-app purchases. In case you missed it, Silly Memory is the debut title of French based indie... | Read more »
TEPPEN guide - Tips and tricks for new p...
TEPPEN is a wild game that nobody asked for, but I’m sure glad it exists. Who would’ve thought that a CCG featuring Capcom characters could be so cool and weird? In case you’re not completely sure what TEPPEN is, make sure to check out our review... | Read more »
Dr. Mario World guide - Other games that...
We now live in a post-Dr. Mario World world, and I gotta say, things don’t feel too different. Nintendo continues to squirt out bad games on phones, causing all but the most stalwart fans of mobile games to question why they even bother... | Read more »
Strategy RPG Brown Dust introduces its b...
Epic turn-based RPG Brown Dust is set to turn 500 days old next week, and to celebrate, Neowiz has just unveiled its biggest and most exciting update yet, offering a host of new rewards, increased gacha rates, and a brand new feature that will... | Read more »
Dr. Mario World is yet another disappoin...
As soon as I booted up Dr. Mario World, I knew I wasn’t going to have fun with it. Nintendo’s record on phones thus far has been pretty spotty, with things trending downward as of late. [Read more] | Read more »
Retro Space Shooter P.3 is now available...
Shoot-em-ups tend to be a dime a dozen on the App Store, but every so often you come across one gem that aims to shake up the genre in a unique way. Developer Devjgame’s P.3 is the latest game seeking to do so this, working as a love letter to the... | Read more »

Price Scanner via MacPrices.net

Weekend Deal: 2018 13″ MacBook Airs starting...
B&H Photo has clearance 2018 13″ MacBook Airs available starting at only $999 with all models now available for $200 off Apple’s original MSRP. Overnight shipping, or expedited shipping, is free... Read more
Apple has clearance 10.5″ iPad Pros available...
Apple has Certified Refurbished 2017 10.5″ iPad Pros available starting at $469. An Apple one-year warranty is included with each iPad, outer shells are new, and shipping is free: – 64GB 10″ iPad Pro... Read more
Apple restocks refurbished iPad mini 4 models...
Apple has restocked Certified Refurbished 32GB iPad mini 4 WiFi models for $229 shipped. That’s $70 off original MSRP for the iPad mini 4. Space Gray, Silver, and Gold colors are available. Read more
Apple, Yet Again, Is Missing An Ultraportable...
EDITORIAL: 07.19.19 Prior to the decision made by Apple earlier this month to retire the thin and light MacBook model with a 12-inch retina display, the Cupertino, California-based company offered,... Read more
Verizon is offering a 50% discount on iPhone...
Verizon is offering 50% discounts on Apple iPhone 8 and iPhone 8 Plus models though July 24th, plus save 50% on activation fees. New line required. The fine print: “New device payment & new... Read more
Get a new 21″ iMac for under $1000 today at t...
B&H Photo has new 21″ Apple iMacs on sale for up to $100 off MSRP with models available starting at $999. These are the same iMacs offered by Apple in their retail and online stores. Shipping is... Read more
Clearance 2017 15″ 2.8GHz Touch Bar MacBook P...
Apple has Certified Refurbished 2017 15″ 2.8GHz Space Gray Touch Bar MacBook Pros available for $1809. Apple’s refurbished price is currently the lowest available for a 15″ MacBook Pro. An standard... Read more
Clearance 12″ 1.2GHz MacBook on sale for $899...
Focus Camera has clearance 12″ 1.2GHz Space Gray MacBooks available for $899.99 shipped. That’s $400 off Apple’s original MSRP. Focus charges sales tax for NY & NJ residents only. Read more
Get a new 2019 13″ 2.4GHz 4-Core MacBook Pro...
B&H Photo has new 2019 13″ 2.4GHz MacBook Pros on sale for up to $150 off Apple’s MSRP. Overnight shipping is free to many addresses in the US: – 2019 13″ 2.4GHz/256GB 6-Core MacBook Pro Silver... Read more
AirPods with Wireless Charging Case now on sa...
Amazon has extended their Prime Day savings on Apple AirPods by offering AirPods with the Wireless Charging case for $169.99. That’s $30 off Apple’s MSRP, and it’s the cheapest price available for... Read more

Jobs Board

Geek Squad *Apple* Master Consultation Agen...
**702908BR** **Job Title:** Geek Squad Apple Master Consultation Agent **Job Category:** Services/Installation/Repair **Location Number:** 000360-Williston-Store Read more
Best Buy *Apple* Computing Master - Best Bu...
**711023BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Location Number:** 000012-St Cloud-Store **Job Description:** **What does a Read more
*Apple* Systems Architect/Engineer, Vice Pre...
…its vision to be the world's most trusted financial group. **Summary:** Apple Systems Architect/Engineer with strong knowledge of products and services related to Read more
Best Buy *Apple* Computing Master - Best Bu...
**696259BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Store Associates **Location Number:** 001076-Temecula-Store **Job Description:** The Read more
Business Development Manager, *Apple* Globa...
Business Development Manager, Apple Global Tampa, FL, US Requisition Number:73805 As a Global Apple Business Development Manager at Insight, you proactively Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.