                             Managing the Desktop

                                    "Oh, London is a fine town, a very
                                     famous city where all the streets
                                     are paved with gold and all the
                                     maidens pretty"
                                 George Coleman the younger, 1762-1836
                                             The Heir at Law

Introduction

          A desktop application typically has a pull-down menu, a status
     bar, and a main area (in between) where windows are displayed. One
     of the best known desktop applications (for us programmer types) is
     Borland's integrated development environment for C and Pascal.

          The unit GOLDDESK provides a set of elegant tools to make it
     easy for you to create a sophisticated desktop application. We
     don't need no stinkin' Turbo Vision!! (Just kidding David.)


     Figure 12.1
     A Typical
     Desktop


Ingredients of a Desktop

          A full desktop includes a pull-down menu, a status bar and one
     or more windows. Gold also allows you to define a partial desktop
     which has either a menu or a status bar. Having built the
     individual desktop components, the desktop is constructed using the
     DeskAssignxxx functions.

The Pull-down Menu

          The desktop utilizes a standard Gold pull-down menu. If you
     are not familiar with these menus, read Chapter 11.

          Having constructed a menu, it can be added to the desktop with
     the procedure DeskAssignMainMenu. This procedure is passed the
     MenuBar variable which defines the menu. For example:

          DeskAssignMainMenu(MainMenu);

     That's all there is to it.

          If, for some strange reason, you want to continue using the
     desktop, but want the menu removed, just call the procedure
     DeskRemoveMainMenu.

The Status Bar

          At status bar is a single row of options positioned on the
     bottom line of the display. It's like a menu bar without any
     popups. The code used to create a status bar is exactly the same as
     the code to create a menu bar.

          If you want to add a status bar to the desktop, create a
     variable of type MenuBar, initialize the variable with InitBar, and
     use the procedure DeskAssignStatusBar to add the status bar to the
     desktop. The following code fragment illustrates this approach:

          var StatusBar: bar;
          begin
             InitBar(StatusBar);
             BarAddItem(StatusBar,'~F1~ Help',1001,315,315,'',nil);
             BarAddItem(StatusBar,'~Alt+X~ Exit the demo',999,301,
                                                301,'',nil);
             DeskAssignStatusBar(Statusbar);
             ...
          end;

          The status bar can be removed by calling the procedure
     DeskRemoveStatusBar.

The Background

          Every desktop has a background; this is the main area of the
     screen which extends from the pull-down menu to the status bar.
     Normally, this area is filled with a single character to provide a
     plain wallpaper.

          You can change the default appearance of the background by
     assigning a different character to the variable DeskVars.BackChar.
     Also, you can change the background color by using the GoldSetColor
     procedure and assigning a new value to the DeskBack element. For
     example:

          DeskVars.BackChar := chr(179)
          GoldSetColor(DeskBack,WhiteonGreen);

          Gold provides a way for you to create more sophisticated
     backgrounds. All you have to do is create a procedure following
     some specific rules, and then call the DeskAssignPaintProc
     procedure to instruct Gold to call your procedure every time the
     background needs to be repainted.

          For a procedure to be eligible as a paint procedure it must
     adhere to the following rules:

          The procedure must be declared as a far procedure at the root
          level. Refer to section Understanding Hooks in Chapter 3 for
          further information.

          The procedure must be declared with no parameters.

     The following procedure declaration follows these rules:

          {$F+}
          procedure CustomBgnd;
          begin
             {some code}
          end; {CustomBgnd}
          {$F-}

          The following procedure is then called to instruct Gold to
     call your procedure every time the desktop needs painting:

     DeskAssignPaintProc (Hook:KeyPressedHook);

          Instructs Gold to call the specified procedure every time Gold
     needs to draw the background.

          If, subsequently, you want to remove the custom paint
     procedure, execute the command DeskRemovePaintProc.

          Run the demo program DEMDESK7.PAS to see a custom paint
     procedure in action.

Responding to User Input

          A desktop is simply a standard way to garner user input. The
     user can select items from the menu, click on the status, or press
     hotkeys. The application must then respond to this input and act
     accordingly. So much for the theory of desktops.

          Gold takes care of managing the user input. You don't need to
     explicitly invoke the menu or status bar; Gold takes care of it for
     you. If the keystroke or mouse activity is directed towards the
     pull-down menu, the keystroke will be passed to the menu for
     processing. Similarly, status bar-related keystrokes will be passed
     to the status bar for processing. If the user clicks on the top
     window (if there is a window visible), the keystroke is passed to
     the window for processing. Finally, if the user clicks on a window
     without focus, the window is moved to the top, i.e. given focus.

Assigning an Action Procedure

          Every item in the menu and status bar has an ID -- you
     assigned the ID as one of the arguments in BarAddItem and
     PopAddItem. When the user selects an item from the desktop, Gold
     passes the ID of the selected item to a user-defined action
     procedure.

          All you have to do is create a procedure following some
     specific rules, and then call the DeskAssignActionProc procedure to
     instruct Gold to call your procedure every time the user makes a
     selection.

          For a procedure to be eligible as an action procedure it must
     adhere to the following rules:

          The procedure must be declared as a far procedure at the root
          level. Refer to the section Understanding Hooks in Chapter 3
          for further information.

          The procedure must be declared with two passed parameters. The
          first parameter is an integer and a variable parameter of type
          DAction.

     The following procedure declaration follows these rules:

          {$F+}
          procedure MyActionProc(Choice:integer;
                    var Action:DAction);
          {}
          begin
             case Choice of
                .....
             end;
          end; { MyActionProc }
          {$F-}

          The following procedure is then called to instruct Gold to
     call your procedure after each desktop input:

     DeskAssignActionProc(Aproc:DeskActionProc);

          Instructs Gold to call the specified procedure every time the
     user makes a status bar or menu selection from the desktop.

Writing the Action procedure

          The first parameter passed to the action procedure is the ID
     of the item selected from the menu or status bar. The body of the
     action procedure usually takes the form of a case statement which
     branches to another part of the program based on the user
     selection.

          The second parameter passed to the action procedure is a
     variable parameter of type DAction, which is an enumerated type
     declared in GOLDDESK as follows:

          DAction = (DFinished, Descaped, DRepaint, DNone, DCloseTop,
                                   DCloseAll,DStop1..DStop99);

          By updating this variable with a value (other than DNone) you
     can instruct Gold to take some form of action. For example, if you
     update the variable with a value of DRepaint, Gold will
     automatically repaint the menu, status bar, background, and every
     window. The following table elaborates on the actions taken buy
     Gold for each of the return values:

     DAction Value   Meaning

     DFinished       Ends the desktop session and returns the code
                     DFinished.
     DEscaped        Ends the desktop session and returns the code
                     DEscaped.
     DRepaint        Forces a repaint of the desktop and all the
                     windows.
     DNone           No action is taken - this is the default value
                     when the action procedure doesn't modify the
                     variable.
     DCloseTop       Forces the top window to close.
     DCloseAll       Forces all the windows to close.
     DStop1..DStop99 Ends the desktop session and returns the
                     appropriate stop code.

Starting a Desktop Session

          Having assigned the menu, status bar and action procedure, you
     are ready to launch the desktop and wait for user input. All you
     have to do is call the function DeskProcessInput. This function
     returns the DAction which caused the session to close (see the
     above table).

A Simple Desktop Example

          Listed below is the entire source code for DEMDESK1.PAS which
     implements a minimalist desktop. Although the desktop doesn't have
     too many features, it does illustrate all the important principles
     of creating a desktop application.

          var MainMenu: Bar;
              SubMenu: PopUp;
              Action : dAction;

          procedure DefineSubMenu;
          {}
          begin
             InitPopUp(SubMenu);
             with SubMenu do
             begin
                PopUpAddItem(SubMenu,'~A~bout',101,65,
                             'Show version...ation',nil);
                PopUpAddItem(SubMenu,'E~x~it',999,88,
                             '~Exit~ this little demo',nil);
             end;
          end; { DefineSubMenu }

          procedure DefineMainMenu;
          {}
          begin
             InitBar(MainMenu);
             BarAddItem(MainMenu,'~C~hoices',100,67,302,
                        'Select one of two menu options',
                         @SubMenu);
             MainMenu.Style := 2;
          end; { DefineMainMenu }

          procedure DisposeMenus;
          {}
          begin
             DestroyBar(MainMenu);
             DestroyPopUp(SubMenu);
          end; { DisposeMenus }

          {$F+}
          procedure MyActionProc(Choice:integer;var Action:DAction);
          {}
          begin
             case Choice of
               101: PromptOK(' The Gold Desktop ',
                    '^Copyright 1995 TechnoJock Software, Inc.|'
                    +'^All Rights Reserved');
               999: Action := DFinished;
             end;
          end; { MyActionProc }
          {$F-}

          begin
             DefineSubMenu;
             DefineMainMenu;
             DeskAssignMainMenu(MainMenu);
             DeskAssignActionProc(MyActionProc);
             MouseShow(true);
             CursorOff;
             Action := DeskProcessInput;
             CursorOn;
             MouseShow(false);
             DisposeMenus;
          end.

Launching Desktop Windows

          A desktop is really a standard framework for launching windows
     or applets. Many of the Gold units include procedures which are
     exclusively designed for launching windows onto the desktop. In
     this section you will learn how to populate the desktop with
     windows.

Understanding Modal and Non-Modal Windows

          A modal window is a window which disables all other aspects of
     the desktop and forces all input to the window. When a modal window
     is active, the user cannot switch to another window, nor select
     items from the menu or the status bar. Modal windows are ideal for
     error messages or dialogs which require immediate input, e.g. an
     open file dialog.

          Non-modal windows, on the other hand, can lose focus and form
     the heart of a true desktop application. Examples of non-modal
     windows include a file browser, calculator and calendar. Borland
     Pascal uses non-modal windows for the edit windows where the source
     code is managed.

Launch-able Gold Components

          Many of the Gold units include primary procedures which begin
     with the word Run, e.g. RunCalculator, RunBrowse, etc. When the
     object can also be used on a desktop there is a related Launch
     function, e.g. LaunchCalculator, LaunchBrowse.

          Listed below is a summary of all the Gold procedures which can
     be used to launch non-modal windows onto the desktop.

     LaunchCalendar(StartDate:Dates;Tit:string): byte;

          Adds a month-at-a-glance window to the desktop. The arguments
     passed to this procedure are the same as for RunCalendar discussed
     in Chapter 7. The function returns the number (or handle) of the
     newly created window.

     LaunchCalculator(Tit:string): byte;

          Adds a push-button calculator to the desktop. The arguments
     passed to this procedure are the same as for RunCalculator
     discussed in Chapter 8. The function returns the number (or handle)
     of the newly created window.

     LaunchBrowse(var ListDetails: ListCfg;Tit:StrScreen;
                                         CloseProc:ListCloseProc):byte;

          Launches a browser for viewing the contents of an array or
     linked list. The arguments passed to this procedure are discussed
     in Chapter 14. The function returns the number (or handle) of the
     newly created window.

     LaunchBrowseFile(Fname:PathStr;Tit:StrScreen):byte;

          Launches a browser for viewing the contents of a file. The
     arguments passed to this procedure are the same as for
     RunBrowseFile discussed in Chapter 14. The function returns the
     number (or handle) of the newly created window.

     LaunchList(var ListDetails: ListCfg;Tit:StrScreen;
                                       CloseProc:ListCloseProc): byte;

     Adds a list window to the desktop. The arguments passed to this
     procedure are discussed in Chapter 14. The function returns the
     number (or handle) of the newly created window.

     LaunchGrid(var ListDetails: ListCfg;Tit:StrScreen):byte;

          Adds a list window to the desktop. The arguments passed to
     this procedure are the same as for RunGrid discussed in Chapter 14.
     The function returns the number (or handle) of the newly created
     window.

     LaunchMemo(var MemoDetails: MemoCfg;Tit:StrScreen;
                                      CloseProc:MemoCloseProc): byte;

          Adds a memo window to the desktop. The arguments passed to
     this procedure are discussed in Chapter 15. The function returns
     the number (or handle) of the newly created window.

     LaunchFormInit(X1,Y1,X2,Y2,style:byte;CloseProc:FormCloseProc):byte

     LaunchForm(StartField:byte);

          These functions are used to create non-modal IO forms, and are
     discussed in more detail in Chapter 16.


          The demo files DEMDESK2.PAS to DEMDESK5.PAS illustrate many of
     the above launch functions.

Positioning New Windows

          Most desktop applications place a new window down and to the
     right of the (old) top window. Complications arise when the top
     window is located near the right or the bottom of the desktop where
     there is insufficient room for the new window.

          Gold offers the following two functions to help you calculate
     the dimensions of a new window:

     DeskNextWinDim(var X1,Y1,X2,Y2: shortint; MinWidth,MinDepth:
                                                           longint);

          Updates the first four parameters with a set of coordinates
     which can be used to size a new window that is about to be added to
     the desktop.

     DeskNextWinCoords(var TLX,TLY: byte);

          Updates the passed parameters with the appropriate coordinates
     (of the top left of the window) which can be assigned to a new
     window that is about to be added to the desktop.

Displaying Modal Windows

          There is nothing to stop you from using modal windows on the
     desktop. For example, you might use PromptStr to ask the user to
     input a password. In fact, most desktop applications use both modal
     and non-modal windows. In Borland Pascal, for example, a modal
     window is used to prompt for confirmation during a multiple search
     and replace operation.

          However, always remember that a modal window must be removed
     before the user can access the menu, status bar, or other windows.
     So use modal windows sparingly.

Adding a Traditional "Window" menu

          If you like to conform to Microsoft Windows and DOS desktop
     applications standards, you should consider adding Window and Help
     items to the pull-down menu. The contents of the Help menu is
     application specific, but the Window menu often displays a standard
     set of items (see Figure 12.2).

          You create a window pop-up just like any other pop-up -- by
     adding items to a PopUpMenu, and by responding to the menu choices
     in the action procedure. The good news is that you do not have to
     write your own tiling and cascading procedures. GOLDDESK includes
     the following procedures to help with window management:


     Figure 12.2
     A Standard
     Window Menu


     DeskCloseAllWindows: boolean;

          Closes all the windows on the desktop. If the function returns
     FALSE, one or more window could not be closed. This might occur,
     for example, when an IO form is closed but the value entered into a
     field is not valid.

     DeskNextWin;

     Changes focus to the next window in the list.

     DeskPrevWin;

     Changes focus to the previous window in the list.

     DeskStretchTopWin: boolean;

          Lets the user manually (without the mouse) stretch the window.
     A FALSE is returned if the window cannot be stretched.

     DeskZoomTopWin: boolean;

          Makes the top window the size of the desktop. A FALSE is
     returned if the window cannot be zoomed.

     DeskMoveTopWin: boolean;

          Lets the user manually (without the mouse) move the top
     window. A FALSE is returned if the window cannot be moved.

     DeskCascade;

          Arranges all the windows, cascading from the top left of the
     desktop.

     DeskTile: boolean;

          Tiles all the windows on the desktop. A FALSE is returned if
     the one or more windows cannot be tiled.

     DeskWinList;

          Displays a window listing all the files and allows the user to
     change focus.

          You can copy and paste the window related code from the demo
     file DEMDESK6.PAS into your own desktop application.

Implementing a Hind Hook

          A desktop hind hook is a procedure which is called every time
     an input has been processed by the desktop. A hind hook can be used
     to change the grayed status of menu items based on the window with
     focus, for example.

          All you have to do is create a procedure following some
     specific rules, and call the DeskAssignHindHook procedure to
     instruct Gold to call your procedure every time some input has been
     processed.

          For a procedure to be eligible as a desktop hind hook, it must
     adhere to the following rules:

          The procedure must be declared as a far procedure at the root
          level. Refer to the section Understanding Hooks in Chapter 3
          for further information.

          The procedure must be declared with no parameters.

     The following procedure declaration follows these rules:

          {$F+}
          procedure CustomHook;
          begin
             {some code}
          end; {CustomHook}
          {$F-}

          The following procedure is then called to instruct Gold to
     call your procedure every time the desktop needs painting:

     DeskAssignHindHook (Hook:KeyPressedHook);

          Instructs Gold to call the specified procedure every time Gold
     has processed an input to the desktop..

          If, subsequently, you want to remove the hind hook procedure,
     execute the command DeskRemoveHindHook.

          Run the demo program DEMOVIEW.PAS to see a custom hind hook in
     action.
