home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / winbase / ipc / ddeml / client / infoctrl.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  26KB  |  753 lines

  1.  
  2. /******************************************************************************\
  3. *       This is a part of the Microsoft Source Code Samples.
  4. *       Copyright (C) 1993-1997 Microsoft Corporation.
  5. *       All rights reserved.
  6. *       This source code is only intended as a supplement to
  7. *       Microsoft Development Tools and/or WinHelp documentation.
  8. *       See these sources for detailed information regarding the
  9. *       Microsoft samples programs.
  10. \******************************************************************************/
  11.  
  12. /***************************************************************************
  13.  *                                                                         *
  14.  *  MODULE      : infoctrl.c                                               *
  15.  *                                                                         *
  16.  *  PURPOSE     : Functions for the infoctrl control class                 *
  17.  *                                                                         *
  18.  ***************************************************************************/
  19. /*
  20.  * INFOCTRL.C
  21.  *
  22.  * This module implements a custom information display control which
  23.  * can present up to 7 seperate strings of information at once and is
  24.  * sizeable and moveable with the mouse.
  25.  */
  26.  
  27. #include <windows.h>
  28. #include <string.h>
  29. #include <tchar.h>
  30. #include <memory.h>
  31. #include "infoctrl.h"
  32. #include "track.h"
  33.  
  34. TCHAR szClass[] = TEXT("InfoCtrl_class");
  35. DWORD cCreated = 0;
  36. TCHAR szNULL[] = TEXT("");
  37. INT cxMargin = 0;
  38. INT cyMargin = 0;
  39. HBRUSH hFocusBrush;
  40.  
  41.  
  42. LONG  APIENTRY InfoCtrlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  43. VOID MyDrawText(HDC hdc, LPRECT lprc, PTSTR psz, DWORD wFormat);
  44. VOID DrawFocus(HDC hdc, HWND hwnd, DWORD style);
  45. INT CountWindows(HWND hwndParent);
  46. VOID GetCascadeWindowPos(HWND hwndParent, INT  iWindow, LPRECT lprc);
  47.  
  48.  
  49. /****************************************************************************
  50.  *                                                                          *
  51.  *  FUNCTION   :                                                            *
  52.  *                                                                          *
  53.  *  PURPOSE    :                                                            *
  54.  *                                                                          *
  55.  *  RETURNS    :                                                            *
  56.  *                                                                          *
  57.  ****************************************************************************/
  58. HWND CreateInfoCtrl(
  59. LPTSTR pszCenter,              // NULL is ok.
  60. INT x,
  61. INT y,
  62. INT cx,
  63. INT cy,
  64. HWND hwndParent,
  65. HANDLE hInst,
  66. LPTSTR pszUL,                // NULLs here are fine.
  67. LPTSTR pszUC,
  68. LPTSTR pszUR,
  69. LPTSTR pszLL,
  70. LPTSTR pszLC,
  71. LPTSTR pszLR,
  72. DWORD  style,
  73. HMENU id,
  74. DWORD dwUser)
  75. {
  76.     INFOCTRL_DATA *picd;
  77.     HWND hwnd;
  78.  
  79.     if (!cCreated) {
  80.         WNDCLASS wc;
  81.         TEXTMETRIC metrics;
  82.         HDC hdc;
  83.  
  84.         wc.style = CS_VREDRAW | CS_HREDRAW;
  85.         wc.lpfnWndProc = InfoCtrlWndProc;
  86.         wc.cbClsExtra = 0;
  87.         wc.cbWndExtra = ICCBWNDEXTRA;
  88.         wc.hInstance = hInst;
  89.         wc.hIcon = NULL;
  90.         wc.hCursor = NULL;
  91.         wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  92.         wc.lpszMenuName =  NULL;
  93.         wc.lpszClassName = szClass;
  94.  
  95.         RegisterClass(&wc);
  96.  
  97.         hdc = GetDC(hwndParent);
  98.         GetTextMetrics(hdc, &metrics);
  99.         cyMargin = metrics.tmHeight;
  100.         cxMargin = metrics.tmAveCharWidth * 2;
  101.         ReleaseDC(hwndParent, hdc);
  102.         hFocusBrush = CreateSolidBrush(RGB(0, 0, 255));
  103.     }
  104.  
  105.     if (!(picd = (INFOCTRL_DATA *)LocalAlloc(LPTR, sizeof(INFOCTRL_DATA))))
  106.         return(FALSE);
  107.  
  108.     if (pszCenter) {
  109.         picd->pszCenter = (PTSTR)(PTSTR)LocalAlloc(LPTR,
  110.                 (_tcslen(pszCenter) + 1) * sizeof(TCHAR));
  111.         _tcscpy(picd->pszCenter, pszCenter);
  112.     } else {
  113.         picd->pszCenter = NULL;
  114.     }
  115.  
  116.     if (pszUL) {
  117.         picd->pszUL = (PTSTR)(PTSTR)LocalAlloc(LPTR,
  118.             (_tcslen(pszUL) + 1) * sizeof(TCHAR));
  119.         _tcscpy(picd->pszUL, pszUL);
  120.     } else {
  121.         picd->pszUL = NULL;
  122.     }
  123.     if (pszUC) {
  124.         picd->pszUC = (PTSTR)LocalAlloc(LPTR,
  125.             (_tcslen(pszUC) + 1) * sizeof(TCHAR));
  126.         _tcscpy(picd->pszUC, pszUC);
  127.     } else {
  128.         picd->pszUC = NULL;
  129.     }
  130.     if (pszUR) {
  131.         picd->pszUR = (PTSTR)LocalAlloc(LPTR,
  132.             (_tcslen(pszUR) + 1) * sizeof(TCHAR));
  133.         _tcscpy(picd->pszUR, pszUR);
  134.     } else {
  135.         picd->pszUR = NULL;
  136.     }
  137.     if (pszLL) {
  138.         picd->pszLL = (PTSTR)LocalAlloc(LPTR,
  139.             (_tcslen(pszLL) + 1) * sizeof(TCHAR));
  140.         _tcscpy(picd->pszLL, pszLL);
  141.     } else {
  142.         picd->pszLL = NULL;
  143.     }
  144.     if (pszLC) {
  145.         picd->pszLC = (PTSTR)LocalAlloc(LPTR,
  146.             (_tcslen(pszLC) + 1) * sizeof(TCHAR));
  147.         _tcscpy(picd->pszLC, pszLC);
  148.     } else {
  149.         picd->pszLC = NULL;
  150.     }
  151.     if (pszLR) {
  152.         picd->pszLR = (PTSTR)LocalAlloc(LPTR,
  153.             (_tcslen(pszLR) + 1) * sizeof(TCHAR));
  154.         _tcscpy(picd->pszLR, pszLR);
  155.     } else {
  156.         picd->pszLR = NULL;
  157.     }
  158.  
  159.     picd->style = style;
  160.     picd->hInst = hInst;
  161.  
  162.     if (hwnd = CreateWindow(szClass, szNULL,
  163.             WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
  164.             x, y, cx, cy, hwndParent, id, hInst, (LPTSTR)picd)) {
  165.         cCreated++;
  166.         SetWindowLong(hwnd, GWL_USER, dwUser);
  167.         BringWindowToTop(hwnd);
  168.         ShowWindow(hwnd, SW_SHOW);
  169.         return(hwnd);
  170.     }
  171.     return(FALSE);
  172. }
  173.  
  174.  
  175.  
  176.  
  177. /****************************************************************************
  178.  *                                                                          *
  179.  *  FUNCTION   : MyDrawText                                                 *
  180.  *                                                                          *
  181.  *  PURPOSE    : Draws psz within lprc in hdc according to wFormat.         *
  182.  *                                                                          *
  183.  *  RETURNS    : Nothing.                                                   *
  184.  *                                                                          *
  185.  ****************************************************************************/
  186. VOID MyDrawText(
  187. HDC hdc,
  188. LPRECT lprc,
  189. PTSTR psz,
  190. DWORD wFormat)
  191. {
  192.     RECT rc;
  193.     DWORD cx;
  194.  
  195.     if (psz == NULL || !*psz)
  196.         return; // notin to draw dude.
  197.  
  198.     SetRect(&rc, 0, 0, 1, 0);
  199.     DrawText(hdc, psz, -1, &rc, DT_CALCRECT | DT_NOCLIP | DT_SINGLELINE);
  200.     cx = min(rc.right - rc.left, lprc->right - lprc->left);
  201.     CopyRect(&rc, lprc);
  202.     switch (wFormat & (DT_LEFT | DT_CENTER | DT_RIGHT)) {
  203.     case DT_LEFT:
  204.         rc.right = rc.left + cx;
  205.         break;
  206.  
  207.     case DT_CENTER:
  208.         cx = (rc.right - rc.left - cx) / 2;
  209.         rc.right -= cx;
  210.         rc.left += cx;
  211.         break;
  212.  
  213.     case DT_RIGHT:
  214.         rc.left = rc.right - cx;
  215.         break;
  216.     }
  217.     DrawText(hdc, psz, -1, &rc, wFormat | DT_VCENTER);
  218. }
  219.  
  220.  
  221.  
  222.  
  223. /****************************************************************************
  224.  *                                                                          *
  225.  *  FUNCTION   : InfoCtrlWndProc                                            *
  226.  *                                                                          *
  227.  *  PURPOSE    : Main window proc for info controls                         *
  228.  *                                                                          *
  229.  *  RETURNS    : case dependent                                             *
  230.  *                                                                          *
  231.  ****************************************************************************/
  232. LONG  APIENTRY InfoCtrlWndProc(
  233. HWND hwnd,
  234. UINT msg,
  235. WPARAM wParam,
  236. LPARAM lParam)
  237. {
  238.     INFOCTRL_DATA *picd;
  239.     INT i;
  240.     RECT rc;
  241.     HDC hdc;
  242.  
  243.     switch (msg) {
  244.     case WM_CREATE:
  245.         /*
  246.          * Info controls keep their information in the GWL_INFODATA window
  247.          * word.
  248.          */
  249.         SetWindowLong(hwnd, GWL_INFODATA,
  250.                 (DWORD)(DWORD)(((LPCREATESTRUCT)lParam)->lpCreateParams));
  251.         break;
  252.  
  253.     case WM_SIZE:
  254.         /*
  255.          * size the info control, updating the hittest rectangles.
  256.          * The window is only allowed to get so small.
  257.          */
  258.         if ((short)LOWORD(lParam) < 2 * cxMargin || (short)HIWORD(lParam) < 2 * cyMargin) {
  259.             MoveWindow(hwnd, 0, 0, max((short)LOWORD(lParam), 2 * cxMargin),
  260.                 max((short)HIWORD(lParam), 2 * cyMargin), TRUE);
  261.         } else {
  262.             picd = (INFOCTRL_DATA *)GetWindowLong(hwnd, GWL_INFODATA);
  263.             SetRect(&picd->rcFocusUL, 0, 0, cxMargin, cyMargin);
  264.             SetRect(&picd->rcFocusUR, (short)LOWORD(lParam) - cxMargin, 0,
  265.                     (short)LOWORD(lParam), cyMargin);
  266.             SetRect(&picd->rcFocusLL, 0, (short)HIWORD(lParam) - cyMargin,
  267.                     cxMargin, (INT)HIWORD(lParam));
  268.             SetRect(&picd->rcFocusLR, picd->rcFocusUR.left, picd->rcFocusLL.top,
  269.                     picd->rcFocusUR.right, picd->rcFocusLL.bottom);
  270.         }
  271.         break;
  272.  
  273.  
  274.     case WM_DESTROY:
  275.         /*
  276.          * Info control death:
  277.          *
  278.          * Inform out parent - last chance to access GWL_USER.
  279.          * Free our information if it still exists.
  280.          * Free all strings associated with this control.
  281.          */
  282.         {
  283.             PTSTR *ppsz;
  284.  
  285.             SendMessage(GetParent(hwnd), ICN_BYEBYE, (WPARAM)hwnd,
  286.                     GetWindowLong(hwnd, GWL_USER));
  287.             SetWindowLong(hwnd, GWL_USER, 0);
  288.  
  289.             picd = (INFOCTRL_DATA *)GetWindowLong(hwnd, GWL_INFODATA);
  290.             if (picd) {
  291.                 ppsz = &picd->pszUL;
  292.                 for (i = 0; i < 5; i++, ppsz++) {
  293.                     if (*ppsz) {
  294.                         LocalUnlock((HANDLE)*ppsz);
  295.                         *ppsz = (PTSTR)LocalFree((HANDLE)*ppsz);
  296.                     }
  297.                 }
  298.                 LocalUnlock((HANDLE)picd);
  299.                 LocalFree((HANDLE)picd);
  300.                 SetWindowLong(hwnd, GWL_INFODATA, 0);
  301.             }
  302.         }
  303.         break;
  304.  
  305.     case WM_SETFOCUS:
  306.     case WM_KILLFOCUS:
  307.         /*
  308.          * When focus changes:
  309.          *
  310.          * Alter our look apropriately
  311.          * Bring ourselves to the top if necessary.
  312.          * Inform our parent.
  313.          * Repaint the focus portion of ourselves.
  314.          * Call DefWindowProc()
  315.          */
  316.         picd = (INFOCTRL_DATA *)GetWindowLong(hwnd, GWL_INFODATA);
  317.         if (picd != NULL) {
  318.             if (picd->style & ICSTY_SHOWFOCUS) {
  319.                 if (msg == WM_SETFOCUS)
  320.                     picd->style |= ICSTY_HASFOCUS;
  321.                 else
  322.                     picd->style &= ~ICSTY_HASFOCUS;
  323.                 BringWindowToTop(hwnd);
  324.                 // notify parent
  325.                 SendMessage(GetParent(hwnd), ICN_HASFOCUS,
  326.                         msg == WM_SETFOCUS, (LPARAM)hwnd);
  327.             } else {
  328.                 picd->style &= ~ICSTY_HASFOCUS;
  329.             }
  330.             hdc = GetDC(hwnd);
  331.             DrawFocus(hdc, hwnd, picd->style);
  332.             ReleaseDC(hwnd, hdc);
  333.         }
  334.         goto DoDWP;
  335.         break;
  336.  
  337.     case WM_MOUSEMOVE:
  338.         /*
  339.          * Keep the cursor updated to show sizing or moving state.
  340.          */
  341.         {
  342.             LPTSTR cursor;
  343.  
  344.             picd = (INFOCTRL_DATA *)GetWindowLong(hwnd, GWL_INFODATA);
  345.             if (picd->style & ICSTY_SHOWFOCUS) {
  346.  
  347.                 if ((INT)HIWORD(lParam) < cyMargin) {
  348.                     if ((short)LOWORD(lParam) < cxMargin) {
  349.                         cursor = IDC_SIZENWSE;
  350.                     } else if ((short)LOWORD(lParam) > picd->rcFocusUR.left) {
  351.                         cursor = IDC_SIZENESW;
  352.                     } else {
  353.                         cursor = IDC_SIZENS;
  354.                     }
  355.                 } else if ((INT)HIWORD(lParam) > picd->rcFocusLL.top) {
  356.                     if ((short)LOWORD(lParam) < cxMargin) {
  357.                         cursor = IDC_SIZENESW;
  358.                     } else if ((short)LOWORD(lParam) > picd->rcFocusUR.left) {
  359.                         cursor = IDC_SIZENWSE;
  360.                     } else {
  361.                         cursor = IDC_SIZENS;
  362.                     }
  363.                 } else {
  364.                     if ((short)LOWORD(lParam) < cxMargin) {
  365.                         cursor = IDC_SIZEWE;
  366.                     } else if ((short)LOWORD(lParam) > picd->rcFocusUR.left) {
  367.                         cursor = IDC_SIZEWE;
  368.                     } else {
  369.                         cursor = IDC_CROSS;
  370.                     }
  371.                 }
  372.             } else {
  373.                 cursor = IDC_ARROW;
  374.             }
  375.             SetCursor(LoadCursor(NULL, cursor));
  376.         }
  377.         break;
  378.  
  379.     case WM_LBUTTONDOWN:
  380.         /*
  381.          * Track window according do mouse location.
  382.          */
  383.         picd = (INFOCTRL_DATA *)GetWindowLong(hwnd, GWL_INFODATA);
  384.         if (picd->style & ICSTY_SHOWFOCUS) {
  385.             DWORD fs = 0;
  386.  
  387.             if (!(picd->style & ICSTY_HASFOCUS)) {
  388.                 SetFocus(hwnd);
  389.             }
  390.  
  391.             if ((short)HIWORD(lParam) < cyMargin) {
  392.                 fs = TF_TOP;
  393.             } else if ((INT)HIWORD(lParam) > picd->rcFocusLL.top) {
  394.                 fs = TF_BOTTOM;
  395.             }
  396.             if ((short)LOWORD(lParam) < cxMargin) {
  397.                 fs |= TF_LEFT;
  398.             } else if ((short)LOWORD(lParam) > picd->rcFocusUR.left) {
  399.                 fs |= TF_RIGHT;
  400.             } else if (fs == 0) {
  401.                 fs = TF_MOVE;
  402.             }
  403.  
  404.             GetClientRect(hwnd, &rc);
  405.             ClientToScreen(hwnd, (LPPOINT)&rc.left);
  406.             ClientToScreen(hwnd, (LPPOINT)&rc.right);
  407.             ScreenToClient(GetParent(hwnd), (LPPOINT)&rc.left);
  408.             ScreenToClient(GetParent(hwnd), (LPPOINT)&rc.right);
  409.             if (TrackRect(picd->hInst, GetParent(hwnd),
  410.                     rc.left, rc.top, rc.right, rc.bottom,
  411.                     2 * cxMargin, 2 * cyMargin,
  412.                     fs | TF_ALLINBOUNDARY, &rc)) {
  413.  
  414.                 MoveWindow(hwnd, rc.left, rc.top, rc.right - rc.left,
  415.                         rc.bottom - rc.top, TRUE);
  416.             }
  417.         }
  418.         break;
  419.  
  420.     case ICM_SETSTRING:
  421.         /*
  422.          * This message is sent when a info control string value is changeing.
  423.          *
  424.          * wParam = ICSID_ constant
  425.          * lParam = new string.
  426.          *
  427.          * If new string is different from old, free old and allocate space
  428.          * for new one and copy in.
  429.          * Redraw invalidated part of info control.
  430.          */
  431.         {
  432.             PTSTR *ppsz;
  433.  
  434.             picd = (INFOCTRL_DATA *)GetWindowLong(hwnd, GWL_INFODATA);
  435.             ppsz = (PTSTR *)&picd->pszUL + wParam;
  436.  
  437.             if (lParam == 0)
  438.                 lParam = (DWORD)(LPTSTR)szNULL;
  439.  
  440.             if (*ppsz) {
  441.                 if (!_tcscmp(*ppsz, (LPTSTR)lParam)) {
  442.                     return 0;
  443.                 }
  444.                 LocalUnlock((HANDLE)*ppsz);
  445.                 *ppsz = (PTSTR)LocalFree((HANDLE)*ppsz);
  446.             }
  447.             if (lParam) {
  448.                 *ppsz = (PTSTR)LocalAlloc(LPTR,
  449.                     (_tcslen((LPTSTR)lParam) + 1) * sizeof(TCHAR));
  450.                 _tcscpy((LPTSTR)*ppsz, (LPTSTR)lParam);
  451.             }
  452.             GetClientRect(hwnd, &rc);
  453.             switch (wParam) {
  454.             case ICSID_UL:
  455.             case ICSID_UC:
  456.             case ICSID_UR:
  457.                 rc.bottom = cyMargin;
  458.                 break;
  459.  
  460.             case ICSID_LL:
  461.             case ICSID_LC:
  462.             case ICSID_LR:
  463.                 rc.top = rc.bottom - cyMargin;
  464.                 break;
  465.  
  466.             case ICSID_CENTER:
  467.                 InflateRect(&rc, -cxMargin, -cyMargin);
  468.                 break;
  469.             }
  470.             InvalidateRect(hwnd, &rc, TRUE);
  471.             UpdateWindow(hwnd);
  472.         }
  473.         break;
  474.  
  475.     case WM_PAINT:
  476.         /*
  477.          * Paint ourselves.
  478.          *
  479.          * Draw frame.
  480.          * Draw info strings.
  481.          * Send ownerdraw message to parent if ICSTY_OWNERDRAW.
  482.          */
  483.         {
  484.             PAINTSTRUCT ps;
  485.             HANDLE brush;
  486.  
  487.             picd = (INFOCTRL_DATA *)GetWindowLong(hwnd, GWL_INFODATA);
  488.             BeginPaint(hwnd, &ps);
  489.             // erasure should have already been done for us.
  490.             GetClientRect(hwnd, &rc);
  491.             brush = GetStockObject(BLACK_BRUSH);
  492.             InflateRect(&rc, -cxMargin / 2, -cyMargin / 2);
  493.             FrameRect(ps.hdc, &rc, brush);
  494.             InflateRect(&rc, cxMargin / 2, cyMargin / 2);
  495.             SetRect(&rc, picd->rcFocusUL.right, 0, picd->rcFocusUR.left,
  496.                     cyMargin);
  497.             MyDrawText(ps.hdc, &rc, picd->pszUR, DT_RIGHT);
  498.             MyDrawText(ps.hdc, &rc, picd->pszUL, DT_LEFT);
  499.             MyDrawText(ps.hdc, &rc, picd->pszUC, DT_CENTER);
  500.             SetRect(&rc, picd->rcFocusLL.right, picd->rcFocusLL.top,
  501.                     picd->rcFocusLR.left, picd->rcFocusLR.bottom);
  502.             MyDrawText(ps.hdc, &rc, picd->pszLR, DT_RIGHT);
  503.             MyDrawText(ps.hdc, &rc, picd->pszLL, DT_LEFT);
  504.             MyDrawText(ps.hdc, &rc, picd->pszLC, DT_CENTER);
  505.  
  506.             GetClientRect(hwnd, &rc);
  507.             InflateRect(&rc, -cxMargin, -cyMargin);
  508.             if (picd->style & ICSTY_OWNERDRAW) {
  509.                 OWNERDRAWPS odps;
  510.  
  511.                 if (IntersectRect(&odps.rcPaint, &rc, &ps.rcPaint)) {
  512.                     if (IntersectClipRect(ps.hdc, rc.left, rc.top, rc.right,
  513.                             rc.bottom) != NULLREGION) {
  514.                         odps.rcBound = rc;
  515.                         odps.hdc = ps.hdc;
  516.                         odps.dwUser = GetWindowLong(hwnd, GWL_USER);
  517.                         SendMessage(GetParent(hwnd), ICN_OWNERDRAW,
  518.                                 GetWindowLong(hwnd, GWL_ID), (DWORD)(LPTSTR)&odps);
  519.                     }
  520.                 }
  521.             } else {
  522.                 MyDrawText(ps.hdc, &rc, picd->pszCenter, DT_LEFT | DT_WORDBREAK | DT_EXPANDTABS);
  523.             }
  524.             DrawFocus(ps.hdc, hwnd, picd->style);
  525.             EndPaint(hwnd, &ps);
  526.         }
  527.         break;
  528.  
  529. DoDWP:
  530.     default:
  531.         return (DefWindowProc(hwnd, msg, wParam, lParam));
  532.     }
  533.     return (0);
  534. }
  535.  
  536.  
  537. /****************************************************************************
  538.  *                                                                          *
  539.  *  FUNCTION   : DrawFocus                                                  *
  540.  *                                                                          *
  541.  *  PURPOSE    : To draw focus part of info control.                        *
  542.  *                                                                          *
  543.  *  RETURNS    : nothing                                                    *
  544.  *                                                                          *
  545.  ****************************************************************************/
  546. VOID DrawFocus(
  547. HDC hdc,
  548. HWND hwnd,
  549. DWORD style)
  550. {
  551.     RECT rc;
  552.  
  553.     GetClientRect(hwnd, &rc);
  554.     FrameRect(hdc, &rc, style & ICSTY_HASFOCUS ?
  555.             hFocusBrush : GetStockObject(GRAY_BRUSH));
  556. }
  557.  
  558.  
  559.  
  560.  
  561. /****************************************************************************
  562.  *                                                                          *
  563.  *  FUNCTION   : CountWindows                                               *
  564.  *                                                                          *
  565.  *  PURPOSE    : Counts how many info controls the parent of this window has*
  566.  *                                                                          *
  567.  *  RETURNS    : the count.                                                 *
  568.  *                                                                          *
  569.  ****************************************************************************/
  570. INT CountWindows(
  571. register HWND hwndParent)
  572. {
  573.   INT cWindows = 0;
  574.   register HWND hwnd;
  575.  
  576.   for (hwnd=GetWindow(hwndParent, GW_CHILD);
  577.         hwnd;
  578.         hwnd= GetWindow(hwnd, GW_HWNDNEXT)) {
  579.       cWindows++;
  580.   }
  581.   return(cWindows);
  582. }
  583.  
  584.  
  585.  
  586. /****************************************************************************
  587.  *                                                                          *
  588.  *  FUNCTION   : GetCascadeWindowPos                                        *
  589.  *                                                                          *
  590.  *  PURPOSE    : Based on a window index and the parent window size,        *
  591.  *               calculates where to place a cascaded window.               *
  592.  *                                                                          *
  593.  *  RETURNS    : rectangle in lprc.                                         *
  594.  *                                                                          *
  595.  ****************************************************************************/
  596. VOID GetCascadeWindowPos(
  597. HWND hwndParent,
  598. INT  iWindow,
  599. LPRECT lprc)
  600. {
  601.   RECT      rc;
  602.   INT       cStack;
  603.   register INT dxClient, dyClient;
  604.  
  605.   /* Compute the width and breadth of the situation. */
  606.   GetClientRect(hwndParent, (LPRECT)&rc);
  607.   dxClient = rc.right - rc.left;
  608.   dyClient = rc.bottom - rc.top;
  609.  
  610.   /* How many windows per stack? */
  611.   cStack = dyClient / (3 * cyMargin);
  612.  
  613.   lprc->right = dxClient - (cStack * cxMargin);
  614.   lprc->bottom = dyClient - (cStack * cyMargin);
  615.  
  616.   cStack++;             /* HACK!: Mod by cStack+1 */
  617.  
  618.   lprc->left = (iWindow % cStack) * cxMargin;
  619.   lprc->top = (iWindow % cStack) * cyMargin;
  620. }
  621.  
  622.  
  623.  
  624.  
  625. /****************************************************************************
  626.  *                                                                          *
  627.  *  FUNCTION   : MyCascadeChildWindows                                      *
  628.  *                                                                          *
  629.  *  PURPOSE    : Cascades all children of a parent window                   *
  630.  *                                                                          *
  631.  *  RETURNS    : nothing                                                    *
  632.  *                                                                          *
  633.  ****************************************************************************/
  634. VOID MyCascadeChildWindows(
  635. register HWND hwndParent)
  636. {
  637.   INT       i;
  638.   INT       cWindows;
  639.   RECT      rc;
  640.   DWORD      wFlags;
  641.   register HWND hwndMove;
  642.   HANDLE    hDefer;
  643.  
  644.   cWindows = CountWindows(hwndParent);
  645.  
  646.   if (!cWindows)
  647.       return;
  648.  
  649.   hwndMove = GetWindow(hwndParent, GW_CHILD);
  650.  
  651.   hDefer = BeginDeferWindowPos(cWindows);
  652.  
  653.   for (i=0; i < cWindows; i++) {
  654.       GetCascadeWindowPos(hwndParent, i, (LPRECT)&rc);
  655.  
  656.       wFlags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS;
  657.  
  658.       /* Size the window. */
  659.       hDefer = DeferWindowPos(hDefer,
  660.                  hwndMove, NULL,
  661.                  rc.left, rc.top,
  662.                  rc.right, rc.bottom,
  663.                  wFlags);
  664.  
  665.       hwndMove = GetWindow(hwndMove, GW_HWNDNEXT);
  666.   }
  667.  
  668.   EndDeferWindowPos(hDefer);
  669. }
  670.  
  671.  
  672. /****************************************************************************
  673.  *                                                                          *
  674.  *  FUNCTION   : TileChildWindows                                           *
  675.  *                                                                          *
  676.  *  PURPOSE    : Tiles all children of a parent window                      *
  677.  *                                                                          *
  678.  *  RETURNS    : nothing.                                                   *
  679.  *                                                                          *
  680.  ****************************************************************************/
  681. VOID TileChildWindows(
  682. register HWND hwndParent)
  683. {
  684.   INT       i;
  685.   INT       dx;
  686.   INT       dy;
  687.   INT       xRes;
  688.   INT       yRes;
  689.   INT       iCol;
  690.   INT       iRow;
  691.   INT       cCols;
  692.   INT       cRows;
  693.   INT       cExtra;
  694.   INT       cWindows;
  695.   register HWND hwndMove;
  696.   RECT      rcClient;
  697.   HANDLE    hDefer;
  698.   DWORD      wFlags;
  699.  
  700.   cWindows = CountWindows(hwndParent);
  701.  
  702.   if (!cWindows)
  703.       return;
  704.  
  705.   /* Compute the smallest nearest square. */
  706.   for (i=2; i * i <= cWindows; i++);
  707.  
  708.   cRows = i - 1;
  709.   cCols = cWindows / cRows;
  710.   cExtra = cWindows % cRows;
  711.  
  712.   GetClientRect(hwndParent, (LPRECT)&rcClient);
  713.   xRes = rcClient.right - rcClient.left;
  714.   yRes = rcClient.bottom - rcClient.top;
  715.  
  716.   if (xRes<=0 || yRes<=0)
  717.       return;
  718.  
  719.   hwndMove = GetWindow(hwndParent, GW_CHILD);
  720.  
  721.   hDefer = BeginDeferWindowPos(cWindows);
  722.  
  723.   for (iCol=0; iCol < cCols; iCol++) {
  724.       if ((cCols-iCol) <= cExtra)
  725.       cRows++;
  726.  
  727.       for (iRow=0; iRow < cRows; iRow++) {
  728.           dx = xRes / cCols;
  729.           dy = yRes / cRows;
  730.  
  731.           wFlags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS;
  732.  
  733.           /* Position and size the window. */
  734.           hDefer = DeferWindowPos(hDefer, hwndMove, NULL,
  735.                      dx * iCol,
  736.                      dy * iRow,
  737.                      dx,
  738.                      dy,
  739.                      wFlags);
  740.  
  741.           hwndMove = GetWindow(hwndMove, GW_HWNDNEXT);
  742.       }
  743.  
  744.       if ((cCols-iCol) <= cExtra) {
  745.           cRows--;
  746.           cExtra--;
  747.       }
  748.   }
  749.  
  750.   EndDeferWindowPos(hDefer);
  751.  
  752. }
  753.