home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / os2sdk / os2sdk11 / tk4 / mdi / mdidoc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-02-20  |  35.0 KB  |  1,124 lines

  1. /***************************************************************************\
  2. * mdidoc.c - MDI application
  3. *
  4. * Created by Microsoft Corporation, 1989
  5. \***************************************************************************/
  6.  
  7. #define INCL_WINSYS
  8. #define INCL_WINCOMMON
  9. #define INCL_WINMESSAGEMGR
  10. #define INCL_WINFRAMEMGR
  11. #define INCL_WINPOINTERS
  12. #define INCL_WINMENUS
  13. #define INCL_WINWINDOWMGR
  14. #define INCL_WINACCELERATORS
  15. #define INCL_WININPUT
  16. #define INCL_WINHEAP
  17. #define INCL_WINSCROLLBARS
  18. #define INCL_WINRECTANGLES
  19. #define INCL_WINCOUNTRY
  20. #define INCL_WINDIALOGS
  21. #define INCL_GPIPRIMITIVES
  22. #define INCL_GPILOGCOLORTABLE
  23.  
  24. #include <os2.h>
  25. #include <string.h>
  26. #include <stdlib.h>
  27. #include <stdio.h>
  28. #include "app.h"
  29. #include "appdata.h"
  30. #include "mdi.h"
  31. #include "mdidata.h"
  32.  
  33. /* Function Prototypes */
  34. VOID BuildWindowMenu(VOID);
  35. VOID TrackSplitbars(HWND, USHORT, SHORT, SHORT);
  36. MRESULT MDIFormatFrame(HWND hwnd, PSWP aswp, MPARAM mp2);
  37. MRESULT MDIMinMaxFrame(HWND hwnd, MPARAM mp1, MPARAM mp2);
  38.  
  39. #define    WM_WINDOWPOSCHANGED    0x55
  40.  
  41. VOID SetMainTitleText(HWND hwndDocFrame)
  42. {
  43.     char szDocTitle[80], szMainTitle[80];
  44.  
  45.     /*
  46.      * Get the titlebar text for the specified document window.
  47.      */
  48.     WinQueryWindowText(hwndDocFrame, 80, (PSZ)szDocTitle);
  49.  
  50.     /*
  51.      * Build up the correct text for the main window.
  52.      */
  53.     strcpy(szMainTitle, " - ");
  54.     strcat(szMainTitle, szDocTitle);
  55.  
  56.     /*
  57.      * Set the main window's titlebar to the new text.
  58.      */
  59.     WinSetWindowText(hwndMDIFrame, szMainTitle);
  60. }
  61.  
  62.  
  63. VOID ClearMainTitleText(VOID)
  64. {
  65.     WinSetWindowText(hwndMDIFrame, "");
  66. }
  67.  
  68.  
  69. VOID AddToWindowMenu(NPDOC npdocNew)
  70. {
  71.     MENUITEM mi;
  72.     char szItemText[1];
  73.     char szTitleNew[128], szTitleCompare[128];
  74.     register NPDOC npdoc, npdocPrev;
  75.     USHORT usRes;
  76.  
  77.     cDocs++;
  78.  
  79.     /* build menuitem structure for this document */
  80.     mi.iPosition = MIT_END;
  81.     mi.afStyle = MIS_TEXT;
  82.     mi.afAttribute = 0;
  83.     mi.id = CMD_WINDOWITEMS + cDocs;
  84.     mi.hwndSubMenu = NULL;
  85.     mi.hItem = NULL;
  86.     szItemText[0] = '\0';
  87.  
  88.     /*
  89.      * Insert a blank item into the menu.
  90.      */
  91.     WinSendMsg(hwndWindowMenu, MM_INSERTITEM, MPFROMP(&mi),
  92.             MPFROMP(szItemText));
  93.  
  94.     /*
  95.      * Add the document to the DOCLIST.
  96.      */
  97.     if (npdocFirst == NULL) {
  98.         npdocFirst = npdocNew;
  99.  
  100.         /*
  101.          * Fill in the DOCLIST for this doc.
  102.          */
  103.         npdocNew->idMI = NULL;
  104.         npdocNew->npdocNext = NULL;
  105.  
  106.     } else {
  107.  
  108.         /*
  109.          * Add the item in the correct
  110.          * sorted location.
  111.          */
  112.         npdoc = npdocFirst;
  113.         npdocPrev = NULL;
  114.  
  115.         while (npdoc != NULL) {
  116.             WinQueryWindowText(npdoc->hwndFrame, 128, szTitleCompare);
  117.             WinQueryWindowText(npdocNew->hwndFrame, 128, szTitleNew);
  118.             WinUpper(NULL, NULL, NULL, szTitleCompare);
  119.             WinUpper(NULL, NULL, NULL, szTitleNew);
  120.  
  121.             usRes = WinCompareStrings(NULL, NULL, NULL, szTitleNew,
  122.                     szTitleCompare, NULL);
  123.  
  124.             if (usRes == WCS_LT) {
  125.                 if (npdocPrev == NULL) {
  126.                     npdocFirst = npdocNew;
  127.                     npdocFirst->npdocNext = npdoc;
  128.                 } else {
  129.                     npdocPrev->npdocNext = npdocNew;
  130.                     npdocNew->npdocNext = npdoc;
  131.                 }
  132.  
  133.                 /*
  134.                  * Fill in the DOC for this document.
  135.                  */
  136.                 npdocNew->idMI = NULL;
  137.                 break;
  138.             } else if (npdoc->npdocNext == NULL) {
  139.                 /*
  140.                  * If we've made it to the end,
  141.                  * just tack it on here.
  142.                  */
  143.                 npdoc->npdocNext =  npdocNew;
  144.  
  145.                 /*
  146.                  * Fill in the DOC for this document.
  147.                  */
  148.                 npdocNew->idMI = NULL;
  149.                 npdocNew->npdocNext = NULL;
  150.             }
  151.  
  152.             npdocPrev = npdoc;
  153.             npdoc = npdoc->npdocNext;
  154.         }
  155.     }
  156.  
  157.     BuildWindowMenu();
  158. }
  159.  
  160.  
  161. VOID RemoveFromWindowMenu(NPDOC npdocRemove)
  162. {
  163.     register NPDOC npdoc, npdocPrev;
  164.  
  165.     VOID BuildWindowMenu(VOID);
  166.  
  167.     /*
  168.      * If the window menu is gone no need to 
  169.      * do any of this stuff.  This can happen
  170.      * when the MDI app is closing.
  171.      */
  172.     if (!WinIsWindow(hab, hwndWindowMenu))
  173.         return;
  174.  
  175.     /* Delete item from menu */
  176.     WinSendMsg(hwndWindowMenu, MM_DELETEITEM,
  177.             MPFROM2SHORT(npdocRemove->idMI, FALSE), NULL);
  178.  
  179.     /*
  180.      * Remove the DOC from the linked-list.
  181.      */
  182.     if (npdocFirst != NULL) {
  183.  
  184.         /*
  185.          * Initialize these for our while loop.
  186.          */
  187.         npdoc = npdocFirst;
  188.         npdocPrev = NULL;
  189.  
  190.         while (npdoc != NULL) {
  191.  
  192.             /*
  193.              * If we've found the element unlink
  194.              * it from the list.
  195.              */
  196.             if (npdoc == npdocRemove) {
  197.  
  198.                 /*
  199.                  * Unlink this document.
  200.                  */
  201.                 if (npdocPrev != NULL)
  202.                     npdocPrev->npdocNext = npdoc->npdocNext;
  203.                 else
  204.                     npdocFirst = npdoc->npdocNext;
  205.  
  206.                 /*
  207.                  * Break out of the while loop,
  208.                  * we're done here.
  209.                  */
  210.                 break;
  211.             }
  212.             npdocPrev = npdoc;
  213.             npdoc = npdoc->npdocNext;
  214.         }
  215.     }
  216.  
  217.     BuildWindowMenu();
  218. }
  219.  
  220.  
  221. VOID BuildWindowMenu(VOID)
  222. {
  223.     register NPDOC npdoc;
  224.     char szDocTitle[80];
  225.     char szItemText[80];
  226.     USHORT idMI, posMI, cWindows;
  227.  
  228.     /*
  229.      * Get the position of the first
  230.      * window in the Window menu.
  231.      */
  232.     posMI = (USHORT)WinSendMsg(hwndWindowMenu, MM_ITEMPOSITIONFROMID,
  233.             MPFROM2SHORT(CMD_WINDOWITEMS, TRUE), NULL);
  234.     posMI++;
  235.  
  236.     npdoc = npdocFirst;
  237.     cWindows = 1;
  238.     while (npdoc != NULL) {
  239.  
  240.         /*
  241.          * Fill in the menuitem ID.
  242.          */
  243.         idMI = (USHORT)WinSendMsg(hwndWindowMenu, MM_ITEMIDFROMPOSITION,
  244.                 MPFROMSHORT(posMI), NULL);
  245.         npdoc->idMI = idMI;
  246.  
  247.         /*
  248.          * Build the item string.
  249.          */
  250.         szItemText[0] = '~';
  251.         itoa(cWindows, szItemText + 1, 10);
  252.         strcat(szItemText, " ");
  253.         WinQueryWindowText(npdoc->hwndFrame, 80, (PSZ)szDocTitle);
  254.         strcat(szItemText, szDocTitle);
  255.  
  256.         /*
  257.          * Set the item text for the menuitem.
  258.          */
  259.         WinSendMsg(hwndWindowMenu, MM_SETITEMTEXT, MPFROMSHORT(idMI),
  260.                 MPFROMP(szItemText));
  261.  
  262.         /*
  263.          * Clear out the previous attributes.
  264.          */
  265.         WinSendMsg(hwndWindowMenu, MM_SETITEMATTR, MPFROM2SHORT(idMI, FALSE),
  266.                 MPFROM2SHORT(MIA_CHECKED, 0));
  267.  
  268.         posMI++;
  269.         cWindows++;
  270.         npdoc = npdoc->npdocNext;
  271.     }
  272. }
  273.  
  274.  
  275. NPDOC MdiNewDocument(USHORT fsStyle, PSZ pszClassName)
  276. {
  277.     ULONG ctlData, flStyle;
  278.     HWND hwndNewFrame, hwndNewClient, hwndSysMenu;
  279.     register NPDOC npdocNew;
  280.     register NPVIEW npview;
  281.     char szDocTitle[80], szDocNumber[4];
  282.  
  283.     usDocNumber++;
  284.  
  285.     /*
  286.      * Setup the window's titlebar text.
  287.      */
  288.     itoa(usDocNumber, szDocNumber, 10);
  289.     strcpy(szDocTitle, "Untitled");
  290.     strcat(szDocTitle, szDocNumber);
  291.  
  292.     ctlData = FCF_TITLEBAR | FCF_MINMAX | FCF_SIZEBORDER |
  293.             FCF_VERTSCROLL | FCF_HORZSCROLL;
  294.  
  295.     hwndNewFrame = WinCreateStdWindow(hwndMDI,
  296.             FS_ICON | FS_ACCELTABLE,
  297.             (VOID FAR *)&ctlData,
  298.             pszClassName, szDocTitle,
  299.             WS_VISIBLE,
  300.             (HMODULE)NULL, IDR_MDIDOC,
  301.             (HWND FAR *)&hwndNewClient);
  302.  
  303.     npdocNew = (NPDOC)WinAllocMem(hHeap, sizeof(DOC));
  304.     if (npdocNew == NULL)
  305.         return (FALSE);
  306.  
  307.     npdocNew->hwndFrame = hwndNewFrame;
  308.     npdocNew->cxVertSplitPos = 0;
  309.     npdocNew->cyHorzSplitPos = 0;
  310.     npdocNew->fs = 0;
  311.     npdocNew->fsStyle = fsStyle;
  312.  
  313.     npview = NPVIEWFROMCLIENT(hwndNewClient);
  314.     npview->npdoc = npdocNew;
  315.  
  316.     /*
  317.      * Link in the VIEW for FID_CLIENT since we
  318.      * know it exists.
  319.      */
  320.     npdocNew->npviewFirst = npview;
  321.  
  322.     /*
  323.      * Create the 'splitbar' controls for the frame.
  324.      * This includes the splitbar and the extra client
  325.      * and scrollbar windows.  This routine will also
  326.      * link the appropriate VIEWs into the DOC structure.
  327.      */
  328.     if (fsStyle & (DS_HORZSPLITBAR | DS_VERTSPLITBAR)) {
  329.         if (CreateSplitbarWindows(hwndNewFrame, npdocNew) == FALSE) {
  330.             WinDestroyWindow(hwndNewFrame);
  331.             return (FALSE);
  332.         }
  333.     }
  334.  
  335.     /*
  336.      * Load in the document window's system menu.
  337.      */
  338.     hwndSysMenu = WinLoadMenu(hwndNewFrame, (HMODULE)NULL, IDM_DOCSYSMENU);
  339.  
  340.     /*
  341.      * Make it look like a normal system menu to the frame manager so
  342.      * gets formatted correctly etc.
  343.      */
  344.     flStyle = WinQueryWindowULong(hwndSysMenu, QWL_STYLE);
  345.     WinSetWindowULong(hwndSysMenu, QWL_STYLE, flStyle | MS_TITLEBUTTON);
  346.     WinSetWindowUShort(hwndSysMenu, QWS_ID, FID_SYSMENU);
  347.  
  348.     /*
  349.      * Set the bitmap to the SBMP_CHILDSYSMENU bitmap.
  350.      */
  351.     WinSendMsg(hwndSysMenu, MM_SETITEMHANDLE, (MPARAM)SC_DOCSYSMENU,
  352.             (MPARAM)hbmChildSysMenu);
  353.  
  354.     /*
  355.      * Set the menu window handles in the DOC structure so these
  356.      * controls can be hidden/shown at the correct time.
  357.      */
  358.     npdocNew->hwndSysMenu = hwndSysMenu;
  359.     npdocNew->hwndMinmax = WinWindowFromID(hwndNewFrame, FID_MINMAX);
  360.  
  361.     /*
  362.      * Subclass the frame so we can handle the accelerators
  363.      * and other MDI stuff.
  364.      */
  365.     pfnFrameWndProc = WinSubclassWindow(hwndNewFrame,
  366.             (PFNWP)DocFrameWndProc);
  367.  
  368.     /*
  369.      * Add the window to the Window menu.
  370.      */
  371.     AddToWindowMenu(npdocNew);
  372.  
  373.     return (npdocNew);
  374. }
  375.  
  376.  
  377. VOID MDISetInitialDocPos(HWND hwndNewFrame)
  378. {
  379.     WinSetWindowPos(hwndNewFrame, NULL, xNextNewDoc, yNextNewDoc,
  380.             cxNewDoc, cyNewDoc, SWP_MOVE | SWP_SIZE | SWP_SHOW);
  381.  
  382.     /*
  383.      * Insert logic to change xNextNewDoc/yNextNewDoc and cxNewDoc/cyNewDoc.
  384.      */
  385. }
  386.  
  387.  
  388. VOID AddAabSysMenu(VOID)
  389. {
  390.     if (fAabSysMenu == FALSE) {
  391.         WinSendMsg(hwndMainMenu, MM_INSERTITEM, MPFROMP(&miAabSysMenu),
  392.                 (MPARAM)NULL);
  393.         fAabSysMenu = TRUE;
  394.     }
  395. }
  396.  
  397.  
  398. VOID RemoveAabSysMenu(VOID)
  399. {
  400.     if (fAabSysMenu == TRUE) {
  401.         WinSendMsg(hwndMainMenu, MM_REMOVEITEM,
  402.                 MPFROMSHORT(IDM_AABDOCSYSMENU), (MPARAM)FALSE);
  403.         fAabSysMenu = FALSE;
  404.     }
  405. }
  406.  
  407.  
  408. BOOL MDICreate(HWND hwndClient)
  409. {
  410.     register NPVIEW npview;
  411.  
  412.     /*
  413.      * Allocate the DOC structure for this window and stick it in
  414.      * the window structure of the client.
  415.      */
  416.     npview = (NPVIEW)WinAllocMem(hHeap, sizeof(VIEW));
  417.     if (npview == NULL)
  418.         return (FALSE);
  419.  
  420.     npview->xOrigin = 0;
  421.     npview->yOrigin = 0;
  422.     npview->fs = 0;
  423.     npview->npviewNext = NULL;
  424.     npview->hwndClient = hwndClient;
  425.  
  426.     WinSetWindowUShort(hwndClient, QWS_USER, (USHORT)npview);
  427.  
  428.     return (TRUE);
  429. }
  430.  
  431.  
  432. VOID MDIActivate(HWND hwndClient, BOOL fActivate)
  433. {
  434.     HWND hwndFrame;
  435.     register NPDOC npdoc;
  436.  
  437.     hwndFrame = WinQueryWindow(hwndClient, QW_PARENT, FALSE);
  438.  
  439.     /*
  440.      * If our active status is changing, show/hide the AAB Sysmenu, check
  441.      * the correct item on the Window menu, and show/hide the titlebar
  442.      * frame controls.
  443.      */
  444.     npdoc = NPDOCFROMCLIENT(hwndClient);
  445.     if (fActivate != FALSE) {
  446.  
  447.         hwndActiveDoc = hwndFrame;
  448.         if (WinQueryWindowULong(hwndFrame, QWL_STYLE) & WS_MAXIMIZED)
  449.             AddAabSysMenu();
  450.         else
  451.             RemoveAabSysMenu();
  452.  
  453.         /*
  454.          * Check the appropriate item on the Window menu.
  455.          */
  456.         WinSendMsg(hwndWindowMenu, MM_SETITEMATTR,
  457.                 MPFROM2SHORT(npdoc->idMI, FALSE),
  458.                 MPFROM2SHORT(MIA_CHECKED, MIA_CHECKED));
  459.  
  460.         /*
  461.          * Show the titlebar frame controls.
  462.          */
  463.         WinSetParent(npdoc->hwndSysMenu, hwndFrame, FALSE);
  464.         WinSetParent(npdoc->hwndMinmax, hwndFrame, FALSE);
  465.         WinSendMsg(hwndFrame, WM_UPDATEFRAME, 0L, 0L);
  466.  
  467.     } else {
  468.         /*
  469.          * Uncheck the appropriate item on the Window menu.
  470.          */
  471.         WinSendMsg(hwndWindowMenu, MM_SETITEMATTR,
  472.                 MPFROM2SHORT(npdoc->idMI, FALSE),
  473.                 MPFROM2SHORT(MIA_CHECKED, 0));
  474.         hwndActiveDoc = NULL;
  475.  
  476.         /*
  477.          * Hide the titlebar frame controls.
  478.          */
  479.         WinSetParent(npdoc->hwndSysMenu, HWND_OBJECT, FALSE);
  480.         WinSetParent(npdoc->hwndMinmax, HWND_OBJECT, FALSE);
  481.         WinSendMsg(hwndFrame, WM_UPDATEFRAME, 0L, 0L);
  482.     }
  483. }
  484.  
  485.  
  486. VOID MDIDestroy(HWND hwndClient)
  487. {
  488.     WinFreeMem(hHeap, (NPBYTE)WinQueryWindowUShort(hwndClient, QWS_USER),
  489.             sizeof(VIEW));
  490. }
  491.  
  492.  
  493. VOID MDIClose(HWND hwndClient)
  494. {
  495.     WinDestroyWindow(WinQueryWindow(hwndClient, QW_PARENT, FALSE));
  496. }
  497.  
  498.  
  499. MRESULT CALLBACK DocFrameWndProc(HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
  500. {
  501.     MRESULT mres;
  502.     USHORT cFrameCtls;
  503.     HWND hwndParent, hwndClient;
  504.     register NPDOC npdoc;
  505.     RECTL rclClient;
  506.  
  507.     switch (msg) {
  508.  
  509.     case WM_SYSCOMMAND:
  510.         if (SHORT1FROMMP(mp1) == SC_SPLIT) {
  511.             WinSetPointer(HWND_DESKTOP, hptrHVSplit);
  512.             TrackSplitbars(WinWindowFromID(hwnd, FID_CLIENT),
  513.                     SPS_HORZ | SPS_VERT, -1, -1);
  514.             WinSetPointer(HWND_DESKTOP, hptrArrow);
  515.         } else if (SHORT1FROMMP(mp2) == CMDSRC_ACCELERATOR) {
  516.  
  517.             /*
  518.              * If the command was sent because of an accelerator
  519.              * we need to see if it goes to the document or the main
  520.              * frame window.
  521.              */
  522.             if ((WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000)) {
  523.  
  524.                 /*
  525.                  * If the control key is down we'll send it
  526.                  * to the document's frame since that means
  527.                  * it's either ctl-esc or one of the document
  528.                  * window's accelerators.
  529.                  */
  530.                 return (*pfnFrameWndProc)(hwnd, msg, mp1, mp2);
  531.             } else if (SHORT1FROMMP(mp1) == SC_DOCSYSMENU) {
  532.  
  533.                 /*
  534.                  * If the window is maximized then we want
  535.                  * to pull down the system menu on the main
  536.                  * menu bar.
  537.                  */
  538.                 if ((WinQueryWindowULong(hwnd, QWL_STYLE) & WS_MAXIMIZED) &&
  539.                         (SHORT1FROMMP(mp1) == SC_DOCSYSMENU)) {
  540.                     WinPostMsg(miAabSysMenu.hwndSubMenu, MM_STARTMENUMODE,
  541.                             MPFROM2SHORT(TRUE, FALSE), 0L);
  542.                     return (0L);
  543.                 } else {
  544.                     WinPostMsg(WinWindowFromID(hwnd, FID_SYSMENU),
  545.                             MM_STARTMENUMODE, MPFROM2SHORT(TRUE, FALSE), 0L);
  546.                 }
  547.             } else {
  548.                 /*
  549.                  * Control isn't down so send it the main
  550.                  * frame window.
  551.                  */
  552.                 return WinSendMsg(hwndMDIFrame, msg, mp1, mp2);
  553.             }
  554.         } else {
  555.             /*
  556.              * WM_SYSCOMMAND not caused by an accelerator
  557.              * so hwnd is the window we want to send the
  558.              * message to.
  559.              */
  560.             return (*pfnFrameWndProc)(hwnd, msg, mp1, mp2);
  561.         }
  562.         break;
  563.  
  564.     case WM_NEXTMENU:
  565.         /*
  566.          * Connect child sysmenu with application menus for cursor motion.
  567.          * Only return a value if we're the System Menu.  We don't want the
  568.          * MinMax menu or any others that might be there to freak out.
  569.          */
  570.         if (WinQueryWindowUShort(HWNDFROMMP(mp1), QWS_ID) == FID_SYSMENU) {
  571.             if (SHORT1FROMMP(mp2)) {
  572.                 return (MRESULT)hwndSysMenu;
  573.             } else {
  574.                 return (MRESULT)hwndAppMenu;
  575.             }
  576.         } else {
  577.             return (0L);
  578.         }
  579.         break;
  580.  
  581.     case WM_MINMAXFRAME:
  582.         return (MDIMinMaxFrame(hwnd, mp1, mp2));
  583.         break;
  584.  
  585.     case WM_WINDOWPOSCHANGED: 
  586.  
  587. #define aswp    ((PSWP)mp1)
  588.  
  589.         npdoc = NPDOCFROMCLIENT(WinWindowFromID(hwnd, FID_CLIENT));
  590.         if (((PSWP)mp1)->fs & SWP_SIZE) {
  591.  
  592.             if (npdoc->fs & (DF_SPLITHORZ | DF_HSPLITOVERFLOW)) {
  593.                 /*
  594.                  * If we were sized then adjust the horizontal splitbar
  595.                  * to be top-aligned.
  596.                  */
  597.                 npdoc->cyHorzSplitPos += (aswp[0].cy - aswp[1].cy);
  598.  
  599.                 /*
  600.                  * Set or clear the DF_HSPLITOVERFLOW and DF_SPLITHORZ flags.
  601.                  */
  602.                 if (npdoc->cyHorzSplitPos < 0) {
  603.                     npdoc->fs |= DF_HSPLITOVERFLOW;
  604.                     npdoc->fs &= ~DF_SPLITHORZ;
  605.                 } else {
  606.                     npdoc->fs &= ~DF_HSPLITOVERFLOW;
  607.                     npdoc->fs |= DF_SPLITHORZ;
  608.                 }
  609.             }
  610.  
  611.             if (npdoc->fs & (DF_SPLITVERT | DF_VSPLITOVERFLOW)) {
  612.  
  613.                 WinQueryWindowRect(hwnd, &rclClient);
  614.                 WinCalcFrameRect(hwnd, &rclClient, TRUE);
  615.  
  616.                 /*
  617.                  * Set or clear the DF_VSPLITOVERFLOW and DF_SPLITVERT flags.
  618.                  */
  619.                 if (npdoc->cxVertSplitPos >
  620.                         (((SHORT)rclClient.xRight - (SHORT)rclClient.xLeft) -
  621.                         cxVertSplitbar)) {
  622.                     npdoc->fs |= DF_VSPLITOVERFLOW;
  623.                     npdoc->fs &= ~DF_SPLITVERT;
  624.                 } else {
  625.                     npdoc->fs &= ~DF_VSPLITOVERFLOW;
  626.                     npdoc->fs |= DF_SPLITVERT;
  627.                 }
  628.             }
  629.         }
  630.  
  631. #undef aswp
  632.  
  633.         return (*pfnFrameWndProc)(hwnd, msg, mp1, mp2);
  634.         break;
  635.  
  636.     case WM_CALCVALIDRECTS:
  637.  
  638.         /*
  639.          * We do our own WM_CALCVALIDRECTS processing
  640.          * because the frame manager uses the window
  641.          * rectangle of FID_CLIENT, which in our case
  642.          * might be smaller than the 'client area' due
  643.          * to window splitting.
  644.          */
  645.  
  646. #define prclOld ((PRECTL)&(((PRECTL)mp1)[0]))
  647. #define prclNew ((PRECTL)&(((PRECTL)mp1)[1]))
  648.  
  649.         /*
  650.          * Calculate the client rectangle of hwnd in its
  651.          * parent's coordinates.
  652.          */
  653.         WinQueryWindowRect(hwnd, (PRECTL)prclOld);
  654.         hwndParent = WinQueryWindow(hwnd, QW_PARENT, FALSE);
  655.         WinMapWindowPoints(hwnd, hwndParent, (PPOINTL)prclOld, 2);
  656.         WinCalcFrameRect(hwnd, (PRECTL)prclOld, TRUE);
  657.  
  658.         /*
  659.          * Calculate the client rect for the
  660.          * destination of the frame window.
  661.          */
  662.         WinCalcFrameRect(hwnd, (PRECTL)prclNew, TRUE);
  663.  
  664.         /*
  665.          * Top align destination bits.
  666.          */
  667.         prclNew->yBottom += (prclNew->yTop - prclNew->yBottom) -
  668.                             (prclOld->yTop - prclOld->yBottom);
  669.  
  670. #undef prclOld
  671. #undef prclNew
  672.  
  673.         /*
  674.          * Return 0 since we've already aligned the bits.
  675.          */
  676.         return(0);
  677.         break;
  678.  
  679.     case WM_QUERYFRAMECTLCOUNT:
  680.         cFrameCtls = (int)(*pfnFrameWndProc)(hwnd, WM_QUERYFRAMECTLCOUNT, mp1, mp2);
  681.  
  682.         /*
  683.          * Max number of additional frame controls is seven
  684.          * Two splitbars, two additional scrollbars, and three
  685.          * additional client windows.  Throw in 3 more just to
  686.          * be sure we don't trash memory
  687.          */
  688.         return (MRFROMSHORT(cFrameCtls + 7 + 3));
  689.  
  690.     case WM_FORMATFRAME:
  691.         return (MDIFormatFrame(hwnd, ((PSWP)PVOIDFROMMP(mp1)), mp2));
  692.         break;
  693.  
  694.     case WM_BUTTON1UP:
  695.         mres =  (*pfnFrameWndProc)(hwnd, msg, mp1, mp2);
  696.  
  697.         /*
  698.          * If we're minimized we need to do activation ourself
  699.          * and bring up the system menu.
  700.          */
  701.         if (!(WinQueryWindowUShort(hwnd, QWS_FLAGS) & FF_ACTIVE)) {
  702.  
  703.             /*
  704.              * Only do this if we're minimized.
  705.              */
  706.             if (WinQueryWindowULong(hwnd, QWL_STYLE) & WS_MINIMIZED) {
  707.  
  708.                 WinSetActiveWindow(HWND_DESKTOP, hwnd);
  709.  
  710.                 /*
  711.                  * Bring up the system menu if there is one.
  712.                  */
  713.                 WinSendDlgItemMsg(hwnd, FID_SYSMENU, MM_STARTMENUMODE,
  714.                         MPFROMSHORT(TRUE), 0L);
  715.             }
  716.         }
  717.         break;
  718.  
  719.     case WM_DESTROY:
  720.         /*
  721.          * If this document is maximized, remove the AabSysMenu.
  722.          */
  723.         if (WinQueryWindowULong(hwnd, QWL_STYLE) & WS_MAXIMIZED)
  724.             RemoveAabSysMenu();
  725.  
  726.         hwndClient = WinWindowFromID(hwnd, FID_CLIENT);
  727.         npdoc = NPDOCFROMCLIENT(hwndClient);
  728.  
  729.         /*
  730.          * Make sure these windows are on the frame so they
  731.          * get destroyed.
  732.          */
  733.         WinSetParent(npdoc->hwndSysMenu, hwnd, FALSE);
  734.         WinSetParent(npdoc->hwndMinmax, hwnd, FALSE);
  735.  
  736.         RemoveFromWindowMenu(npdoc);
  737.  
  738.         WinFreeMem(hHeap, (NPBYTE)npdoc, sizeof(DOC));
  739.  
  740.         return (*pfnFrameWndProc)(hwnd, msg, mp1, mp2);
  741.  
  742.     default:
  743.         return (*pfnFrameWndProc)(hwnd, msg, mp1, mp2);
  744.     }
  745. }
  746.  
  747. MRESULT MDIMinMaxFrame(HWND hwnd, MPARAM mp1, MPARAM mp2)
  748. {
  749.     PSWP pswp;
  750.  
  751.     pswp = (PSWP)PVOIDFROMMP(mp1);
  752.     if ((pswp->fs & SWP_MAXIMIZE) &&
  753.             ((WinQueryWindowULong(hwnd, QWL_STYLE) & WS_MAXIMIZED) == 0L)) {
  754.         pswp->cy += cyTitlebar;
  755.         AddAabSysMenu();
  756.         SetMainTitleText(hwnd);
  757.     } else if (((pswp->fs & SWP_RESTORE) || (pswp->fs & SWP_MINIMIZE)) &&
  758.             (WinQueryWindowULong(hwnd, QWL_STYLE) & WS_MAXIMIZED)) {
  759.         RemoveAabSysMenu();
  760.         ClearMainTitleText();
  761.     }
  762.     return (*pfnFrameWndProc)(hwnd, WM_MINMAXFRAME, mp1, mp2);
  763. }
  764.  
  765. MRESULT MDIFormatFrame(HWND hwnd, PSWP aswp, MPARAM mp2)
  766. {
  767.     SWP swpClient;
  768.     PSWP pswpHScroll, pswpVScroll;
  769.     USHORT chwnd, iswpNext;
  770.     register NPDOC npdoc;
  771.     SHORT cyHorzSplitPos, cxVertSplitPos;
  772.  
  773.     iswpNext = chwnd = (USHORT)(*pfnFrameWndProc)(hwnd, WM_FORMATFRAME,
  774.                             aswp, mp2);
  775.  
  776.     FindSwp(aswp, chwnd, FID_HORZSCROLL, (PSWP FAR *)&pswpHScroll);
  777.     FindSwp(aswp, chwnd, FID_VERTSCROLL, (PSWP FAR *)&pswpVScroll);
  778.  
  779.     npdoc = NPDOCFROMCLIENT(aswp[chwnd - 1].hwnd);
  780.  
  781.     cyHorzSplitPos = npdoc->cyHorzSplitPos;
  782.     cxVertSplitPos = npdoc->cxVertSplitPos;
  783.  
  784.     /*
  785.      * Save the client rectangle away because we
  786.      * want to do thing based on the original
  787.      * client rectangle as well as move the
  788.      * client's ordering in the SWP list behind
  789.      * the other client windows.
  790.      */
  791.     swpClient = aswp[chwnd - 1];
  792.  
  793.     /*
  794.      * Start from the client window's SWP
  795.      * since we're going to move it to
  796.      * the end.
  797.      */
  798.     iswpNext = (chwnd - 1);
  799.  
  800.     if (npdoc->fsStyle & DS_VERTSPLITBAR) {
  801.  
  802.         /*
  803.          * If the horizontal scrollbar is being hidden
  804.          * then we certainly don't need to be around...
  805.          */
  806.         if (pswpHScroll->fs & SWP_HIDE) {
  807.  
  808.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_CLIENT2),
  809.                     &iswpNext);
  810.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_HORZSCROLL2),
  811.                     &iswpNext);
  812.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_VERTSPLITBAR),
  813.                     &iswpNext);
  814.  
  815.         } else if (npdoc->fs & DF_VSPLITOVERFLOW) {
  816.  
  817.             /*
  818.              * First hide CLIENT3 and VERTSCROLL2
  819.              * since we know they're going away.
  820.              */
  821.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_CLIENT2),
  822.                     &iswpNext);
  823.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_HORZSCROLL2),
  824.                     &iswpNext);
  825.  
  826.             /*
  827.              * Hide CLIENT4 since it won't be needed for now.
  828.              */
  829.             if (npdoc->fsStyle & DS_HORZSPLITBAR) {
  830.                 HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_CLIENT4),
  831.                         &iswpNext);
  832.             }
  833.  
  834.             /*
  835.              * If there isn't any room for even the hidden
  836.              * representation then get rid of it...
  837.              */
  838.             if (pswpHScroll->cx < (cxVertSplitbar * 3)) {
  839.                 HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_VERTSPLITBAR),
  840.                         &iswpNext);
  841.             } else {
  842.                 SetSwpPos(&aswp[iswpNext++],
  843.                         WinWindowFromID(hwnd, ID_VERTSPLITBAR), NULL,
  844.                         pswpHScroll->x + pswpHScroll->cx - cxVertSplitbar,
  845.                         pswpHScroll->y + cyBorder, cxVertSplitbar,
  846.                         pswpHScroll->cy - cyBorder, pswpHScroll->fs);
  847.  
  848.                 pswpHScroll->cx -= cxVertSplitbar;
  849.             }
  850.  
  851.         } else if (npdoc->fs & DF_SPLITVERT) {
  852.  
  853.             /*
  854.              * Format the client windows.
  855.              *
  856.              * If we're split horzintally as well then we
  857.              * need to show the fourth client.
  858.              */
  859.  
  860.             /*
  861.              * If we're split horizontally and there was
  862.              * enough room to be split then format
  863.              * the 'fourth' client window.
  864.              */
  865.             if ((npdoc->fs & DF_SPLITHORZ) &&
  866.                     (pswpVScroll->cy > (cyHorzSplitbar * 3))) {
  867.                 aswp[iswpNext].hwnd = WinWindowFromID(hwnd, ID_CLIENT4);
  868.                 aswp[iswpNext].hwndInsertBehind = NULL;
  869.                 aswp[iswpNext].x = swpClient.x +
  870.                         (cxVertSplitPos + cxVertSplitbar);
  871.                 aswp[iswpNext].cx = swpClient.cx -
  872.                         (cxVertSplitPos + cxVertSplitbar);
  873.                 aswp[iswpNext].y = swpClient.y;
  874.                 aswp[iswpNext].cy = cyHorzSplitPos;
  875.                 aswp[iswpNext].fs = swpClient.fs | SWP_SHOW;
  876.  
  877.                 iswpNext++;
  878.             }
  879.  
  880.             aswp[iswpNext].hwnd = WinWindowFromID(hwnd, ID_CLIENT2);
  881.             aswp[iswpNext].hwndInsertBehind = NULL;
  882.             aswp[iswpNext].x = swpClient.x +
  883.                     (cxVertSplitPos + cxVertSplitbar);
  884.             aswp[iswpNext].cx = swpClient.cx -
  885.                     (cxVertSplitPos + cxVertSplitbar);
  886.             /*
  887.              * If we're split horizontally and there was
  888.              * enough room to be split then format
  889.              * the 'second' client window against the
  890.              * 'fourth' client window.
  891.              */
  892.             if ((npdoc->fs & DF_SPLITHORZ) &&
  893.                     (pswpVScroll->cy > (cyHorzSplitbar * 3))) {
  894.                 aswp[iswpNext].y = swpClient.y +
  895.                         (cyHorzSplitPos + cyHorzSplitbar);
  896.                 aswp[iswpNext].cy = swpClient.cy -
  897.                         (cyHorzSplitPos + cyHorzSplitbar);
  898.             } else {
  899.                 aswp[iswpNext].y = swpClient.y;
  900.                 aswp[iswpNext].cy = swpClient.cy;
  901.             }
  902.             aswp[iswpNext].fs = swpClient.fs | SWP_SHOW;
  903.  
  904.             iswpNext++;
  905.  
  906.         } else {
  907.             /*
  908.              * If we're not split then format the window with the
  909.              * splitbar to the left of the horizontal scrollbar.
  910.              */
  911.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_HORZSCROLL2),
  912.                     &iswpNext);
  913.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_CLIENT2),
  914.                     &iswpNext);
  915.  
  916.             /*
  917.              * Hide ID_CLIENT4 since it won't be needed for now.
  918.              */
  919.             if (npdoc->fsStyle & DS_HORZSPLITBAR) {
  920.                 HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_CLIENT4),
  921.                         &iswpNext);
  922.             }
  923.  
  924.             aswp[iswpNext].hwnd = WinWindowFromID(hwnd, ID_VERTSPLITBAR);
  925.             aswp[iswpNext].hwndInsertBehind = NULL;
  926.             aswp[iswpNext].x = pswpHScroll->x;
  927.             aswp[iswpNext].y = pswpHScroll->y + cyBorder;
  928.             aswp[iswpNext].cx = cxVertSplitbar;
  929.             aswp[iswpNext].cy = pswpHScroll->cy - cyBorder;
  930.             aswp[iswpNext].fs = pswpHScroll->fs;
  931.             pswpHScroll->x += cxVertSplitbar;
  932.             pswpHScroll->cx -= cxVertSplitbar;
  933.  
  934.             iswpNext++;
  935.         }
  936.     }
  937.  
  938.     if (npdoc->fsStyle & DS_HORZSPLITBAR) {
  939.         /*
  940.          * If the horizontal scrollbar is being hidden
  941.          * then we certainly don't need to be around...
  942.          */
  943.         if (pswpVScroll->fs & SWP_HIDE) {
  944.  
  945.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_CLIENT3),
  946.                     &iswpNext);
  947.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_VERTSCROLL2),
  948.                     &iswpNext);
  949.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_HORZSPLITBAR),
  950.                     &iswpNext);
  951.  
  952.         } else if (npdoc->fs & DF_HSPLITOVERFLOW) {
  953.  
  954.             /*
  955.              * First hide CLIENT3 and VERTSCROLL2
  956.              * since we know they're going away.
  957.              */
  958.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_CLIENT3),
  959.                     &iswpNext);
  960.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_VERTSCROLL2),
  961.                     &iswpNext);
  962.  
  963.             /*
  964.              * Hide CLIENT4 since it won't be needed for now.
  965.              */
  966.             if (npdoc->fsStyle & DS_VERTSPLITBAR) {
  967.                 HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_CLIENT4),
  968.                         &iswpNext);
  969.             }
  970.  
  971.             /*
  972.              * If there isn't any room for even the hidden
  973.              * representation then get rid of it
  974.              */
  975.             if (pswpVScroll->cy < (cyHorzSplitbar * 3)) {
  976.                 HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_HORZSPLITBAR),
  977.                         &iswpNext);
  978.             } else {
  979.                 SetSwpPos(&aswp[iswpNext++],
  980.                         WinWindowFromID(hwnd, ID_HORZSPLITBAR), NULL,
  981.                         pswpVScroll->x, pswpVScroll->y, pswpVScroll->cx - cxBorder,
  982.                         cyHorzSplitbar, pswpVScroll->fs);
  983.  
  984.                 pswpVScroll->y += cyHorzSplitbar;
  985.                 pswpVScroll->cy -= cyHorzSplitbar;
  986.             }
  987.  
  988.         } else if (npdoc->fs & DF_SPLITHORZ) {
  989.  
  990.             if (pswpVScroll->cy > (cyHorzSplitbar * 3)) {
  991.                 /*
  992.                  * Format the client windows.
  993.                  */
  994.                 SetSwpPos(&aswp[iswpNext++],
  995.                         WinWindowFromID(hwnd, ID_CLIENT3), NULL,
  996.                         swpClient.x, swpClient.y,
  997.                         /*
  998.                          * If we're split vertically then format
  999.                          * the client against the vertical splitbar
  1000.                          * as well as the horzontal splitbar.
  1001.                          */
  1002.                         (npdoc->fs & DF_SPLITVERT) ? cxVertSplitPos : swpClient.cx,
  1003.                         cyHorzSplitPos, swpClient.fs | SWP_SHOW);
  1004.  
  1005.             } else {
  1006.                 HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_CLIENT3),
  1007.                         &iswpNext);
  1008.  
  1009.                 /*
  1010.                  * Hide CLIENT4 since it won't be needed for now.
  1011.                  */
  1012.                 if (npdoc->fsStyle & DS_VERTSPLITBAR) {
  1013.                     HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_CLIENT4),
  1014.                             &iswpNext);
  1015.                 }
  1016.             }
  1017.  
  1018.         } else {
  1019.             /*
  1020.              * If we're not split then format the window with the
  1021.              * splitbar at the top of the vertical scrollbar.
  1022.              */
  1023.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_VERTSCROLL2),
  1024.                     &iswpNext);
  1025.  
  1026.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_CLIENT3),
  1027.                     &iswpNext);
  1028.  
  1029.             if (npdoc->fsStyle & DS_VERTSPLITBAR) {
  1030.                 HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_CLIENT4),
  1031.                     &iswpNext);
  1032.             }
  1033.  
  1034.             if (pswpVScroll->cy > (cyHorzSplitbar * 3)) {
  1035.                 SetSwpPos(&aswp[iswpNext++],
  1036.                         WinWindowFromID(hwnd, ID_HORZSPLITBAR), NULL,
  1037.                         pswpVScroll->x, pswpVScroll->y + pswpVScroll->cy -
  1038.                             cyHorzSplitbar,
  1039.                         pswpVScroll->cx - cxBorder, cyHorzSplitbar, pswpVScroll->fs);
  1040.  
  1041.                 pswpVScroll->cy -= cyHorzSplitbar;
  1042.             } else {
  1043.                 HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_HORZSPLITBAR),
  1044.                         &iswpNext);
  1045.             }
  1046.         }
  1047.     }
  1048.  
  1049.     /*
  1050.      * Now format the original client window.
  1051.      */
  1052.     aswp[iswpNext] = swpClient;
  1053.  
  1054.     if (npdoc->fs & (DF_SPLITHORZ | DF_SPLITVERT)) {
  1055.  
  1056.         /*
  1057.          * Adjust the main client window for the splitbars.
  1058.          */
  1059.         if (npdoc->fs & DF_SPLITVERT) {
  1060.             aswp[iswpNext].cx = cxVertSplitPos;
  1061.         }
  1062.  
  1063.         if ((npdoc->fs & DF_SPLITHORZ) &&
  1064.                 (pswpVScroll->cy > (cyHorzSplitbar * 3))) {
  1065.             aswp[iswpNext].y += (cyHorzSplitPos + cyHorzSplitbar);
  1066.             aswp[iswpNext].cy -= (cyHorzSplitPos + cyHorzSplitbar);
  1067.         }
  1068.     }
  1069.  
  1070.     iswpNext++;
  1071.  
  1072.     if (npdoc->fs & DF_SPLITHORZ) {
  1073.         /*
  1074.          * Format the scrollbars and the splitbar.
  1075.          */
  1076.         if (pswpVScroll->cy > (cyHorzSplitbar * 3)) {
  1077.             SetSwpPos(&aswp[iswpNext++],
  1078.                     WinWindowFromID(hwnd, ID_HORZSPLITBAR), NULL,
  1079.                     swpClient.x, swpClient.y + cyHorzSplitPos,
  1080.                     swpClient.cx + pswpVScroll->cx - cxBorder, cyHorzSplitbar,
  1081.                     pswpVScroll->fs | SWP_SHOW);
  1082.  
  1083.             SetSwpPos(&aswp[iswpNext++],
  1084.                     WinWindowFromID(hwnd, ID_VERTSCROLL2), NULL,
  1085.                     pswpVScroll->x, pswpVScroll->y,
  1086.                     pswpVScroll->cx, cyHorzSplitPos + (cyBorder * 2),
  1087.                     pswpVScroll->fs | SWP_SHOW);
  1088.  
  1089.             pswpVScroll->y += (cyHorzSplitPos + cyHorzSplitbar);
  1090.             pswpVScroll->cy -= (cyHorzSplitPos + cyHorzSplitbar);
  1091.  
  1092.         } else {
  1093.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_VERTSCROLL2),
  1094.                     &iswpNext);
  1095.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_HORZSPLITBAR),
  1096.                     &iswpNext);
  1097.             HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_CLIENT3),
  1098.                     &iswpNext);
  1099.             if (npdoc->fsStyle & DS_VERTSPLITBAR)
  1100.                 HideSwp(&aswp[iswpNext], WinWindowFromID(hwnd, ID_CLIENT4),
  1101.                         &iswpNext);
  1102.         }
  1103.     }
  1104.  
  1105.     if (npdoc->fs & DF_SPLITVERT) {
  1106.  
  1107.         SetSwpPos(&aswp[iswpNext++],
  1108.                 WinWindowFromID(hwnd, ID_VERTSPLITBAR), NULL,
  1109.                 swpClient.x + cxVertSplitPos, pswpHScroll->y + cyBorder,
  1110.                 cxVertSplitbar, swpClient.cy + pswpHScroll->cy - cyBorder,
  1111.                 pswpHScroll->fs);
  1112.  
  1113.         SetSwpPos(&aswp[iswpNext++],
  1114.                 WinWindowFromID(hwnd, ID_HORZSCROLL2), NULL,
  1115.                 swpClient.x + cxVertSplitPos + cxVertSplitbar - cyBorder,
  1116.                 pswpHScroll->y, pswpHScroll->cx - (cxVertSplitPos + cxVertSplitbar),
  1117.                 pswpHScroll->cy, pswpVScroll->fs | SWP_SHOW);
  1118.  
  1119.         pswpHScroll->cx = cxVertSplitPos + (cxBorder * 2);
  1120.     }
  1121.  
  1122.     return (iswpNext);
  1123. }
  1124.