home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / os2 / mdi / mdi.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-12-15  |  79.7 KB  |  2,124 lines

  1. // Includes
  2. // --------
  3.    #define  INCL_WIN
  4.    #define  INCL_DOS
  5.  
  6.    #include <stdio.h>
  7.    #include <stdlib.h>
  8.    #include <string.h>
  9.    #include <os2.h>
  10.  
  11.    #include "MDI.H"
  12.    #include "MDI.RH"
  13.  
  14.    #include "AllocMem.H"
  15.    #include "TellUser.H"
  16.  
  17.  
  18. // Constants
  19. // ---------
  20.    #define MAX_STRING    256           // Maximum string size
  21.    #define INSERT_TITLE    1           // Insert title command code
  22.    #define REMOVE_TITLE    2           // Remove title command code
  23.    #define AWP_TILED       1           // Arrange tiled flag
  24.    #define AWP_CASCADED    2           // Arrange cascaded flag
  25.    #define CASC_EDGE_NUM   2           // Cascaded arrange edge
  26.    #define CASC_EDGE_DENOM 3           // Cascaded arrange edge
  27.    #define ICON_PARK_NUM   5           // Icon park
  28.    #define ICON_PARK_DENOM 3           // Icon park
  29.  
  30.    const USHORT cbExtraData            // Extra window storage
  31.               = (2 * sizeof (PVOID));
  32.  
  33.  
  34. // Desktop Creation Parameters
  35. // ---------------------------
  36.    typedef struct _DESKTOPCREATEPARMS
  37.      {USHORT cbDeskData;               // Size of user desktop data
  38.       PCHAR pszExit;                   // Exit menu item text
  39.       PCHAR pszWindow;                 // Window menu item text
  40.      } DESKTOPCREATEPARMS;
  41.  
  42.    typedef DESKTOPCREATEPARMS *PDESKTOPCREATEPARMS;
  43.  
  44.  
  45. // Document Creation Parameters
  46. // ----------------------------
  47.    typedef struct _DOCUMENTCREATEPARMS
  48.      {USHORT cbDocData;                // Size of user document data
  49.       USHORT cbInstData;               // Size of user instance data
  50.       ULONG flCreateFlags;             // Copy of frame creation flags
  51.       HMODULE hmodResources;           // Copy of frame resources
  52.       USHORT idResources;              // Copy of frame resources id
  53.       PFNWP pfnUser;                   // User window procedure
  54.      } DOCUMENTCREATEPARMS;
  55.  
  56.    typedef DOCUMENTCREATEPARMS *PDOCUMENTCREATEPARMS;
  57.  
  58.  
  59. // Desktop Intra-instance Data
  60. // ---------------------------
  61.    typedef struct _INSTDESKTOP
  62.      {HWND hwndDocument;               // Active document window
  63.       HWND hwndDesktop;                // Desktop window
  64.       HWND hwndParent;                 // Its frame's parent
  65.       HWND hwndFrame;                  // Its frame
  66.       HWND hwndMenu;                   // Its menu
  67.       HACCEL haccel;                   // Its accelerator table
  68.       CHAR szTitle[MAX_STRING];        // Its title
  69.       SWP swp;                         // Its position
  70.       MENUITEM miWindow;               // Window menu
  71.       MENUITEM miSysMenu;              // Maximized child system menu
  72.       PVOID pvDeskData;                // -->user desktop data
  73.       BOOL fTileAlways;                // TRUE == Tile always
  74.       BOOL fCascadeAlways;             // TRUE == Cascade always
  75.       PCHAR pszExit;                   // Exit menu item text
  76.       PCHAR pszWindow;                 // Window menu item text
  77.       USHORT idDesktop;                // Desktop id
  78.      } INSTDESKTOP;
  79.  
  80.    typedef INSTDESKTOP *PINSTDESKTOP;
  81.  
  82.  
  83. // Document Intra-instance Data
  84. // ----------------------------
  85.    typedef struct _INSTDOCUMENT
  86.      {HWND hwndDocument;               // Document window
  87.       HWND hwndDesktop;                // Desktop window
  88.       HWND hwndFrame;                  // Its frame
  89.       HWND hwndSysMenu;                // Its system menu
  90.       HWND hwndTitleBar;               // Its title bar
  91.       HWND hwndMinMax;                 // Its min/max buttons
  92.       HWND hwndVScroll;                // Its vertical scroll bar
  93.       HWND hwndHScroll;                // Its horizontal scroll bar
  94.       USHORT idFrame;                  // Its frame id
  95.       HACCEL haccel;                   // Its accelerator table
  96.       CHAR szTitle[MAX_STRING];        // Its title
  97.       SHORT iTitle;                    // Its title index
  98.       SWP swp;                         // Its position
  99.       USHORT usState;                  // Its state
  100.       USHORT cbDocData;                // Size of user document data
  101.       USHORT cbInstData;               // Size of user instance data
  102.       PFNWP pfnUser;                   // User window procedure
  103.       ULONG flCreateFlags;             // Frame creation flags
  104.       HMODULE hmodResources;           // Frame resources
  105.       USHORT idResources;              // Frame resources id
  106.       USHORT idDesktop;                // Desktop id
  107.       PVOID pvDocData;                 // -->user document data
  108.       PVOID pvInstData;                // -->user instance data
  109.      } INSTDOCUMENT;
  110.  
  111.    typedef INSTDOCUMENT *PINSTDOCUMENT;
  112.  
  113.  
  114. // Function Declarations (see also MDI.H)
  115. // --------------------------------------
  116.    MRESULT       FAR      MDIDesktopClose            (PINSTDESKTOP);
  117.    MRESULT       FAR      MDIDesktopCommand          (PINSTDESKTOP, USHORT);
  118.    MRESULT       FAR      MDIDesktopCreate           (HWND, PDESKTOPCREATEPARMS);
  119.    MRESULT       FAR      MDIDesktopDestroy          (PINSTDESKTOP);
  120.    MRESULT       FAR      MDIDesktopPaint            (PINSTDESKTOP);
  121.    MRESULT       FAR      MDIDocumentActivate        (PINSTDOCUMENT, BOOL);
  122.    VOID          FAR      MDIDocumentArrange         (HWND, USHORT);
  123.    VOID          FAR      MDIDocumentArrangeCascaded (PRECTL, SHORT, PSWP);
  124.    VOID          FAR      MDIDocumentArrangeTiled    (PRECTL, SHORT, PSWP);
  125.    VOID          FAR      MDIDocumentClone           (PINSTDOCUMENT);
  126.    MRESULT       FAR      MDIDocumentClose           (PINSTDOCUMENT);
  127.    MRESULT       FAR      MDIDocumentCommand         (PINSTDOCUMENT, USHORT);
  128.    MRESULT       FAR      MDIDocumentCreate          (HWND, PDOCUMENTCREATEPARMS);
  129.    MRESULT       FAR      MDIDocumentDestroy         (PINSTDOCUMENT);
  130.    VOID          FAR      MDIDocumentHide            (PINSTDOCUMENT);
  131.    MRESULT       FAR      MDIDocumentMinMax          (PINSTDOCUMENT, PSWP);
  132.    MRESULT       FAR      MDIDocumentPaint           (PINSTDOCUMENT);
  133.    PINSTDOCUMENT FAR      MDIDocumentTitlesRebuild   (PINSTDOCUMENT, USHORT);
  134.    MRESULT       EXPENTRY MDIDocumentUnhideDlgProc   (HWND, USHORT, MPARAM, MPARAM);
  135.    VOID          EXPENTRY MDIInitialize              (HAB);
  136.    HACCEL        FAR      MDILoadAccelTable          (USHORT);
  137.    HWND          FAR      MDILoadDialog              (HWND, PFNWP, USHORT, PVOID);
  138.    HWND          FAR      MDILoadMenu                (HWND, USHORT);
  139.  
  140.  
  141. // Function: MDICreateDesktop - Create MDI Desktop
  142. // -----------------------------------------------
  143.    HWND FAR MDICreateDesktop (pfnUser, flFrameFlags, pszTitle, hmodResources,
  144.                               idResources, phwndClient, cbDeskData, idDesktop, pszExit, pszWindow)
  145.  
  146.       PFNWP pfnUser;                   // User window procedure
  147.       ULONG flFrameFlags;              // Frame creation flags
  148.       PCHAR pszTitle;                  // Desktop title
  149.       HMODULE hmodResources;           // Module that contains resources
  150.       USHORT idResources;              // Resources identifier
  151.       PHWND phwndClient;               // -->area to receive desktop handle
  152.       USHORT cbDeskData;               // Size of user desktop data
  153.       USHORT idDesktop;                // ID of desktop
  154.       PCHAR pszExit;                   // "Exit" menu text
  155.       PCHAR pszWindow;                 // Desired "Window" menu text
  156.  
  157.    // Define function data
  158.  
  159.      {HWND hwndFrame;                  // Frame window
  160.       FRAMECDATA fcdata;               // Frame creation parameters
  161.       DESKTOPCREATEPARMS deskcp;       // Desktop creation parameters
  162.       PINSTDESKTOP pinst;              // -->desktop instance data
  163.  
  164.    // Initialize frame creation parameters
  165.  
  166.       fcdata.cb = sizeof (fcdata);
  167.       fcdata.flCreateFlags = flFrameFlags;
  168.       fcdata.hmodResources = hmodResources;
  169.       fcdata.idResources = idResources;
  170.  
  171.    // Initialize desktop creation parameters
  172.  
  173.       deskcp.cbDeskData = cbDeskData;
  174.       deskcp.pszExit = pszExit;
  175.       deskcp.pszWindow = pszWindow;
  176.  
  177.    // Create desktop frame and client windows
  178.  
  179.       hwndFrame = WinCreateWindow (HWND_DESKTOP, WC_FRAME, pszTitle,
  180.         WS_VISIBLE, 0, 0, 0, 0, NULL, HWND_TOP, idDesktop, &fcdata, NULL);
  181.       if (hwndFrame == NULL) return NULL;
  182.       *phwndClient = WinCreateWindow (hwndFrame, MDI_DESKTOPCLASS, NULL,
  183.         WS_VISIBLE, 0, 0, 0, 0, hwndFrame, HWND_BOTTOM, FID_CLIENT, &deskcp, NULL);
  184.       if (*phwndClient == NULL) return NULL;
  185.  
  186.    // Subclass desktop window procedure
  187.  
  188.       if (pfnUser != NULL)
  189.          WinSubclassWindow (*phwndClient, pfnUser);
  190.  
  191.    // Tell user that desktop has been created
  192.  
  193.       pinst = (PINSTDESKTOP) WinQueryWindowULong (*phwndClient, QWL_USER + sizeof (PVOID));
  194.       WinSendMsg (*phwndClient, WM_CONTROL,
  195.         MPFROM2SHORT (pinst->idDesktop, MDI_NOTIFY_CREATE_DESKTOP),
  196.         (MPARAM) pinst->pvDeskData);
  197.  
  198.    // Return
  199.  
  200.       return hwndFrame;
  201.  
  202.      }
  203.  
  204.  
  205. // Function: MDICreateDocument - Create MDI Document
  206. // -------------------------------------------------
  207.    HWND FAR MDICreateDocument (pfnUser, hwndParent, flFrameFlags, pszTitle,
  208.                                hmodResources, idResources, phwndClient, cbDocData, cbInstData)
  209.  
  210.       PFNWP pfnUser;                   // User window procedure
  211.       HWND hwndParent;                 // Document parent
  212.       ULONG flFrameFlags;              // Frame creation flags
  213.       PCHAR pszTitle;                  // Document title
  214.       HMODULE hmodResources;           // Module that contains resources
  215.       USHORT idResources;              // Resources identifier
  216.       PHWND phwndClient;               // -->area to receive document handle
  217.       USHORT cbDocData;                // Size of user document data
  218.       USHORT cbInstData;               // Size of user document data
  219.  
  220.    // Define function data
  221.  
  222.      {HWND hwndFrame;                  // Frame window
  223.       FRAMECDATA fcdata;               // Frame creation parameters
  224.       DOCUMENTCREATEPARMS doccp;       // Document creation parameters
  225.       PINSTDOCUMENT pinst;             // -->document instance data
  226.  
  227.    // Initialize frame creation parameters
  228.  
  229.       fcdata.cb = sizeof (fcdata);
  230.       fcdata.flCreateFlags = flFrameFlags;
  231.       fcdata.hmodResources = hmodResources;
  232.       fcdata.idResources = idResources;
  233.  
  234.    // Initialize document creation parameters
  235.  
  236.       doccp.cbDocData = cbDocData;
  237.       doccp.cbInstData = cbInstData;
  238.       doccp.pfnUser = pfnUser;
  239.       doccp.flCreateFlags = flFrameFlags;
  240.       doccp.hmodResources = hmodResources;
  241.       doccp.idResources = idResources;
  242.  
  243.    // Create document frame and client windows
  244.  
  245.       hwndFrame = WinCreateWindow (hwndParent, WC_FRAME, pszTitle,
  246.         WS_VISIBLE, 0, 0, 0, 0, NULL, HWND_TOP, 1, &fcdata, NULL);
  247.       if (hwndFrame == NULL) return NULL;
  248.       *phwndClient = WinCreateWindow (hwndFrame, MDI_DOCUMENTCLASS, NULL,
  249.         WS_VISIBLE, 0, 0, 0, 0, hwndFrame, HWND_BOTTOM, FID_CLIENT, &doccp, NULL);
  250.       if (*phwndClient == NULL) return NULL;
  251.  
  252.    // Subclass document window procedure
  253.  
  254.       if (pfnUser != NULL)
  255.          WinSubclassWindow (*phwndClient, pfnUser);
  256.  
  257.    // Tell user that document has been created
  258.  
  259.       pinst = (PINSTDOCUMENT) WinQueryWindowULong (*phwndClient, QWL_USER + sizeof (PVOID));
  260.       WinSendMsg (*phwndClient, WM_CONTROL,
  261.         MPFROM2SHORT (pinst->idDesktop, MDI_NOTIFY_CREATE_DOCUMENT),
  262.         (MPARAM) pinst->pvInstData);
  263.  
  264.    // Return
  265.  
  266.       return hwndFrame;
  267.  
  268.      }
  269.  
  270.  
  271. // Function: MDIDesktopClose - Close MDI Desktop
  272. // ---------------------------------------------
  273.    MRESULT FAR MDIDesktopClose (pinst)
  274.  
  275.       PINSTDESKTOP pinst;              // -->desktop instance data
  276.  
  277.    // Post "quit"
  278.  
  279.      {WinPostMsg (NULL, WM_QUIT, NULL, NULL);
  280.  
  281.    // Destroy MDI desktop window
  282.  
  283.       WinDestroyWindow (pinst->hwndFrame);
  284.  
  285.    // Indicate close handled
  286.  
  287.       return (MRESULT) NULL;
  288.  
  289.      }
  290.  
  291.  
  292. // Function: MDIDesktopCommand - Handle MDI Desktop Command
  293. // --------------------------------------------------------
  294.    MRESULT FAR MDIDesktopCommand (pinst, usCommand)
  295.  
  296.       PINSTDESKTOP pinst;              // -->desktop instance data
  297.       USHORT usCommand;                // WM_COMMAND code
  298.  
  299.    // Define function data
  300.  
  301.      {PINSTDOCUMENT pinstDocument;     // -->document instance data
  302.       HWND hdlg;                       // Unhide dialog box
  303.  
  304.    // Act upon command code
  305.  
  306.       switch (usCommand)
  307.  
  308.    // Handle Desktop system menu command by passing the system equivalent
  309.    // to the desktop frame
  310.  
  311.         {case CMD_DESKTOP_SIZE:
  312.          case CMD_DESKTOP_MOVE:
  313.          case CMD_DESKTOP_MINIMIZE:
  314.          case CMD_DESKTOP_MAXIMIZE:
  315.          case CMD_DESKTOP_CLOSE:
  316.          case CMD_DESKTOP_NEXT:
  317.          case CMD_DESKTOP_APPMENU:
  318.          case CMD_DESKTOP_SYSMENU:
  319.          case CMD_DESKTOP_RESTORE:
  320.             WinSendMsg (pinst->hwndFrame, WM_SYSCOMMAND,
  321.               MPFROMSHORT ((usCommand - CMD_DESKTOP_BASE) | SC_SIZE),
  322.               MPFROM2SHORT (CMDSRC_ACCELERATOR, FALSE));
  323.             break;
  324.  
  325.    // Handle Maximized Document system menu command by applying
  326.    // the command to the active document
  327.  
  328.          case CMD_DOCUMENT_SIZE:
  329.          case CMD_DOCUMENT_MOVE:
  330.          case CMD_DOCUMENT_MINIMIZE:
  331.          case CMD_DOCUMENT_MAXIMIZE:
  332.          case CMD_DOCUMENT_CLOSE:
  333.          case CMD_DOCUMENT_NEXT:
  334.          case CMD_DOCUMENT_APPMENU:
  335.          case CMD_DOCUMENT_SYSMENU:
  336.          case CMD_DOCUMENT_RESTORE:
  337.             if (pinst->hwndDocument != NULL)
  338.               {pinstDocument = (PINSTDOCUMENT) WinQueryWindowULong (pinst->hwndDocument,
  339.                  QWL_USER + sizeof (PVOID));
  340.                if (pinstDocument->usState == SWP_MAXIMIZE)
  341.                  {if (usCommand == CMD_DOCUMENT_RESTORE)
  342.                     {WinSetParent (pinstDocument->hwndMinMax, pinstDocument->hwndFrame, FALSE);
  343.                      WinSetWindowPos (pinstDocument->hwndFrame, NULL, 0, 0, 0, 0, SWP_RESTORE);
  344.                     }
  345.                   else if (usCommand == CMD_DOCUMENT_NEXT)
  346.                      WinSendMsg (pinstDocument->hwndFrame, WM_SYSCOMMAND,
  347.                        MPFROMSHORT (SC_NEXT), MPFROM2SHORT (CMDSRC_MENU, FALSE));
  348.                   else if (usCommand == CMD_DOCUMENT_CLOSE)
  349.                      WinDestroyWindow (pinstDocument->hwndFrame);
  350.                  }
  351.               }
  352.             else WinAlarm (HWND_DESKTOP, WA_WARNING);
  353.             break;
  354.  
  355.    // Handle arrange tiled request
  356.  
  357.          case CMD_ARRANGE_TILED:
  358.             MDIDocumentArrange (pinst->hwndDesktop, AWP_TILED);
  359.             break;
  360.  
  361.    // Handle arrange cascaded request
  362.  
  363.          case CMD_ARRANGE_CASCADED:
  364.             MDIDocumentArrange (pinst->hwndDesktop, AWP_CASCADED);
  365.             break;
  366.  
  367.    // Handle tile always request by reseting flags and checking menu items;
  368.    // then we perform actual tile
  369.  
  370.          case CMD_TILE_ALWAYS:
  371.             pinst->fTileAlways = ! pinst->fTileAlways;
  372.             pinst->fCascadeAlways = FALSE;
  373.             WinSendMsg (pinst->miWindow.hwndSubMenu, MM_SETITEMATTR,
  374.               MPFROM2SHORT (CMD_TILE_ALWAYS, FALSE),
  375.               MPFROM2SHORT (MIA_CHECKED, (pinst->fTileAlways)? MIA_CHECKED : FALSE));
  376.             WinSendMsg (pinst->miWindow.hwndSubMenu, MM_SETITEMATTR,
  377.               MPFROM2SHORT (CMD_CASCADE_ALWAYS, FALSE), MPFROM2SHORT (MIA_CHECKED, FALSE));
  378.             if (pinst->fTileAlways)
  379.                MDIDocumentArrange (pinst->hwndDesktop, AWP_TILED);
  380.             break;
  381.  
  382.    // Handle cascade always request by reseting flags and checking menu items;
  383.    // then we perform actual cascade
  384.  
  385.          case CMD_CASCADE_ALWAYS:
  386.             pinst->fCascadeAlways = ! pinst->fCascadeAlways;
  387.             pinst->fTileAlways = FALSE;
  388.             WinSendMsg (pinst->miWindow.hwndSubMenu, MM_SETITEMATTR,
  389.               MPFROM2SHORT (CMD_CASCADE_ALWAYS, FALSE),
  390.               MPFROM2SHORT (MIA_CHECKED, (pinst->fCascadeAlways)? MIA_CHECKED : FALSE));
  391.             WinSendMsg (pinst->miWindow.hwndSubMenu, MM_SETITEMATTR,
  392.               MPFROM2SHORT (CMD_TILE_ALWAYS, FALSE), MPFROM2SHORT (MIA_CHECKED, FALSE));
  393.             if (pinst->fCascadeAlways)
  394.                MDIDocumentArrange (pinst->hwndDesktop, AWP_CASCADED);
  395.             break;
  396.  
  397.    // Handle request to hide document by removing its entry from the
  398.    // window submenu, activating the next entry in the submenu (if any)
  399.    // and hiding the document
  400.  
  401.          case CMD_HIDE:
  402.             if (pinst->hwndDocument != NULL)
  403.               {pinstDocument = (PINSTDOCUMENT) WinQueryWindowULong (pinst->hwndDocument,
  404.                  QWL_USER + sizeof (PVOID));
  405.                MDIDocumentHide (pinstDocument);
  406.               }
  407.             break;
  408.  
  409.    // Handle request to unhide document using the unhide dialog procedure
  410.  
  411.          case CMD_UNHIDE:
  412.             hdlg = MDILoadDialog (pinst->hwndDesktop, MDIDocumentUnhideDlgProc,
  413.                DIALOG_UNHIDE, (PVOID) pinst);
  414.             WinProcessDlg (hdlg);
  415.             WinDestroyWindow (hdlg);
  416.             break;
  417.  
  418.    // Handle request for a new document by creating the document
  419.    // based on the currently active document
  420.  
  421.          case CMD_NEW_DOCUMENT:
  422.             if (pinst->hwndDocument != NULL)
  423.               {pinstDocument = (PINSTDOCUMENT) WinQueryWindowULong (pinst->hwndDocument,
  424.                  QWL_USER + sizeof (PVOID));
  425.                MDIDocumentClone (pinstDocument);
  426.               }
  427.             break;
  428.  
  429.    // Handle request to activate document
  430.  
  431.          default:
  432.             if (usCommand >= CMD_SELECT_DOCUMENT)
  433.                WinSetWindowPos (WinWindowFromID (pinst->hwndDesktop, usCommand),
  434.                  HWND_TOP, 0, 0, 0, 0, SWP_ACTIVATE | SWP_ZORDER);
  435.             break;
  436.  
  437.    // Complete handling of command code
  438.  
  439.         }
  440.  
  441.    // Indicate command handled
  442.  
  443.       return (MRESULT) NULL;
  444.  
  445.      }
  446.  
  447.  
  448. // Function: MDIDesktopCreate - Create MDI Desktop
  449. // -----------------------------------------------
  450.    MRESULT FAR MDIDesktopCreate (hwndDesktop, pdeskcp)
  451.  
  452.       HWND hwndDesktop;                // Desktop window
  453.       PDESKTOPCREATEPARMS pdeskcp;     // -->desktop creation parameters
  454.  
  455.    // Define function data
  456.  
  457.      {PINSTDESKTOP pinst;              // -->instance data
  458.       BOOL fIniDatFound;               // TRUE == OS2.INI data found
  459.       USHORT cbBuf;                    // Length of OS2.INI data
  460.       SHORT xLeft, xRight;             // X indentation factors
  461.       SHORT yBottom, yTop;             // Y indentation factors
  462.       CHAR szText[MAX_STRING];         // Menu item text
  463.  
  464.    // Acquire instance data and save in MDI desktop window
  465.  
  466.       pinst = AllocMemAlloc ((LONG) (sizeof (*pinst)));
  467.       WinSetWindowULong (hwndDesktop, QWL_USER + sizeof (PVOID), (ULONG) pinst);
  468.  
  469.    // Copy desktop creation parameters if available
  470.  
  471.       if (pdeskcp != NULL)
  472.         {pinst->pszExit = pdeskcp->pszExit;
  473.          pinst->pszWindow = pdeskcp->pszWindow;
  474.         }
  475.  
  476.    // Initialize inter-window relationships
  477.  
  478.       pinst->hwndDesktop = hwndDesktop;
  479.       pinst->hwndFrame = WinQueryWindow (pinst->hwndDesktop, QW_PARENT, FALSE);
  480.       pinst->hwndParent = WinQueryWindow (pinst->hwndFrame, QW_PARENT, FALSE);
  481.       pinst->hwndMenu = WinWindowFromID (pinst->hwndFrame, FID_MENU);
  482.       pinst->idDesktop = WinQueryWindowUShort (pinst->hwndFrame, QWS_ID);
  483.  
  484.    // Verify inter-window relationships
  485.  
  486.       while (pinst->hwndMenu == NULL)
  487.          TellUser (ERROR_DESKTOP_NO_MENU, MDI_MODNAME, MB_ABORTRETRYIGNORE | MB_ICONHAND);
  488.  
  489.    // Find location in desktop menu at which to insert the window sub-menu
  490.  
  491.       pinst->miWindow.iPosition = 0;
  492.       while ((pinst->miWindow.id = (USHORT) WinSendMsg (pinst->hwndMenu, MM_ITEMIDFROMPOSITION,
  493.        MPFROMSHORT (pinst->miWindow.iPosition), NULL)) != MID_ERROR)
  494.         {WinSendMsg (pinst->hwndMenu, MM_QUERYITEMTEXT, MPFROM2SHORT (pinst->miWindow.id,
  495.            sizeof (szText)), (MPARAM) szText);
  496.          if (strcmpi (szText, pinst->pszExit) == 0) break;
  497.          pinst->miWindow.iPosition++;
  498.         }
  499.  
  500.    // Verify that a location for the window sub-menu was found
  501.  
  502.       while (pinst->miWindow.id == MID_ERROR)
  503.          TellUser (ERROR_DESKTOP_NO_EXIT, MDI_MODNAME, MB_ABORTRETRYIGNORE | MB_ICONHAND, pinst->pszExit);
  504.  
  505.    // Initialize the window menu
  506.  
  507.       pinst->miWindow.afStyle = MIS_TEXT | MIS_SUBMENU;
  508.       pinst->miWindow.afAttribute = 0;
  509.       pinst->miWindow.id = MENU_WINDOW;
  510.       pinst->miWindow.hwndSubMenu = MDILoadMenu (pinst->hwndMenu, MENU_WINDOW);
  511.       pinst->miWindow.hItem = NULL;
  512.  
  513.    // Initialize the maximized child system menu
  514.  
  515.       pinst->miSysMenu.iPosition = 0;
  516.       pinst->miSysMenu.afStyle = MIS_BITMAP | MIS_SUBMENU;
  517.       pinst->miSysMenu.afAttribute = 0;
  518.       pinst->miSysMenu.id = MENU_MAXCHILD_SYSMENU;
  519.       pinst->miSysMenu.hwndSubMenu = MDILoadMenu (pinst->hwndMenu, MENU_MAXCHILD_SYSMENU);
  520.       pinst->miSysMenu.hItem = (ULONG) WinGetSysBitmap (HWND_DESKTOP, SBMP_CHILDSYSMENU);
  521.  
  522.    // Load accelerator table
  523.  
  524.       pinst->haccel = MDILoadAccelTable (ACCEL_DESKTOP);
  525.       WinSetAccelTable (NULL, pinst->haccel, pinst->hwndFrame);
  526.  
  527.    // Determine initial title
  528.  
  529.       WinQueryWindowText (pinst->hwndFrame, sizeof (pinst->szTitle), pinst->szTitle);
  530.  
  531.    // Create user desktop data if applicable
  532.  
  533.       if (pdeskcp && (pdeskcp->cbDeskData > 0))
  534.          pinst->pvDeskData = AllocMemAlloc ((LONG) (pdeskcp->cbDeskData));
  535.  
  536.    // Recall window position from OS2.INI file, if available
  537.  
  538.       cbBuf = sizeof (pinst->swp);
  539.       pinst->swp.fs = 0;
  540.       fIniDatFound = WinQueryProfileData (NULL, MDI_DESKTOPCLASS, pinst->szTitle,
  541.         &(pinst->swp), &cbBuf);
  542.  
  543.    // If no prior position was stored, or if the window was maximized,
  544.    // minimized or hidden, find a default position
  545.  
  546.       if (! fIniDatFound || (pinst->swp.fs & (SWP_MINIMIZE | SWP_MAXIMIZE | SWP_HIDE)))
  547.         {xLeft = (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CXSIZEBORDER);
  548.          xRight = (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CXSIZEBORDER);
  549.          yTop = (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CYSIZEBORDER);
  550.          yBottom = 2 * (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CYICON);
  551.          pinst->swp.x = xLeft;
  552.          pinst->swp.y = yBottom;
  553.          pinst->swp.cx = (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CXSCREEN) - xLeft - xRight;
  554.          pinst->swp.cy = (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CYSCREEN) - yBottom - yTop;
  555.          pinst->swp.fs &= ~SWP_HIDE;
  556.         }
  557.  
  558.    // Display and position the desktop window
  559.  
  560.       pinst->swp.fs |= SWP_ACTIVATE | SWP_MOVE | SWP_SIZE | SWP_SHOW | SWP_ZORDER;
  561.       pinst->swp.hwndInsertBehind = HWND_TOP;
  562.       pinst->swp.hwnd = pinst->hwndFrame;
  563.       WinSetMultWindowPos (NULL, &(pinst->swp), 1);
  564.  
  565.    // Indicate create handled
  566.  
  567.       return (MRESULT) NULL;
  568.  
  569.      }
  570.  
  571.  
  572. // Function: MDIDesktopDestroy - Destroy MDI Desktop
  573. // -------------------------------------------------
  574.    MRESULT FAR MDIDesktopDestroy (pinst)
  575.  
  576.       PINSTDESKTOP pinst;              // -->desktop instance data
  577.  
  578.    // Save window position onto OS2.INI file
  579.  
  580.      {WinWriteProfileData (NULL, MDI_DESKTOPCLASS, pinst->szTitle,
  581.         &(pinst->swp), sizeof (pinst->swp));
  582.  
  583.    // Free accelerator table
  584.  
  585.       WinDestroyAccelTable (pinst->haccel);
  586.  
  587.    // Tell user desktop will be destroyed
  588.  
  589.       WinSendMsg (pinst->hwndDesktop, WM_CONTROL,
  590.         MPFROM2SHORT (pinst->idDesktop, MDI_NOTIFY_DESTROY_DESKTOP),
  591.         (MPARAM) pinst->pvDeskData);
  592.  
  593.    // Free user desktop data if applicable
  594.  
  595.       if (pinst->pvDeskData)
  596.          AllocMemFree (pinst->pvDeskData);
  597.  
  598.    // Free instance data
  599.  
  600.       AllocMemFree (pinst);
  601.  
  602.    // Indicate destroy handled
  603.  
  604.       return (MRESULT) NULL;
  605.  
  606.      }
  607.  
  608.  
  609. // Function: MDIDesktopPaint - Paint MDI Desktop
  610. // ---------------------------------------------
  611.    MRESULT FAR MDIDesktopPaint (pinst)
  612.  
  613.       PINSTDESKTOP pinst;              // -->desktop instance data
  614.  
  615.    // Define function data
  616.  
  617.      {HPS hps;                         // Presentation space
  618.       RECTL rcl;                       // Update rectangle
  619.  
  620.    // Paint a white background
  621.  
  622.       hps = WinBeginPaint (pinst->hwndDesktop, NULL, &rcl);
  623.       WinFillRect (hps, &rcl, CLR_WHITE);
  624.       WinEndPaint (hps);
  625.  
  626.    // Indicate paint handled
  627.  
  628.       return (MRESULT) NULL;
  629.  
  630.      }
  631.  
  632.  
  633. // Function: MDIDesktopWndProc - MDI Desktop window procedure
  634. // ----------------------------------------------------------
  635.    MRESULT EXPENTRY MDIDesktopWndProc (hwndDesktop, msg, mp1, mp2)
  636.  
  637.       HWND hwndDesktop;                // Desktop window
  638.       USHORT msg;                      // PM message
  639.       MPARAM mp1;                      // Mesage parameter 1
  640.       MPARAM mp2;                      // Mesage parameter 2
  641.  
  642.    // Define function data
  643.  
  644.      {PINSTDESKTOP pinst;              // -->instance data
  645.  
  646.    // Locate instance data
  647.  
  648.       pinst = (PINSTDESKTOP) WinQueryWindowULong (hwndDesktop, QWL_USER + sizeof (PVOID));
  649.  
  650.    // Analyze and process message
  651.  
  652.       switch (msg)
  653.  
  654.         {case WM_CLOSE:
  655.             return MDIDesktopClose (pinst);
  656.  
  657.          case WM_COMMAND:
  658.             return MDIDesktopCommand (pinst, LOUSHORT (mp1));
  659.  
  660.          case WM_CREATE:
  661.             return MDIDesktopCreate (hwndDesktop, (PDESKTOPCREATEPARMS) mp1);
  662.  
  663.          case WM_DESTROY:
  664.             return MDIDesktopDestroy (pinst);
  665.  
  666.          case WM_MOVE:
  667.             return (MRESULT) !WinQueryWindowPos (pinst->hwndFrame, &(pinst->swp));
  668.  
  669.          case WM_PAINT:
  670.             return MDIDesktopPaint (pinst);
  671.  
  672.          case WM_SIZE:
  673.             return (MRESULT) !WinQueryWindowPos (pinst->hwndFrame, &(pinst->swp));
  674.  
  675.          case MDI_MSG_LOCATE_DESKTOP_DATA:
  676.             return (MRESULT) pinst->pvDeskData;
  677.  
  678.         }
  679.  
  680.    // Return to PM
  681.  
  682.       return WinDefWindowProc (hwndDesktop, msg, mp1, mp2);
  683.  
  684.      }
  685.  
  686.  
  687. // Function: MDIDocumentActivate - Activate MDI Document
  688. // -----------------------------------------------------
  689.    MRESULT FAR MDIDocumentActivate (pinst, fActivate)
  690.  
  691.       PINSTDOCUMENT pinst;             // -->document instance data
  692.       BOOL fActivate;                  // TRUE == document is being activated
  693.  
  694.    // Define function data
  695.  
  696.      {PINSTDESKTOP pinstDesktop;       // -->desktop instance data
  697.       CHAR szTitle[MAX_STRING];        // Desktop title
  698.       MENUITEM mi;                     // Menu item
  699.  
  700.    // Show/hide all frame controls if document is not maximized or minimized
  701.  
  702.       if ((pinst->usState != SWP_MAXIMIZE) && (pinst->usState != SWP_MINIMIZE))
  703.         {WinSetParent (pinst->hwndSysMenu, (fActivate)? pinst->hwndFrame : HWND_OBJECT, FALSE);
  704.          WinSetParent (pinst->hwndMinMax, (fActivate)? pinst->hwndFrame : HWND_OBJECT, FALSE);
  705.          WinSetParent (pinst->hwndVScroll, (fActivate)? pinst->hwndFrame : HWND_OBJECT, FALSE);
  706.          WinSetParent (pinst->hwndHScroll, (fActivate)? pinst->hwndFrame : HWND_OBJECT, FALSE);
  707.          WinSetParent (pinst->hwndTitleBar, pinst->hwndFrame, FALSE);
  708.          WinSendMsg (pinst->hwndFrame, WM_UPDATEFRAME, NULL, NULL);
  709.         }
  710.  
  711.    // Locate the desktop instance data
  712.  
  713.       pinstDesktop = (PINSTDESKTOP) WinQueryWindowULong (pinst->hwndDesktop, QWL_USER + sizeof (PVOID));
  714.  
  715.    // Set the handle of the active document window in the desktop
  716.  
  717.       pinstDesktop->hwndDocument = (fActivate)? pinst->hwndDocument : NULL;
  718.  
  719.    // Check/uncheck the document title in the window menu
  720.  
  721.       WinSendMsg (pinstDesktop->miWindow.hwndSubMenu, MM_SETITEMATTR,
  722.         MPFROM2SHORT (pinst->idFrame, FALSE),
  723.         MPFROM2SHORT (MIA_CHECKED, (fActivate)? MIA_CHECKED : FALSE));
  724.  
  725.    // For maximized documents, insert/remove the maximized child system
  726.    // menu from the desktop and set/reset the desktop title
  727.  
  728.       if (pinst->usState == SWP_MAXIMIZE)
  729.         {if (fActivate)
  730.            {if (! (BOOL) WinSendMsg (pinstDesktop->hwndMenu, MM_QUERYITEM,
  731.               MPFROM2SHORT (MENU_MAXCHILD_SYSMENU, FALSE), (MPARAM) &mi))
  732.                WinSendMsg (pinstDesktop->hwndMenu, MM_INSERTITEM, (MPARAM) &(pinstDesktop->miSysMenu), NULL);
  733.             if (pinst->iTitle == 0)
  734.               sprintf (szTitle, "%s - %s", pinstDesktop->szTitle, pinst->szTitle);
  735.             else sprintf (szTitle, "%s - %s:%d", pinstDesktop->szTitle, pinst->szTitle, pinst->iTitle);
  736.             WinSetWindowText (pinstDesktop->hwndFrame, szTitle);
  737.            }
  738.          else
  739.            {WinSendMsg (pinstDesktop->hwndMenu, MM_REMOVEITEM,
  740.               MPFROM2SHORT (MENU_MAXCHILD_SYSMENU, FALSE), NULL);
  741.             WinSetWindowText (pinstDesktop->hwndFrame, pinstDesktop->szTitle);
  742.            }
  743.         }
  744.  
  745.    // Indicate activate handled
  746.  
  747.       return (MRESULT) NULL;
  748.  
  749.      }
  750.  
  751.  
  752. // Function: MDIDocumentArrange - Arrange all documents in the desktop
  753. // -------------------------------------------------------------------
  754.    VOID FAR MDIDocumentArrange (hwndDesktop, fStyle)
  755.  
  756.       HWND hwndDesktop;                // Desktop window
  757.       USHORT fStyle;                   // AWP_TILED or AWP_CASCADED
  758.  
  759.    // Define function data
  760.  
  761.      {USHORT cswpWnd, cswpIcon;        // Window, Icon count
  762.       PSWP pswpWnd, pswpIcon;          // Window, Icon positions
  763.       ULONG ulStyle;                   // Style flags
  764.       RECTL rcl;                       // Rectangle work area
  765.       HWND hwnd;                       // Window work area
  766.       SHORT cDocs;                     // Document count
  767.       USHORT yIcon;                    // Icon height
  768.       SHORT cxBorderInset;             // Border inset width
  769.       USHORT i;                        // Counter
  770.       PINSTDOCUMENT pinstDocument;     // -->document instance data
  771.  
  772.       SHORT cxBorder =                 // Border width
  773.         (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CXSIZEBORDER);
  774.  
  775.       SHORT cyBorder =                 // Border height
  776.         (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CYSIZEBORDER);
  777.  
  778.    // Count the number of document windows
  779.  
  780.       for (cDocs = 0, hwnd = WinQueryWindow (hwndDesktop, QW_TOP, FALSE);
  781.         hwnd; hwnd = WinQueryWindow (hwnd, QW_NEXT, FALSE)) cDocs++;
  782.  
  783.    // Allocate space for document and icon positions
  784.  
  785.       pswpWnd = (PSWP) AllocMemAlloc ((LONG) (sizeof(SWP) * cDocs));
  786.       pswpIcon = (PSWP) AllocMemAlloc ((LONG) (sizeof(SWP) * cDocs));
  787.  
  788.    // Enumerate windows and selectively add them to the arrange lists
  789.  
  790.       for (cswpWnd = cswpIcon = 0, hwnd = WinQueryWindow (hwndDesktop, QW_TOP, FALSE);
  791.        hwnd; hwnd = WinQueryWindow (hwnd, QW_NEXT, FALSE))
  792.  
  793.       // Make sure the window is visible and owned by the desktop
  794.  
  795.         {ulStyle = WinQueryWindowULong (hwnd, QWL_STYLE);
  796.          if (WinQueryWindow (hwnd, QW_OWNER, FALSE)
  797.           || !(ulStyle & WS_VISIBLE)) continue;
  798.  
  799.       // Count icons
  800.  
  801.          if (ulStyle & WS_MINIMIZED)
  802.             pswpIcon[cswpIcon++].hwnd = hwnd;
  803.  
  804.       // Count windows (restore any that are maximized)
  805.  
  806.          else
  807.            {if (ulStyle & WS_MAXIMIZED)
  808.               {pinstDocument = (PINSTDOCUMENT) WinQueryWindowULong
  809.                  (WinWindowFromID (hwnd, FID_CLIENT), QWL_USER + sizeof (PVOID));
  810.                WinSetParent (pinstDocument->hwndMinMax, pinstDocument->hwndFrame, FALSE);
  811.                WinSetWindowPos (hwnd, NULL, 0, 0, 0, 0, SWP_ACTIVATE | SWP_RESTORE);
  812.               }
  813.             pswpWnd[cswpWnd++].hwnd = hwnd;
  814.            }
  815.  
  816.       // Loop until all windows accounted for
  817.  
  818.         }
  819.  
  820.    // Get dimensions of desktop window
  821.  
  822.       WinQueryWindowRect (hwndDesktop, &rcl);
  823.       cxBorderInset = (SHORT) (WinQuerySysValue (HWND_DESKTOP, SV_CXBYTEALIGN)
  824.         - WinQuerySysValue (HWND_DESKTOP, SV_CXSIZEBORDER));
  825.       WinInflateRect (NULL, &rcl, -cxBorderInset, -cxBorderInset * (cyBorder / cxBorder));
  826.  
  827.    // Make room for a single row of icons
  828.  
  829.       if (cswpIcon > 0)
  830.         {yIcon = LOUSHORT (WinQuerySysValue(HWND_DESKTOP, SV_CYICON));
  831.          rcl.yBottom += (yIcon * ICON_PARK_NUM) / ICON_PARK_DENOM;
  832.         }
  833.  
  834.    // Set window positions
  835.  
  836.       if (fStyle == AWP_CASCADED)
  837.          MDIDocumentArrangeCascaded (&rcl, cswpWnd, pswpWnd);
  838.       else if (fStyle == AWP_TILED)
  839.          MDIDocumentArrangeTiled (&rcl, cswpWnd, pswpWnd);
  840.  
  841.    // Set icon positions
  842.  
  843.       for (i = 0; i < cswpIcon; i++)
  844.         {pswpIcon[i].x = 0;
  845.          pswpIcon[i].y = 0;
  846.          pswpIcon[i].fs = SWP_MOVE;
  847.         }
  848.  
  849.    // Reposition all windows and icons
  850.  
  851.       WinSetMultWindowPos (NULL, pswpWnd, cswpWnd);
  852.       WinSetMultWindowPos (NULL, pswpIcon, cswpIcon);
  853.  
  854.    // Free space used for document and icon positions
  855.  
  856.       AllocMemFree (pswpWnd);
  857.       AllocMemFree (pswpIcon);
  858.  
  859.      }
  860.  
  861.  
  862. // Function: MDIDocumentArrangeCascaded - Arrange all documents cascaded
  863. // ---------------------------------------------------------------------
  864.    VOID FAR MDIDocumentArrangeCascaded (prc, cWnd, aswp)
  865.  
  866.       PRECTL prc;                      // -->area in which arrange will occur
  867.       SHORT cWnd;                      // Count of windows
  868.       PSWP aswp;                       // Array of window positions
  869.  
  870.    // Define function data
  871.  
  872.      {SHORT xEdge, yEdge;
  873.       SHORT xDelta, yDelta;
  874.       SHORT cMaxWnd;
  875.       SHORT x, y, i, j;
  876.       RECTL rc;
  877.  
  878.       SHORT cxBorder =
  879.         (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CXSIZEBORDER);
  880.  
  881.       SHORT cyBorder =
  882.         (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CYSIZEBORDER);
  883.  
  884.    // Set cascade parameters
  885.  
  886.       rc.xLeft = prc->xLeft - cxBorder;
  887.       rc.xRight = prc->xRight + cyBorder;
  888.       rc.yBottom = prc->yBottom - cyBorder;
  889.       rc.yTop = prc->yTop + cyBorder;
  890.  
  891.    // Get x and y deltas from system values
  892.  
  893.       xDelta = LOUSHORT(WinQuerySysValue(HWND_DESKTOP, SV_CXSIZEBORDER))
  894.         + LOUSHORT(WinQuerySysValue(HWND_DESKTOP, SV_CXMINMAXBUTTON)) / 2 + 2;
  895.       yDelta = LOUSHORT(WinQuerySysValue(HWND_DESKTOP, SV_CYTITLEBAR));
  896.  
  897.    // Get initial cut at yEdge using fraction
  898.  
  899.       yEdge = (((SHORT)(rc.yTop - rc.yBottom)) * CASC_EDGE_NUM) / CASC_EDGE_DENOM;
  900.  
  901.    // Determine maximum number of deltas used per run
  902.  
  903.       cMaxWnd = (((SHORT)(rc.yTop - rc.yBottom)) - yEdge) / yDelta;
  904.  
  905.    // Set x and y edges so full cascade will fill rectangle completely
  906.  
  907.       xEdge = ((SHORT)(rc.xRight - rc.xLeft)) - xDelta/2 - cMaxWnd * xDelta;
  908.       yEdge = ((SHORT)(rc.yTop - rc.yBottom)) - cMaxWnd * yDelta;
  909.       cMaxWnd++;
  910.  
  911.    // Arrange if only one run is needed
  912.  
  913.       if (cWnd <= cMaxWnd)
  914.         {x = (SHORT)rc. xLeft;
  915.          y = (SHORT)rc. yTop - yEdge;
  916.          for (i = cWnd - 1; i >= 0; i--)
  917.            {aswp[i].x = x;
  918.             aswp[i].y = y;
  919.             aswp[i].cx = xEdge;
  920.             aswp[i].cy = yEdge;
  921.             aswp[i].fs = SWP_SIZE | SWP_MOVE;
  922.             x += xDelta;
  923.             y -= yDelta;
  924.            }
  925.         }
  926.  
  927.    // Arrange if multiple runs are necessary; start at bottom
  928.    // right, iterate up to top left
  929.  
  930.       else
  931.         {i = 0;
  932.          while (i < cWnd)
  933.            {x = ((SHORT)rc. xLeft) + (cMaxWnd-1) * xDelta;
  934.             y = ((SHORT)rc. yTop) - yEdge - (cMaxWnd-1) * yDelta;
  935.             for (j = 0; j < cMaxWnd; j++)
  936.               {aswp[i].x = x;
  937.                aswp[i].y = y;
  938.                aswp[i].cx = xEdge;
  939.                aswp[i].cy = yEdge;
  940.                aswp[i].fs = SWP_SIZE | SWP_MOVE;
  941.                x -= xDelta;
  942.                y += yDelta;
  943.                if (++i >= cWnd) break;
  944.               }
  945.             if (i >= cWnd) break;
  946.             x = ((SHORT)rc. xLeft) + (cMaxWnd-1) * xDelta + xDelta/2;
  947.             y = ((SHORT)rc. yTop) - yEdge - (cMaxWnd-1) * yDelta + yDelta/2;
  948.             for (j = 0; j < cMaxWnd - 1; j++)
  949.               {aswp[i].x = x;
  950.                aswp[i].y = y;
  951.                aswp[i].cx = xEdge;
  952.                aswp[i].cy = yEdge;
  953.                aswp[i].fs = SWP_SIZE | SWP_MOVE;
  954.                x -= xDelta;
  955.                y += yDelta;
  956.                if (++i >= cWnd) break;
  957.               }
  958.            }
  959.         }
  960.      }
  961.  
  962.  
  963. // Function: MDIDocumentArrangeTiled - Arrange all documents tiled
  964. // ---------------------------------------------------------------
  965.    VOID FAR MDIDocumentArrangeTiled (prc, cWnd, aswp)
  966.  
  967.       PRECTL prc;                      // -->area in which arrange will occur
  968.       SHORT cWnd;                      // Count of windows
  969.       PSWP aswp;                       // Array of window positions
  970.  
  971.    // Define function data
  972.  
  973.      {USHORT usRoot;
  974.       SHORT cExtras;
  975.       SHORT iChange;
  976.       SHORT cDiff;
  977.       SHORT x, y, cx, cy;
  978.       SHORT iRow, iCol;
  979.  
  980.       SHORT cxBorder =
  981.         (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CXSIZEBORDER);
  982.  
  983.       SHORT cyBorder =
  984.         (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CYSIZEBORDER);
  985.  
  986.    // Get grid dimensions
  987.  
  988.       if ((USHORT) cWnd > (0xFE * 0xFE))
  989.          usRoot = 0x00FF;
  990.       else for (usRoot = 0; (usRoot * usRoot) < cWnd; usRoot++);
  991.       cExtras = usRoot * usRoot - cWnd;
  992.  
  993.    // Find column where number of rows increases and find initial
  994.    // difference of rows versus columns
  995.  
  996.       if (cExtras >= usRoot)
  997.         {iChange = cExtras - usRoot;
  998.          cDiff = 2;
  999.         }
  1000.       else
  1001.         {iChange = cExtras;
  1002.          cDiff = 1;
  1003.         }
  1004.  
  1005.    // Assign x coordinates
  1006.  
  1007.       x = (SHORT)prc->xLeft;
  1008.       cx = 0;
  1009.       for (iCol = 0; iCol < usRoot; iCol++)
  1010.         {x += cx - cxBorder;
  1011.          cx = ((SHORT)prc->xLeft) + (((SHORT)(prc->xRight - prc->xLeft))
  1012.            * (iCol + 1)) / usRoot - x + cxBorder;
  1013.          for (iRow = 0; iRow < usRoot - cDiff; iRow++)
  1014.            {aswp[iRow * usRoot + iCol].x = x;
  1015.             aswp[iRow * usRoot + iCol].cx = cx;
  1016.             aswp[iRow * usRoot + iCol].fs = SWP_SIZE | SWP_MOVE;
  1017.            }
  1018.          if (iCol >= iChange)
  1019.            {aswp[iRow * usRoot + iCol - iChange].x = x;
  1020.             aswp[iRow * usRoot + iCol - iChange].cx = cx;
  1021.             aswp[iRow * usRoot + iCol - iChange].fs = SWP_SIZE | SWP_MOVE;
  1022.            }
  1023.         }
  1024.  
  1025.    // Assign y coordinates, for columns without extra row
  1026.  
  1027.       y = (SHORT)prc->yBottom;
  1028.       cy = 0;
  1029.       for (iRow = usRoot - cDiff - 1; iRow >= 0; iRow--)
  1030.         {y += cy - cyBorder;
  1031.          cy = ((SHORT)prc->yBottom) + (((SHORT)(prc->yTop - prc->yBottom))
  1032.            * (usRoot - cDiff - iRow)) / (usRoot - cDiff) - y + cyBorder;
  1033.          for (iCol = 0; iCol < iChange; iCol++)
  1034.            {aswp[iRow * usRoot + iCol].y = y;
  1035.             aswp[iRow * usRoot + iCol].cy = cy;
  1036.            }
  1037.         }
  1038.  
  1039.    // Assign y coordinates, for columns with extra row
  1040.    // do last row first (different offsets)
  1041.  
  1042.       y = (SHORT)prc->yBottom - cyBorder;
  1043.       cy = ((SHORT)(prc->yTop - prc->yBottom)) / (usRoot - cDiff + 1) + (2 * cyBorder);
  1044.       for (iCol = iChange; iCol < usRoot; iCol++)
  1045.         {aswp[usRoot * (usRoot - cDiff) + iCol - iChange].y = y;
  1046.          aswp[usRoot * (usRoot - cDiff) + iCol - iChange].cy = cy;
  1047.         }
  1048.       for (iRow = usRoot - cDiff - 1; iRow >= 0; iRow--)
  1049.         {y += cy - cyBorder;
  1050.          cy = ((SHORT)(prc->yBottom)) + (((SHORT)(prc->yTop - prc->yBottom))
  1051.            * (usRoot - cDiff - iRow + 1)) / (usRoot - cDiff + 1) - y + cyBorder;
  1052.          for (iCol = iChange; iCol < usRoot; iCol++)
  1053.            {aswp[iRow * usRoot + iCol].y = y;
  1054.             aswp[iRow * usRoot + iCol].cy = cy;
  1055.            }
  1056.         }
  1057.      }
  1058.  
  1059.  
  1060. // Function: MDIDocumentClone - Clone MDI Document
  1061. // -----------------------------------------------
  1062.    VOID FAR MDIDocumentClone (pinst)
  1063.  
  1064.       PINSTDOCUMENT pinst;             // -->document instance data
  1065.  
  1066.    // Define function data
  1067.  
  1068.      {PINSTDESKTOP pinstDesktop;       // -->desktop instance data
  1069.       HWND hwndDocument;               // New document window
  1070.       HWND hwndFrame;                  // New document frame
  1071.       HPOINTER hptrIcon;               // New document icon
  1072.       ULONG flFrameFlags;              // Frame flags
  1073.  
  1074.    // Locate the desktop instance data
  1075.  
  1076.       pinstDesktop = (PINSTDESKTOP) WinQueryWindowULong (pinst->hwndDesktop,
  1077.         QWL_USER + sizeof (PVOID));
  1078.  
  1079.    // Copy creation flags from base document; set defaults if missing
  1080.  
  1081.       if (pinst->flCreateFlags != NULL)
  1082.         flFrameFlags = pinst->flCreateFlags;
  1083.       else flFrameFlags = FCF_TITLEBAR | FCF_SYSMENU | FCF_SIZEBORDER | FCF_MINMAX
  1084.         | FCF_SHELLPOSITION | FCF_NOBYTEALIGN;
  1085.  
  1086.    // Create new document
  1087.  
  1088.       hwndFrame = MDICreateDocument (pinst->pfnUser, pinstDesktop->hwndDesktop,
  1089.         flFrameFlags, pinst->szTitle, pinst->hmodResources, pinst->idResources,
  1090.         &hwndDocument, pinst->cbDocData, pinst->cbInstData);
  1091.  
  1092.    // Copy QWL_USER from base document
  1093.  
  1094.       WinSetWindowULong (hwndDocument, QWL_USER, WinQueryWindowULong
  1095.         (pinst->hwndDocument, QWL_USER));
  1096.  
  1097.    // If applicable, copy icon from base document
  1098.  
  1099.       if ((hptrIcon = (HPOINTER) WinSendMsg (pinst->hwndFrame,
  1100.         WM_QUERYICON, NULL, NULL)) != NULL)
  1101.          WinSendMsg (hwndFrame, WM_SETICON, (MPARAM) hptrIcon, NULL);
  1102.  
  1103.      }
  1104.  
  1105.  
  1106. // Function: MDIDocumentClose - Close MDI Document
  1107. // -----------------------------------------------
  1108.    MRESULT FAR MDIDocumentClose (pinst)
  1109.  
  1110.       PINSTDOCUMENT pinst;             // -->document instance data
  1111.  
  1112.    // Destroy document window
  1113.  
  1114.      {WinDestroyWindow (pinst->hwndFrame);
  1115.  
  1116.    // Indicate close handled
  1117.  
  1118.       return (MRESULT) NULL;
  1119.  
  1120.      }
  1121.  
  1122.  
  1123. // Function: MDIDocumentCommand - Handle MDI Document Command
  1124. // ----------------------------------------------------------
  1125.    MRESULT FAR MDIDocumentCommand (pinst, usCommand)
  1126.  
  1127.       PINSTDOCUMENT pinst;             // -->document instance data
  1128.       USHORT usCommand;                // WM_COMMAND code
  1129.  
  1130.    // Act upon command code
  1131.  
  1132.      {switch (usCommand)
  1133.  
  1134.    // Handle Desktop system menu command by passing it to the desktop frame
  1135.  
  1136.         {case CMD_DESKTOP_SIZE:
  1137.          case CMD_DESKTOP_MOVE:
  1138.          case CMD_DESKTOP_MINIMIZE:
  1139.          case CMD_DESKTOP_MAXIMIZE:
  1140.          case CMD_DESKTOP_CLOSE:
  1141.          case CMD_DESKTOP_NEXT:
  1142.          case CMD_DESKTOP_APPMENU:
  1143.          case CMD_DESKTOP_SYSMENU:
  1144.          case CMD_DESKTOP_RESTORE:
  1145.             WinSendMsg (pinst->hwndDesktop, WM_COMMAND,
  1146.               MPFROMSHORT (usCommand), MPFROM2SHORT (CMDSRC_ACCELERATOR, FALSE));
  1147.             break;
  1148.  
  1149.    // Handle Document system menu command by passing the system equivalent
  1150.    // to the document's frame. However, if the document is maximized,
  1151.    // the command is passed to the desktop for processing
  1152.  
  1153.          case CMD_DOCUMENT_SIZE:
  1154.          case CMD_DOCUMENT_MOVE:
  1155.          case CMD_DOCUMENT_MINIMIZE:
  1156.          case CMD_DOCUMENT_MAXIMIZE:
  1157.          case CMD_DOCUMENT_CLOSE:
  1158.          case CMD_DOCUMENT_NEXT:
  1159.          case CMD_DOCUMENT_APPMENU:
  1160.          case CMD_DOCUMENT_SYSMENU:
  1161.          case CMD_DOCUMENT_RESTORE:
  1162.             if (pinst->usState == SWP_MAXIMIZE)
  1163.                WinSendMsg (pinst->hwndDesktop, WM_COMMAND,
  1164.                  MPFROMSHORT (usCommand), MPFROM2SHORT (CMDSRC_ACCELERATOR, FALSE));
  1165.             else
  1166.                WinSendMsg (pinst->hwndFrame, WM_SYSCOMMAND,
  1167.                  MPFROMSHORT ((usCommand - CMD_DOCUMENT_BASE) | SC_SIZE),
  1168.                  MPFROM2SHORT (CMDSRC_ACCELERATOR, FALSE));
  1169.             break;
  1170.  
  1171.    // Terminate command processing
  1172.  
  1173.         }
  1174.  
  1175.    // Indicate command handled
  1176.  
  1177.       return (MRESULT) NULL;
  1178.  
  1179.      }
  1180.  
  1181.  
  1182. // Function: MDIDocumentCreate - Create MDI Document
  1183. // -------------------------------------------------
  1184.    MRESULT FAR MDIDocumentCreate (hwndDocument, pdoccp)
  1185.  
  1186.       HWND hwndDocument;               // Document window
  1187.       PDOCUMENTCREATEPARMS pdoccp;     // -->document creation parameters
  1188.  
  1189.    // Define function data
  1190.  
  1191.      {PINSTDOCUMENT pinst;             // -->instance data
  1192.       PINSTDOCUMENT pinstFirst;        // -->first document instance data
  1193.       PINSTDESKTOP pinstDesktop;       // -->desktop instance data
  1194.       MENUITEM mi;                     // Menu structure
  1195.       CHAR szClassName[MAX_STRING];    // Class name
  1196.       BOOL fIniDatFound;               // TRUE == OS2.INI data found
  1197.       USHORT cbBuf;                    // Length of OS2.INI data
  1198.       HWND hwndSibling;                // Sibling window handle
  1199.       SWP swp;                         // Sibling position
  1200.  
  1201.    // Acquire instance data and save in MDI document window
  1202.  
  1203.       pinst = AllocMemAlloc ((LONG) (sizeof (*pinst)));
  1204.       WinSetWindowULong (hwndDocument, QWL_USER + sizeof (PVOID), (ULONG) pinst);
  1205.  
  1206.    // Copy document creation parameters if available
  1207.  
  1208.       if (pdoccp != NULL)
  1209.         {pinst->cbDocData = pdoccp->cbDocData;
  1210.          pinst->cbInstData = pdoccp->cbInstData;
  1211.          pinst->pfnUser = pdoccp->pfnUser;
  1212.          pinst->flCreateFlags = pdoccp->flCreateFlags;
  1213.          pinst->hmodResources = pdoccp->hmodResources;
  1214.          pinst->idResources = pdoccp->idResources;
  1215.         }
  1216.  
  1217.    // Initialize inter-window relationships
  1218.  
  1219.       pinst->hwndDocument = hwndDocument;
  1220.       pinst->hwndFrame = WinQueryWindow (pinst->hwndDocument, QW_PARENT, FALSE);
  1221.       pinst->hwndDesktop = WinQueryWindow (pinst->hwndFrame, QW_PARENT, FALSE);
  1222.       pinst->hwndSysMenu = WinWindowFromID (pinst->hwndFrame, FID_SYSMENU);
  1223.       pinst->hwndTitleBar = WinWindowFromID (pinst->hwndFrame, FID_TITLEBAR);
  1224.       pinst->hwndMinMax = WinWindowFromID (pinst->hwndFrame, FID_MINMAX);
  1225.       pinst->hwndVScroll = WinWindowFromID (pinst->hwndFrame, FID_VERTSCROLL);
  1226.       pinst->hwndHScroll = WinWindowFromID (pinst->hwndFrame, FID_HORZSCROLL);
  1227.       pinst->idDesktop = WinQueryWindowUShort (WinQueryWindow (pinst->hwndDesktop, QW_PARENT, FALSE), QWS_ID);
  1228.  
  1229.    // Verify inter-window relationships
  1230.  
  1231.       WinQueryClassName (pinst->hwndDesktop, sizeof (szClassName), szClassName);
  1232.       while (stricmp (szClassName, MDI_DESKTOPCLASS) != 0)
  1233.          TellUser (ERROR_DOCUMENT_PARENT_INVALID, MDI_MODNAME, MB_ABORTRETRYIGNORE | MB_ICONHAND);
  1234.  
  1235.       while (pinst->hwndSysMenu == NULL)
  1236.          TellUser (ERROR_DOCUMENT_NO_SYSMENU, MDI_MODNAME, MB_ABORTRETRYIGNORE | MB_ICONHAND);
  1237.  
  1238.    // Find the desktop instance data
  1239.  
  1240.       pinstDesktop = (PINSTDESKTOP) WinQueryWindowULong (pinst->hwndDesktop, QWL_USER + sizeof (PVOID));
  1241.  
  1242.    // Load accelerator table
  1243.  
  1244.       pinst->haccel = MDILoadAccelTable (ACCEL_DOCUMENT);
  1245.       WinSetAccelTable (NULL, pinst->haccel, pinst->hwndFrame);
  1246.  
  1247.    // Alter the appearance of the child system menu to the MDI standard
  1248.  
  1249.       WinSendMsg (pinst->hwndSysMenu, MM_QUERYITEM, MPFROM2SHORT (SC_SYSMENU, FALSE), (MPARAM) &mi);
  1250.       WinSendMsg (pinst->hwndSysMenu, MM_REMOVEITEM, MPFROM2SHORT (SC_SYSMENU, FALSE), NULL);
  1251.       mi.hItem = (ULONG) WinGetSysBitmap (HWND_DESKTOP, SBMP_CHILDSYSMENU);
  1252.       mi.hwndSubMenu = MDILoadMenu (pinst->hwndSysMenu, MENU_CHILD_SYSMENU);
  1253.       WinSendMsg (pinst->hwndSysMenu, MM_INSERTITEM, (MPARAM) &mi, NULL);
  1254.  
  1255.    // Determine initial title
  1256.  
  1257.       WinQueryWindowText (pinst->hwndFrame, sizeof (pinst->szTitle), pinst->szTitle);
  1258.  
  1259.    // Rebuild the titles of all document windows inserting the current title
  1260.  
  1261.       pinstFirst = MDIDocumentTitlesRebuild (pinst, INSERT_TITLE);
  1262.  
  1263.    // If applicable, allocate user document and instance data
  1264.  
  1265.       if (pdoccp && (pdoccp->cbInstData > 0))
  1266.          pinst->pvInstData = AllocMemAlloc ((LONG) (pdoccp->cbInstData));
  1267.       if (pdoccp && (pdoccp->cbDocData > 0))
  1268.         {if (pinst == pinstFirst)
  1269.             pinst->pvDocData = AllocMemAlloc ((LONG) (pdoccp->cbDocData));
  1270.          else if (pinstFirst != NULL)
  1271.             pinst->pvDocData = pinstFirst->pvDocData;
  1272.         }
  1273.  
  1274.    // Recall window information from OS2.INI file, if available
  1275.  
  1276.       cbBuf = sizeof (pinst->swp);
  1277.       pinst->swp.fs = 0;
  1278.       fIniDatFound = WinQueryProfileData (NULL, MDI_DOCUMENTCLASS,
  1279.         pinst->szTitle, &(pinst->swp), &cbBuf);
  1280.  
  1281.    // If no prior position was stored, or if the window was maximized,
  1282.    // minimized or hidden, find a default position inside the desktop
  1283.    // window. If there is a sibling that is a frame, and is itself not
  1284.    // maximized, minimized or hidden, offset the new window from it
  1285.  
  1286.       if (! fIniDatFound || (pinst->swp.fs & (SWP_MINIMIZE | SWP_MAXIMIZE | SWP_HIDE)))
  1287.         {WinQueryWindowPos (pinst->hwndDesktop, &swp);
  1288.          pinst->swp.cx = swp.cx / 2;
  1289.          pinst->swp.cy = swp.cy / 2;
  1290.          pinst->swp.x = swp.x + ((SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CXVSCROLL) / 2);
  1291.          pinst->swp.y = swp.y + pinst->swp.cy - ((SHORT) WinQuerySysValue
  1292.            (HWND_DESKTOP, SV_CYTITLEBAR) / 2);
  1293.          if ((hwndSibling = WinQueryWindow (pinst->hwndFrame, QW_NEXT, FALSE)) != NULL)
  1294.            {WinQueryClassName (hwndSibling, sizeof (szClassName), szClassName);
  1295.             if (strcmpi (szClassName, "#1") == 0)
  1296.               {WinQueryWindowPos (hwndSibling, &swp);
  1297.                if (! (swp.fs & (SWP_MINIMIZE | SWP_MAXIMIZE | SWP_HIDE)))
  1298.                  {pinst->swp.x = swp.x + ((SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CXVSCROLL) / 2);
  1299.                   pinst->swp.y = swp.y - (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CYTITLEBAR);
  1300.                   pinst->swp.cx = swp.cx;
  1301.                   pinst->swp.cy = swp.cy;
  1302.                  }
  1303.               }
  1304.            }
  1305.          pinst->swp.fs &= ~SWP_HIDE;
  1306.         }
  1307.  
  1308.    // Display, position and activate the document window
  1309.  
  1310.       if (pinstDesktop->fTileAlways || pinstDesktop->fCascadeAlways)
  1311.          pinst->swp.fs = SWP_ACTIVATE | SWP_SHOW | SWP_ZORDER;
  1312.       else pinst->swp.fs |= SWP_ACTIVATE | SWP_MOVE | SWP_SIZE | SWP_SHOW | SWP_ZORDER;
  1313.       pinst->swp.hwndInsertBehind = HWND_TOP;
  1314.       pinst->swp.hwnd = pinst->hwndFrame;
  1315.       WinSetMultWindowPos (NULL, &(pinst->swp), 1);
  1316.  
  1317.    // Re-tile or re-cascade all windows if necessary
  1318.  
  1319.       if (pinstDesktop->fTileAlways)
  1320.          MDIDocumentArrange (pinst->hwndDesktop, AWP_TILED);
  1321.       else if (pinstDesktop->fCascadeAlways)
  1322.          MDIDocumentArrange (pinst->hwndDesktop, AWP_CASCADED);
  1323.  
  1324.    // Force the current window to be activated
  1325.  
  1326.       MDIDocumentActivate (pinst, TRUE);
  1327.  
  1328.    // Indicate create handled
  1329.  
  1330.       return (MRESULT) NULL;
  1331.  
  1332.      }
  1333.  
  1334.  
  1335. // Function: MDIDocumentDestroy - Destroy MDI Document
  1336. // ---------------------------------------------------
  1337.    MRESULT FAR MDIDocumentDestroy (pinst)
  1338.  
  1339.       PINSTDOCUMENT pinst;             // -->document instance data
  1340.  
  1341.    // Define function data
  1342.  
  1343.      {PINSTDESKTOP pinstDesktop;       // -->desktop instance data
  1344.       PINSTDOCUMENT pinstFirst;        // -->first document instance data
  1345.       PINSTDOCUMENT pinstDocument;     // -->next active document instance data
  1346.       HWND hwndFrame;                  // Next active document frame
  1347.       MENUITEM mi;                     // Menu item
  1348.  
  1349.    // Find the desktop instance data
  1350.  
  1351.       pinstDesktop = (PINSTDESKTOP) WinQueryWindowULong (pinst->hwndDesktop, QWL_USER + sizeof (PVOID));
  1352.  
  1353.    // Save window position onto OS2.INI file
  1354.  
  1355.       WinWriteProfileData (NULL, MDI_DOCUMENTCLASS, pinst->szTitle,
  1356.         &(pinst->swp), sizeof (pinst->swp));
  1357.  
  1358.    // Rebuild the titles of all document windows removing the current title
  1359.  
  1360.       pinstFirst = MDIDocumentTitlesRebuild (pinst, REMOVE_TITLE);
  1361.  
  1362.    // Free accelerator table
  1363.  
  1364.       WinDestroyAccelTable (pinst->haccel);
  1365.  
  1366.    // Re-tile or re-cascade all remaining windows if necessary
  1367.  
  1368.       if (pinstDesktop->fTileAlways)
  1369.          MDIDocumentArrange (pinst->hwndDesktop, AWP_TILED);
  1370.       else if (pinstDesktop->fCascadeAlways)
  1371.          MDIDocumentArrange (pinst->hwndDesktop, AWP_CASCADED);
  1372.  
  1373.    // If there is a next entry in the window submenu, activate it
  1374.  
  1375.       if ((mi.id = (SHORT) WinSendMsg (pinstDesktop->miWindow.hwndSubMenu, MM_ITEMIDFROMPOSITION,
  1376.        MPFROMSHORT (CMD_WINDOW_MAX - CMD_WINDOW), NULL)) != MID_ERROR)
  1377.         {hwndFrame = WinWindowFromID (pinstDesktop->hwndDesktop, mi.id);
  1378.          WinSetWindowPos (hwndFrame, HWND_TOP, 0, 0, 0, 0, SWP_ACTIVATE | SWP_ZORDER);
  1379.          pinstDocument = (PINSTDOCUMENT) WinQueryWindowULong (WinWindowFromID (hwndFrame, FID_CLIENT),
  1380.            QWL_USER + sizeof (PVOID));
  1381.          if (pinstDocument)
  1382.             MDIDocumentActivate (pinstDocument, TRUE);
  1383.         }
  1384.  
  1385.    // Tell user instance and maybe document will be destroyed
  1386.  
  1387.       WinSendMsg (pinst->hwndDocument, WM_CONTROL,
  1388.         MPFROM2SHORT (pinst->idDesktop, MDI_NOTIFY_DESTROY_INSTANCE),
  1389.         (MPARAM) pinst->pvInstData);
  1390.       if (pinstFirst == NULL)
  1391.          WinSendMsg (pinst->hwndDocument, WM_CONTROL,
  1392.            MPFROM2SHORT (pinst->idDesktop, MDI_NOTIFY_DESTROY_DOCUMENT), (MPARAM) pinst->pvDocData);
  1393.  
  1394.    // If applicable, free the user document and instance data
  1395.  
  1396.       if (pinst->pvInstData)
  1397.          AllocMemFree (pinst->pvInstData);
  1398.       if (pinst->pvDocData && (pinstFirst == NULL))
  1399.          AllocMemFree (pinst->pvDocData);
  1400.  
  1401.    // Free instance data
  1402.  
  1403.       AllocMemFree (pinst);
  1404.  
  1405.    // Indicate destroy handled
  1406.  
  1407.       return (MRESULT) NULL;
  1408.  
  1409.      }
  1410.  
  1411.  
  1412. // Function: MDIDocumentHide - Hide MDI Document
  1413. // ---------------------------------------------
  1414.    VOID FAR MDIDocumentHide (pinst)
  1415.  
  1416.       PINSTDOCUMENT pinst;             // -->document instance data
  1417.  
  1418.    // Define function data
  1419.  
  1420.      {PINSTDESKTOP pinstDesktop;       // -->desktop instance data
  1421.       PINSTDOCUMENT pinstDocument;     // -->next active document instance data
  1422.       HWND hwndFrame;                  // Next active document frame
  1423.       MENUITEM mi;                     // Menu item
  1424.  
  1425.    // Locate the desktop instance data
  1426.  
  1427.       pinstDesktop = (PINSTDESKTOP) WinQueryWindowULong (pinst->hwndDesktop, QWL_USER + sizeof (PVOID));
  1428.  
  1429.    // Delete document entry from window submenu
  1430.  
  1431.       WinSendMsg (pinstDesktop->miWindow.hwndSubMenu, MM_DELETEITEM,
  1432.        MPFROM2SHORT (pinst->idFrame, FALSE), NULL);
  1433.  
  1434.    // If there is a next entry in the window submenu, activate it
  1435.  
  1436.       if ((mi.id = (SHORT) WinSendMsg (pinstDesktop->miWindow.hwndSubMenu, MM_ITEMIDFROMPOSITION,
  1437.        MPFROMSHORT (CMD_WINDOW_MAX - CMD_WINDOW), NULL)) != MID_ERROR)
  1438.         {hwndFrame = WinWindowFromID (pinstDesktop->hwndDesktop, mi.id);
  1439.          WinSetWindowPos (hwndFrame, HWND_TOP, 0, 0, 0, 0, SWP_ACTIVATE | SWP_ZORDER);
  1440.          pinstDocument = (PINSTDOCUMENT) WinQueryWindowULong (WinWindowFromID (hwndFrame, FID_CLIENT),
  1441.            QWL_USER + sizeof (PVOID));
  1442.          if (pinstDocument)
  1443.             MDIDocumentActivate (pinstDocument, TRUE);
  1444.         }
  1445.  
  1446.    // Otherwise, remove any maximized child system menu and reset the desktop
  1447.    // In addition, we disable the HIDE and NEW window menu options
  1448.  
  1449.       else
  1450.         {WinSendMsg (pinstDesktop->hwndMenu, MM_REMOVEITEM,
  1451.            MPFROM2SHORT (MENU_MAXCHILD_SYSMENU, FALSE), NULL);
  1452.          WinSetWindowText (pinstDesktop->hwndFrame, pinstDesktop->szTitle);
  1453.          pinstDesktop->hwndDocument = NULL;
  1454.          WinSendMsg (pinstDesktop->miWindow.hwndSubMenu, MM_SETITEMATTR,
  1455.            MPFROM2SHORT (CMD_HIDE, FALSE), MPFROM2SHORT (MIA_DISABLED, MIA_DISABLED));
  1456.          WinSendMsg (pinstDesktop->miWindow.hwndSubMenu, MM_SETITEMATTR,
  1457.            MPFROM2SHORT (CMD_NEW_DOCUMENT, FALSE), MPFROM2SHORT (MIA_DISABLED, MIA_DISABLED));
  1458.         }
  1459.  
  1460.    // Hide document
  1461.  
  1462.       WinShowWindow (pinst->hwndFrame, FALSE);
  1463.       pinst->usState |= SWP_HIDE;
  1464.  
  1465.    // Enable the UNHIDE window submenu option
  1466.  
  1467.       WinSendMsg (pinstDesktop->miWindow.hwndSubMenu, MM_SETITEMATTR,
  1468.         MPFROM2SHORT (CMD_UNHIDE, FALSE), MPFROM2SHORT (MIA_DISABLED, FALSE));
  1469.  
  1470.    // Re-tile or re-cascade if necessary
  1471.  
  1472.       if (pinstDesktop->fTileAlways)
  1473.          MDIDocumentArrange (pinstDesktop->hwndDesktop, AWP_TILED);
  1474.       else if (pinstDesktop->fCascadeAlways)
  1475.          MDIDocumentArrange (pinstDesktop->hwndDesktop, AWP_CASCADED);
  1476.  
  1477.      }
  1478.  
  1479.  
  1480. // Function: MDIDocumentMinMax - Minimize/Maximize MDI Document
  1481. // ------------------------------------------------------------
  1482.    MRESULT FAR MDIDocumentMinMax (pinst, pswp)
  1483.  
  1484.       PINSTDOCUMENT pinst;             // -->document instance data
  1485.       PSWP pswp;                       // -->document position information
  1486.  
  1487.    // Define function data
  1488.  
  1489.      {PINSTDESKTOP pinstDesktop;       // -->desktop instance data
  1490.       CHAR szTitle[MAX_STRING];        // Desktop title
  1491.       MENUITEM mi;                     // Menu item
  1492.  
  1493.    // Update document state flags
  1494.  
  1495.       pinst->usState = pswp->fs & (SWP_MAXIMIZE | SWP_MINIMIZE);
  1496.  
  1497.    // Show/hide all frame controls
  1498.  
  1499.       WinSetParent (pinst->hwndSysMenu, (pinst->usState == SWP_MAXIMIZE)?
  1500.         HWND_OBJECT : pinst->hwndFrame, FALSE);
  1501.       WinSetParent (pinst->hwndMinMax, (pinst->usState == SWP_MAXIMIZE)?
  1502.         HWND_OBJECT : pinst->hwndFrame, FALSE);
  1503.       WinSetParent (pinst->hwndTitleBar, (pinst->usState == SWP_MAXIMIZE)?
  1504.         HWND_OBJECT : pinst->hwndFrame, FALSE);
  1505.       WinSendMsg (pinst->hwndFrame, WM_UPDATEFRAME, NULL, NULL);
  1506.  
  1507.    // Locate the desktop instance data
  1508.  
  1509.       pinstDesktop = (PINSTDESKTOP) WinQueryWindowULong (pinst->hwndDesktop, QWL_USER + sizeof (PVOID));
  1510.  
  1511.    // For maximized documents, insert the maximized child system
  1512.    // menu to the desktop if it does not already exist, and update
  1513.    // the desktop title
  1514.  
  1515.       if (pinst->usState == SWP_MAXIMIZE)
  1516.         {if (! (BOOL) WinSendMsg (pinstDesktop->hwndMenu, MM_QUERYITEM,
  1517.            MPFROM2SHORT (MENU_MAXCHILD_SYSMENU, FALSE), (MPARAM) &mi))
  1518.             WinSendMsg (pinstDesktop->hwndMenu, MM_INSERTITEM,
  1519.               (MPARAM) &(pinstDesktop->miSysMenu), NULL);
  1520.          if (pinst->iTitle == 0)
  1521.             sprintf (szTitle, "%s - %s", pinstDesktop->szTitle, pinst->szTitle);
  1522.          else sprintf (szTitle, "%s - %s:%d", pinstDesktop->szTitle, pinst->szTitle, pinst->iTitle);
  1523.          WinSetWindowText (pinstDesktop->hwndFrame, szTitle);
  1524.         }
  1525.  
  1526.    // Otherwise, remove the maximized child system menu from the desktop
  1527.    // and reset the desktop title
  1528.  
  1529.       else
  1530.         {WinSendMsg (pinstDesktop->hwndMenu, MM_REMOVEITEM,
  1531.            MPFROM2SHORT (MENU_MAXCHILD_SYSMENU, FALSE), NULL);
  1532.          WinSetWindowText (pinstDesktop->hwndFrame, pinstDesktop->szTitle);
  1533.         }
  1534.  
  1535.    // Indicate min/max handled
  1536.  
  1537.       return (MRESULT) NULL;
  1538.  
  1539.      }
  1540.  
  1541.  
  1542. // Function: MDIDocumentPaint - Paint MDI Document
  1543. // -----------------------------------------------
  1544.    MRESULT FAR MDIDocumentPaint (pinst)
  1545.  
  1546.       PINSTDOCUMENT pinst;             // -->document instance data
  1547.  
  1548.    // Define function data
  1549.  
  1550.      {HPS hps;                         // Presentation space
  1551.       RECTL rcl;                       // Update rectangle
  1552.  
  1553.    // Paint a white background
  1554.  
  1555.       hps = WinBeginPaint (pinst->hwndDocument, NULL, &rcl);
  1556.       WinFillRect (hps, &rcl, CLR_WHITE);
  1557.       WinEndPaint (hps);
  1558.  
  1559.    // Indicate paint handled
  1560.  
  1561.       return (MRESULT) NULL;
  1562.  
  1563.      }
  1564.  
  1565.  
  1566. // Function: MDIDocumentTitlesRebuild - Rebuild the titles of all Documents
  1567. // ------------------------------------------------------------------------
  1568.    PINSTDOCUMENT FAR MDIDocumentTitlesRebuild (pinst, usCommand)
  1569.  
  1570.       PINSTDOCUMENT pinst;             // -->document instance data
  1571.       USHORT usCommand;                // INSERT_TITLE or REMOVE_TITLE
  1572.  
  1573.    // Define function data
  1574.  
  1575.      {HWND hwndDocument;               // Document window
  1576.       HWND hwndFrame;                  // Its frame
  1577.       CHAR szClassName[MAX_STRING];    // Class/Title name
  1578.       PINSTDOCUMENT pinstFirst;        // -->first document instance data
  1579.       PINSTDOCUMENT pinstDocument;     // -->document instance data
  1580.       PINSTDESKTOP pinstDesktop;       // -->desktop instance data
  1581.       MENUITEM mi;                     // Menu item
  1582.       SHORT cmd;                       // Which window to retrieve
  1583.       SHORT nSameName;                 // Number of documents with the same name
  1584.       SHORT nDocuments;                // Number of documents
  1585.       SHORT iDocument;                 // Document index
  1586.       BOOL fListed;                    // TRUE == at least one document listed
  1587.  
  1588.    // Find the desktop instance data
  1589.  
  1590.       pinstDesktop = (PINSTDESKTOP) WinQueryWindowULong (pinst->hwndDesktop, QWL_USER + sizeof (PVOID));
  1591.  
  1592.    // Count the number of documents that already exist with the same name
  1593.    // as the current document and the total number of documents
  1594.  
  1595.       cmd = QW_TOP;
  1596.       hwndFrame = pinst->hwndDesktop;
  1597.       nDocuments = nSameName = 0;
  1598.       while ((hwndFrame = WinQueryWindow (hwndFrame, cmd, FALSE)) != NULL)
  1599.         {cmd = QW_NEXT;
  1600.          WinQueryClassName (hwndFrame, sizeof (szClassName), szClassName);
  1601.          if (strcmpi (szClassName, "#1") != 0) continue;
  1602.          hwndDocument = WinWindowFromID (hwndFrame, FID_CLIENT);
  1603.          WinQueryClassName (hwndDocument, sizeof (szClassName), szClassName);
  1604.          if (strcmpi (szClassName, MDI_DOCUMENTCLASS) != 0) continue;
  1605.          pinstDocument = (PINSTDOCUMENT) WinQueryWindowULong (hwndDocument, QWL_USER + sizeof (PVOID));
  1606.          nDocuments++;
  1607.          if (pinst == pinstDocument) continue;
  1608.          if (strcmp (pinst->szTitle, pinstDocument->szTitle) == 0)
  1609.             nSameName++;
  1610.         }
  1611.       if (usCommand == REMOVE_TITLE) nDocuments--;
  1612.  
  1613.    // If there is exactly one document, insert the window submenu
  1614.  
  1615.       if ((nDocuments == 1) && (usCommand == INSERT_TITLE))
  1616.          WinSendMsg (pinstDesktop->hwndMenu, MM_INSERTITEM, (MPARAM) &(pinstDesktop->miWindow),
  1617.           (MPARAM) pinstDesktop->pszWindow);
  1618.  
  1619.    // If there are no documents left, remove the window submenu
  1620.  
  1621.       else if ((nDocuments == 0) && (usCommand == REMOVE_TITLE))
  1622.          WinSendMsg (pinstDesktop->hwndMenu, MM_REMOVEITEM, MPFROM2SHORT (MENU_WINDOW, FALSE), NULL);
  1623.  
  1624.    // Remove all the document entries in the window submenu
  1625.  
  1626.       for (iDocument = 0; iDocument <= nDocuments; iDocument++)
  1627.         {WinSendMsg (pinstDesktop->miWindow.hwndSubMenu, MM_DELETEITEM,
  1628.            MPFROM2SHORT (CMD_SELECT_DOCUMENT + iDocument, FALSE), NULL);
  1629.         }
  1630.  
  1631.    // Scan each document window in the desktop
  1632.  
  1633.       cmd = QW_TOP;
  1634.       hwndFrame = pinst->hwndDesktop;
  1635.       iDocument = -1;
  1636.       pinstFirst = NULL;
  1637.       fListed = FALSE;
  1638.       while ((hwndFrame = WinQueryWindow (hwndFrame, cmd, FALSE)) != NULL)
  1639.         {cmd = QW_NEXT;
  1640.          WinQueryClassName (hwndFrame, sizeof (szClassName), szClassName);
  1641.          if (strcmpi (szClassName, "#1") != 0) continue;
  1642.          hwndDocument = WinWindowFromID (hwndFrame, FID_CLIENT);
  1643.          WinQueryClassName (hwndDocument, sizeof (szClassName), szClassName);
  1644.          if (strcmpi (szClassName, MDI_DOCUMENTCLASS) != 0) continue;
  1645.          pinstDocument = (PINSTDOCUMENT) WinQueryWindowULong (hwndDocument, QWL_USER + sizeof (PVOID));
  1646.  
  1647.       // Reset the id of this document
  1648.  
  1649.          if ((pinst == pinstDocument) && (usCommand == REMOVE_TITLE))
  1650.            {WinSetWindowUShort (pinstDocument->hwndFrame, QWS_ID, CMD_SELECT_DOCUMENT - 1);
  1651.             continue;
  1652.            }
  1653.          iDocument++;
  1654.          pinstDocument->idFrame = CMD_SELECT_DOCUMENT + iDocument;
  1655.          WinSetWindowUShort (pinstDocument->hwndFrame, QWS_ID, pinstDocument->idFrame);
  1656.  
  1657.       // If document titles match, re-number title
  1658.  
  1659.          if (strcmp (pinst->szTitle, pinstDocument->szTitle) == 0)
  1660.  
  1661.          // If we are inserting the current title, re-number apropriately
  1662.  
  1663.            {if (usCommand == INSERT_TITLE)
  1664.               {if (nSameName == 0)
  1665.                   pinstDocument->iTitle = 0;
  1666.                else if (nSameName == 1)
  1667.                  {pinstDocument->iTitle = (pinst == pinstDocument)? 2 : 1;
  1668.                   sprintf (szClassName, "%s:%d", pinstDocument->szTitle, pinstDocument->iTitle);
  1669.                   WinSetWindowText (pinstDocument->hwndFrame, szClassName);
  1670.                  }
  1671.                else if (pinst == pinstDocument)
  1672.                  {pinstDocument->iTitle = nSameName + 1;
  1673.                   sprintf (szClassName, "%s:%d", pinstDocument->szTitle, pinstDocument->iTitle);
  1674.                   WinSetWindowText (pinstDocument->hwndFrame, szClassName);
  1675.                  }
  1676.               }
  1677.  
  1678.          // If we are removing the current title, re-number apropriately
  1679.  
  1680.             else if (usCommand == REMOVE_TITLE)
  1681.               {if (nSameName == 1)
  1682.                  {pinstDocument->iTitle = 0;
  1683.                   sprintf (szClassName, "%s", pinstDocument->szTitle);
  1684.                   WinSetWindowText (pinstDocument->hwndFrame, szClassName);
  1685.                  }
  1686.                else if ((nSameName > 1) && (pinstDocument->iTitle > pinst->iTitle))
  1687.                  {pinstDocument->iTitle--;
  1688.                   sprintf (szClassName, "%s:%d", pinstDocument->szTitle, pinstDocument->iTitle);
  1689.                   WinSetWindowText (pinstDocument->hwndFrame, szClassName);
  1690.                  }
  1691.               }
  1692.  
  1693.          // Save a pointer to the instance data of the first document
  1694.          // with the same name
  1695.  
  1696.             if (pinstDocument->iTitle <= 1)
  1697.                pinstFirst = pinstDocument;
  1698.            }
  1699.  
  1700.       // Put document title into the window submenu if it is not hidden
  1701.  
  1702.          if (! (pinstDocument->usState & SWP_HIDE))
  1703.            {mi.id = CMD_SELECT_DOCUMENT + iDocument;
  1704.             mi.iPosition = MIT_END;
  1705.             mi.afAttribute = 0;
  1706.             mi.hwndSubMenu = NULL;
  1707.             mi.hItem = NULL;
  1708.             mi.afStyle = MIS_TEXT;
  1709.             if (pinstDocument->iTitle == 0)
  1710.                sprintf (szClassName, "%s", pinstDocument->szTitle);
  1711.             else sprintf (szClassName, "%s:%d", pinstDocument->szTitle, pinstDocument->iTitle);
  1712.             WinSendMsg (pinstDesktop->miWindow.hwndSubMenu, MM_INSERTITEM, (MPARAM) &mi, (MPARAM) szClassName);
  1713.             fListed = TRUE;
  1714.            }
  1715.  
  1716.       // Loop until all windows processed
  1717.  
  1718.         }
  1719.  
  1720.    // If at least one document is listed in the window submenu, enable
  1721.    // the HIDE and NEW window submenu options
  1722.  
  1723.       WinSendMsg (pinstDesktop->miWindow.hwndSubMenu, MM_SETITEMATTR,
  1724.         MPFROM2SHORT (CMD_HIDE, FALSE), MPFROM2SHORT (MIA_DISABLED, FALSE));
  1725.       WinSendMsg (pinstDesktop->miWindow.hwndSubMenu, MM_SETITEMATTR,
  1726.         MPFROM2SHORT (CMD_NEW_DOCUMENT, FALSE), MPFROM2SHORT (MIA_DISABLED, FALSE));
  1727.  
  1728.    // Return pointer to instance data of first document with the same name
  1729.  
  1730.       return pinstFirst;
  1731.  
  1732.      }
  1733.  
  1734.  
  1735. // Function: MDIDocumentUnhideDlgProc - MDI Document Unhide dialog procedure
  1736. // -------------------------------------------------------------------------
  1737.    MRESULT EXPENTRY MDIDocumentUnhideDlgProc (hwndUnhide, msg, mp1, mp2)
  1738.  
  1739.       HWND hwndUnhide;                 // Unhide dialog box
  1740.       USHORT msg;                      // PM message
  1741.       MPARAM mp1;                      // Message parameter 1
  1742.       MPARAM mp2;                      // Message parameter 2
  1743.  
  1744.    // Define function data
  1745.  
  1746.      {HWND hwndDocument;               // Document window
  1747.       HWND hwndFrame;                  // Its frame
  1748.       HWND hwndFirst;                  // First document unhidden
  1749.       CHAR szClassName[MAX_STRING];    // Class/Title name
  1750.       CHAR szTitle[MAX_STRING];        // Window title
  1751.       PINSTDESKTOP pinstDesktop;       // -->desktop instance data
  1752.       PINSTDOCUMENT pinstDocument;     // -->document instance data
  1753.       MENUITEM mi;                     // Menu item
  1754.       SHORT cmd;                       // Which window to retrieve
  1755.       SHORT nItems, nUnhidden;         // Number of items, number unhidden
  1756.       SHORT i;                         // Temporary counter
  1757.  
  1758.    // Analyze and process message
  1759.  
  1760.       switch (msg)
  1761.  
  1762.       // Initially, load list box with all hidden window titles
  1763.  
  1764.         {case WM_INITDLG:
  1765.             pinstDesktop = (PINSTDESKTOP) mp2;
  1766.             WinSetWindowULong (hwndUnhide, QWL_USER, (ULONG) pinstDesktop);
  1767.  
  1768.          // Scan each document window in the desktop
  1769.  
  1770.             cmd = QW_TOP;
  1771.             hwndFrame = pinstDesktop->hwndDesktop;
  1772.             while ((hwndFrame = WinQueryWindow (hwndFrame, cmd, FALSE)) != NULL)
  1773.               {cmd = QW_NEXT;
  1774.                WinQueryClassName (hwndFrame, sizeof (szClassName), szClassName);
  1775.                if (strcmpi (szClassName, "#1") != 0) continue;
  1776.                hwndDocument = WinWindowFromID (hwndFrame, FID_CLIENT);
  1777.                WinQueryClassName (hwndDocument, sizeof (szClassName), szClassName);
  1778.                if (strcmpi (szClassName, MDI_DOCUMENTCLASS) != 0) continue;
  1779.                pinstDocument = (PINSTDOCUMENT) WinQueryWindowULong (hwndDocument, QWL_USER + sizeof (PVOID));
  1780.  
  1781.             // If the document is hidden, add its title to the list box
  1782.  
  1783.                if (pinstDocument->usState & SWP_HIDE)
  1784.                  {if (pinstDocument->iTitle == 0)
  1785.                      sprintf (szClassName, "%s", pinstDocument->szTitle);
  1786.                   else sprintf (szClassName, "%s:%d", pinstDocument->szTitle, pinstDocument->iTitle);
  1787.                   WinSendMsg (WinWindowFromID (hwndUnhide, DIALOG_UNHIDE_LISTBOX),
  1788.                     LM_INSERTITEM, MPFROMSHORT (LIT_SORTASCENDING), (MPARAM) szClassName);
  1789.                  }
  1790.  
  1791.             // Loop until all documents processed
  1792.  
  1793.               }
  1794.  
  1795.          // Indicate initialize handled
  1796.  
  1797.             return (MRESULT) NULL;
  1798.  
  1799.       // When OK hit, unhide all selected entries
  1800.  
  1801.          case WM_COMMAND:
  1802.             if (SHORT1FROMMP (mp1) == DID_OK)
  1803.  
  1804.          // Scan each selected entry in the list box
  1805.  
  1806.               {i = (SHORT) WinSendDlgItemMsg (hwndUnhide, DIALOG_UNHIDE_LISTBOX,
  1807.                  LM_QUERYSELECTION, MPFROMSHORT (LIT_FIRST), NULL);
  1808.                nItems = (SHORT) WinSendDlgItemMsg (hwndUnhide, DIALOG_UNHIDE_LISTBOX,
  1809.                  LM_QUERYITEMCOUNT, NULL, NULL);
  1810.                nUnhidden = 0;
  1811.                hwndFirst = NULL;
  1812.                pinstDesktop = (PINSTDESKTOP) WinQueryWindowULong (hwndUnhide, QWL_USER);
  1813.                while (i != LIT_NONE)
  1814.                  {WinSendDlgItemMsg (hwndUnhide, DIALOG_UNHIDE_LISTBOX,
  1815.                     LM_QUERYITEMTEXT, MPFROM2SHORT (i, sizeof (szClassName)), (MPARAM) szTitle);
  1816.  
  1817.                // Scan each document window in the desktop
  1818.  
  1819.                   cmd = QW_TOP;
  1820.                   hwndFrame = pinstDesktop->hwndDesktop;
  1821.                   while ((hwndFrame = WinQueryWindow (hwndFrame, cmd, FALSE)) != NULL)
  1822.                     {cmd = QW_NEXT;
  1823.                      WinQueryClassName (hwndFrame, sizeof (szClassName), szClassName);
  1824.                      if (strcmpi (szClassName, "#1") != 0) continue;
  1825.                      hwndDocument = WinWindowFromID (hwndFrame, FID_CLIENT);
  1826.                      WinQueryClassName (hwndDocument, sizeof (szClassName), szClassName);
  1827.                      if (strcmpi (szClassName, MDI_DOCUMENTCLASS) != 0) continue;
  1828.                      pinstDocument = (PINSTDOCUMENT) WinQueryWindowULong (hwndDocument, QWL_USER + sizeof (PVOID));
  1829.  
  1830.                   // If the document is hidden, extract its title
  1831.  
  1832.                      if (pinstDocument->usState & SWP_HIDE)
  1833.                        {if (pinstDocument->iTitle == 0)
  1834.                            sprintf (szClassName, "%s", pinstDocument->szTitle);
  1835.                         else sprintf (szClassName, "%s:%d", pinstDocument->szTitle, pinstDocument->iTitle);
  1836.  
  1837.                      // If title matches select list box entry, unhide document
  1838.                      // and put its entry back in the window menu
  1839.  
  1840.                         if (strcmp (szClassName, szTitle) == 0)
  1841.                           {pinstDocument->usState &= ~SWP_HIDE;
  1842.                            mi.id = pinstDocument->idFrame;
  1843.                            mi.iPosition = MIT_END;
  1844.                            mi.afAttribute = 0;
  1845.                            mi.hwndSubMenu = NULL;
  1846.                            mi.hItem = NULL;
  1847.                            mi.afStyle = MIS_TEXT;
  1848.                            WinSendMsg (pinstDesktop->miWindow.hwndSubMenu,
  1849.                              MM_INSERTITEM, (MPARAM) &mi, (MPARAM) szClassName);
  1850.                            WinShowWindow (pinstDocument->hwndFrame, TRUE);
  1851.                            if (hwndFirst == NULL)
  1852.                               hwndFirst = pinstDocument->hwndFrame;
  1853.                            nUnhidden++;
  1854.                            break;
  1855.                           }
  1856.                        }
  1857.  
  1858.                   // Loop until all documents scanned
  1859.  
  1860.                     }
  1861.  
  1862.                // Loop until all list box entries processed
  1863.  
  1864.                   i = (SHORT) WinSendDlgItemMsg (hwndUnhide, DIALOG_UNHIDE_LISTBOX,
  1865.                     LM_QUERYSELECTION, MPFROMSHORT (i), NULL);
  1866.                  }
  1867.  
  1868.             // If all entries have been unhidden, disable the
  1869.             // UNHIDE window submenu option
  1870.  
  1871.                if (nItems == nUnhidden)
  1872.                   WinSendMsg (pinstDesktop->miWindow.hwndSubMenu, MM_SETITEMATTR,
  1873.                     MPFROM2SHORT (CMD_UNHIDE, FALSE), MPFROM2SHORT (MIA_DISABLED, MIA_DISABLED));
  1874.  
  1875.             // If any entries at all have been unhidden, enable the
  1876.             // HIDE and NEW window submenu options
  1877.  
  1878.                WinSendMsg (pinstDesktop->miWindow.hwndSubMenu, MM_SETITEMATTR,
  1879.                  MPFROM2SHORT (CMD_HIDE, FALSE), MPFROM2SHORT (MIA_DISABLED, FALSE));
  1880.                WinSendMsg (pinstDesktop->miWindow.hwndSubMenu, MM_SETITEMATTR,
  1881.                  MPFROM2SHORT (CMD_NEW_DOCUMENT, FALSE), MPFROM2SHORT (MIA_DISABLED, FALSE));
  1882.  
  1883.             // Re-tile or re-cascade if necessary
  1884.  
  1885.                if (pinstDesktop->fTileAlways)
  1886.                   MDIDocumentArrange (pinstDesktop->hwndDesktop, AWP_TILED);
  1887.                else if (pinstDesktop->fCascadeAlways)
  1888.                   MDIDocumentArrange (pinstDesktop->hwndDesktop, AWP_CASCADED);
  1889.  
  1890.             // Activate the first document unhidden
  1891.  
  1892.                if (hwndFirst != NULL)
  1893.                   WinSetWindowPos (hwndFirst, HWND_TOP, 0, 0, 0, 0, SWP_ACTIVATE | SWP_ZORDER);
  1894.  
  1895.               }
  1896.  
  1897.          // Indicate dialog box terminated
  1898.  
  1899.             WinDismissDlg (hwndUnhide, SHORT1FROMMP (mp1));
  1900.             return (MRESULT) NULL;
  1901.  
  1902.         }
  1903.  
  1904.    // Return to PM
  1905.  
  1906.       return WinDefDlgProc (hwndUnhide, msg, mp1, mp2);
  1907.  
  1908.      }
  1909.  
  1910.  
  1911. // Function: MDIDocumentWndProc - MDI Document window procedure
  1912. // ------------------------------------------------------------
  1913.    MRESULT EXPENTRY MDIDocumentWndProc (hwndDocument, msg, mp1, mp2)
  1914.  
  1915.       HWND hwndDocument;               // Document window
  1916.       USHORT msg;                      // PM message
  1917.       MPARAM mp1;                      // Message parameter 1
  1918.       MPARAM mp2;                      // Message parameter 2
  1919.  
  1920.    // Define function data
  1921.  
  1922.      {PINSTDOCUMENT pinst;             // -->instance data
  1923.       PINSTDESKTOP pinstDesktop;       // -->desktop instance data
  1924.  
  1925.    // Locate instance data
  1926.  
  1927.       pinst = (PINSTDOCUMENT) WinQueryWindowULong (hwndDocument, QWL_USER + sizeof (PVOID));
  1928.  
  1929.    // Analyze and process message
  1930.  
  1931.       switch (msg)
  1932.  
  1933.         {case WM_CLOSE:
  1934.             return MDIDocumentClose (pinst);
  1935.  
  1936.          case WM_COMMAND:
  1937.             return MDIDocumentCommand (pinst, LOUSHORT (mp1));
  1938.  
  1939.          case WM_CREATE:
  1940.             return MDIDocumentCreate (hwndDocument, (PDOCUMENTCREATEPARMS) mp1);
  1941.  
  1942.          case WM_DESTROY:
  1943.             return MDIDocumentDestroy (pinst);
  1944.  
  1945.          case WM_MINMAXFRAME:
  1946.             return MDIDocumentMinMax (pinst, (PSWP) mp1);
  1947.  
  1948.          case WM_MOVE:
  1949.             return (MRESULT) !WinQueryWindowPos (pinst->hwndFrame, &(pinst->swp));
  1950.  
  1951.          case WM_PAINT:
  1952.             return MDIDocumentPaint (pinst);
  1953.  
  1954.          case WM_SETFOCUS:
  1955.             pinstDesktop = (PINSTDESKTOP) WinQueryWindowULong (pinst->hwndDesktop, QWL_USER + sizeof (PVOID));
  1956.             if ((WinIsChild ((HWND) mp1, pinst->hwndFrame)
  1957.              || ((HWND) mp1 == pinstDesktop->hwndMenu)) && ! (BOOL) mp2)
  1958.                return (MRESULT) NULL;
  1959.             return MDIDocumentActivate (pinst, (BOOL) mp2);
  1960.  
  1961.          case WM_SIZE:
  1962.             return (MRESULT) !WinQueryWindowPos (pinst->hwndFrame, &(pinst->swp));
  1963.  
  1964.          case MDI_MSG_LOCATE_DOCUMENT_DATA:
  1965.             return (MRESULT) pinst->pvDocData;
  1966.  
  1967.          case MDI_MSG_LOCATE_INSTANCE_DATA:
  1968.             return (MRESULT) pinst->pvInstData;
  1969.  
  1970.         }
  1971.  
  1972.    // Return to PM
  1973.  
  1974.       return WinDefWindowProc (hwndDocument, msg, mp1, mp2);
  1975.  
  1976.      }
  1977.  
  1978.  
  1979. // Function: MDIInitialize - Initialize MDI processing
  1980. // ---------------------------------------------------
  1981.    VOID EXPENTRY MDIInitialize (hab)
  1982.  
  1983.       HAB hab;                         // Application anchor block
  1984.  
  1985.    // Define function data
  1986.  
  1987.      {static DOSFSRSEM dosfsrs;        // Semaphore to block registration
  1988.       static BOOL fRegistered = FALSE; // TRUE if already registered
  1989.  
  1990.    // Block the actions that follow such that only one process at a
  1991.    // time executes them.
  1992.  
  1993.       DosFSRamSemRequest (&dosfsrs, SEM_INDEFINITE_WAIT);
  1994.  
  1995.    // Once only, perform initialization.
  1996.  
  1997.       if (! fRegistered)
  1998.  
  1999.       // Register MDI Desktop window class
  2000.  
  2001.         {WinRegisterClass (hab, MDI_DESKTOPCLASS, MDIDesktopWndProc,
  2002.            /*CS_PUBLIC |*/ CS_SIZEREDRAW | CS_MOVENOTIFY, cbExtraData);
  2003.  
  2004.       // Register MDI Document window class
  2005.  
  2006.          WinRegisterClass (hab, MDI_DOCUMENTCLASS, MDIDocumentWndProc,
  2007.            /*CS_PUBLIC |*/ CS_SIZEREDRAW | CS_MOVENOTIFY, cbExtraData);
  2008.  
  2009.       // Indicate initialization complete
  2010.  
  2011.        /*fRegistered = TRUE;*/
  2012.         }
  2013.  
  2014.    // Release the block and return
  2015.  
  2016.       DosFSRamSemClear (&dosfsrs);
  2017.      }
  2018.  
  2019.  
  2020. // Function: MDILoadAccelTable - Load an MDI Accelerator Table
  2021. // -----------------------------------------------------------
  2022.    HACCEL FAR MDILoadAccelTable (idAccelTable)
  2023.  
  2024.       USHORT idAccelTable;             // Accelerator table id
  2025.  
  2026.    // Define function data
  2027.  
  2028.      {HACCEL haccel;                   // Accelerator table
  2029.       HMODULE hmod;                    // DLL module handle
  2030.       CHAR szDLL[MAX_STRING];          // DLL name
  2031.       PCHAR pszDot;                    // Temporary pointer
  2032.  
  2033.    // Extract the DLL name from our name
  2034.  
  2035.       strcpy (szDLL, MDI_MODNAME);
  2036.       if ((pszDot = strrchr (szDLL, '.')) != NULL)
  2037.         *pszDot = 0;
  2038.  
  2039.    // Load accelerator table
  2040.  
  2041.       DosLoadModule (szDLL, sizeof (szDLL), szDLL, &hmod);
  2042.       haccel = WinLoadAccelTable (NULL, hmod, idAccelTable);
  2043.       DosFreeModule (hmod);
  2044.  
  2045.    // Return handle
  2046.  
  2047.       return haccel;
  2048.  
  2049.      }
  2050.  
  2051.  
  2052. // Function: MDILoadDialog - Load an MDI Dialog Box
  2053. // ------------------------------------------------
  2054.    HWND FAR MDILoadDialog (hwndParent, pfnDlgProc, idDialog, pCreateParms)
  2055.  
  2056.       HWND hwndParent;                 // Dialog box parent
  2057.       PFNWP pfnDlgProc;                // Dialog box procedure
  2058.       USHORT idDialog;                 // Dialog box id
  2059.       PVOID pCreateParms;              // Creation parameters
  2060.  
  2061.    // Define function data
  2062.  
  2063.      {HWND hdlg;                       // Dialog box
  2064.       HMODULE hmod;                    // DLL module handle
  2065.       CHAR szDLL[MAX_STRING];          // DLL name
  2066.       PCHAR pszDot;                    // Temporary pointer
  2067.  
  2068.    // Extract the DLL name from our name
  2069.  
  2070.       strcpy (szDLL, MDI_MODNAME);
  2071.       if ((pszDot = strrchr (szDLL, '.')) != NULL)
  2072.         *pszDot = 0;
  2073.  
  2074.    // Load dialog box
  2075.  
  2076.       DosLoadModule (szDLL, sizeof (szDLL), szDLL, &hmod);
  2077.       hdlg = WinLoadDlg (HWND_DESKTOP, hwndParent, pfnDlgProc, hmod, idDialog, pCreateParms);
  2078.       DosFreeModule (hmod);
  2079.  
  2080.    // Return handle
  2081.  
  2082.       return hdlg;
  2083.  
  2084.      }
  2085.  
  2086.  
  2087. // Function: MDILoadMenu - Load an MDI Menu
  2088. // ----------------------------------------
  2089.    HWND FAR MDILoadMenu (hwndParent, idMenu)
  2090.  
  2091.       HWND hwndParent;                 // Menu parent
  2092.       USHORT idMenu;                   // Menu identifier
  2093.  
  2094.    // Define function data
  2095.  
  2096.      {HWND hMenu;                      // Menu
  2097.       HMODULE hmod;                    // DLL module handle
  2098.       CHAR szDLL[MAX_STRING];          // DLL name
  2099.       SEL sel;                         // Menu template selector
  2100.       PVOID pvmt;                      // Menu template address
  2101.       PCHAR pszDot;                    // Temporary pointer
  2102.  
  2103.    // Extract the DLL name from our name
  2104.  
  2105.       strcpy (szDLL, MDI_MODNAME);
  2106.       if ((pszDot = strrchr (szDLL, '.')) != NULL)
  2107.         *pszDot = 0;
  2108.  
  2109.    // Load menu
  2110.  
  2111.       DosLoadModule (szDLL, sizeof (szDLL), szDLL, &hmod);
  2112.       DosGetResource (hmod, RT_MENU, idMenu, &sel);
  2113.       pvmt = MAKEP (sel, 0);
  2114.       hMenu = WinCreateWindow (hwndParent, WC_MENU, NULL, NULL,
  2115.           0, 0, 0, 0, hwndParent, HWND_BOTTOM, idMenu, pvmt, NULL);
  2116.       DosFreeSeg (sel);
  2117.       DosFreeModule (hmod);
  2118.  
  2119.    // Return handle
  2120.  
  2121.       return hMenu;
  2122.  
  2123.      }
  2124.