home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sdktools / winnt / dlgedit / util.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  26KB  |  1,014 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. /****************************** Module Header *******************************
  13. * Module Name: util.c
  14. *
  15. * Contains miscellaneous utility functions for dlgedit.
  16. *
  17. * Functions:
  18. *   MyAlloc()
  19. *   MyRealloc()
  20. *   MyFree()
  21. *   IsValue()
  22. *   HasBlanks()
  23. *   valtoi()
  24. *   axtoi()
  25. *   Myitoa()
  26. *   itoax()
  27. *   IsUniqueID()
  28. *   NextID()
  29. *   Message()
  30. *   ClientToScreenRect()
  31. *   ScreenToClientRect()
  32. *   DUToWinPoint()
  33. *   WinToDUPoint()
  34. *   DUToWinRect()
  35. *   WinToDURect()
  36. *   MapDlgClientPoint()
  37. *   MapWindowPoint()
  38. *   MyMapWindowRect()
  39. *   GetChildRect()
  40. *   CenterWindow()
  41. *   FitRectToScreen()
  42. *   ids()
  43. *   PixelsToPointSize()
  44. *   PointSizeToPixels()
  45. *
  46. * Comments:
  47. *
  48. ****************************************************************************/
  49.  
  50. #include "dlgedit.h"
  51. #include "dlgfuncs.h"
  52. #include "dlgextrn.h"
  53.  
  54. #include <stdarg.h>
  55. #include <ctype.h>
  56.  
  57.  
  58. #define CBOVERHEAD      (sizeof(INT)+sizeof(INT)+sizeof(INT))
  59. #define MEMSIGHEAD      0x1234
  60. #define MEMSIGTAIL      0x5678
  61.  
  62.  
  63. STATICFN BOOL IDUsedByCtrl(INT id);
  64.  
  65.  
  66.  
  67. /****************************************************************************
  68. * MyAlloc
  69. *
  70. * Does a local alloc.
  71. * Arguments:
  72. *   INT cbAlloc - number of bytes to allocate.
  73. *
  74. * Returns:
  75. *
  76. * A pointer to the memory if succesful; otherwise, NULL.
  77. *
  78. ****************************************************************************/
  79.  
  80. VOID *MyAlloc(
  81.     INT cbAlloc)
  82. {
  83.     register HANDLE hMem;
  84.  
  85.     if (hMem = LocalAlloc(LMEM_FIXED, cbAlloc)) {
  86.         return (VOID *)hMem;
  87.     }
  88.     else {
  89.         MessageBeep(0);
  90.         Message(MSG_OUTOFMEMORY);
  91.  
  92.         return NULL;
  93.     }
  94. }
  95.  
  96.  
  97. /****************************************************************************
  98. * MyRealloc
  99. *
  100. * reallocates memory and returns a pointer to the memory block.
  101. *
  102. ****************************************************************************/
  103.  
  104. VOID *MyRealloc(
  105.     VOID *npMem,
  106.     INT cbNewAlloc)
  107. {
  108.     npMem = (VOID *)LocalReAlloc((HANDLE)npMem, cbNewAlloc, LMEM_MOVEABLE);
  109.  
  110.     if (!npMem) {
  111.         MessageBeep(0);
  112.         Message(MSG_OUTOFMEMORY);
  113.  
  114.         return NULL;
  115.     }
  116.  
  117.     return npMem;
  118. }
  119.  
  120. /****************************************************************************
  121. * MyFree
  122. *
  123. * Frees the specified memory.
  124. *
  125. ****************************************************************************/
  126.  
  127. VOID *MyFree(
  128.     VOID *npMem)
  129. {
  130.     if (LocalFree((HANDLE)npMem)) {
  131.         MessageBeep(0);
  132.         Message(MSG_MEMERROR);
  133.  
  134.         return npMem;
  135.     }
  136.  
  137.     return NULL;
  138. }
  139.  
  140.  
  141.  
  142. /************************************************************************
  143. * IsValue
  144. *
  145. * This function tells you if the string you give it represents a
  146. * valid value or not.  For this purpose, a valid value can only
  147. * have the ascii characters from '0' to '9' with possibly the
  148. * first character being '-'.  Or be a Hex Number, with 0x preceeding
  149. * it.
  150. *
  151. * Arguments:
  152. *     LPTSTR pszValue  = The string to test.
  153. *
  154. * Returns:
  155. *     (0 == 0) if szValue represents a value.
  156. *     (c == 0) if szValue does not represent a value where c is
  157. *               non-zero.
  158. *
  159. *
  160. ************************************************************************/
  161.  
  162. BOOL IsValue(
  163.     LPTSTR pszValue)
  164. {
  165.     INT i;
  166.  
  167.     if (pszValue[0] == CHAR_0 &&
  168.             (pszValue[1] == CHAR_X || pszValue[1] == CHAR_CAP_X)) {
  169.         for (i = 2; iswxdigit(pszValue[i]); i++)
  170.             ;
  171.     }
  172.     else {
  173.         for (i = 0; iswdigit(pszValue[i]) ||
  174.                 (i == 0 && pszValue[i] == CHAR_MINUS); i++)
  175.             ;
  176.     }
  177.  
  178.     return (pszValue[i] == 0);
  179. }
  180.  
  181.  
  182.  
  183. /************************************************************************
  184. * HasBlanks
  185. *
  186. * This function returns TRUE if the given string has imbedded
  187. * blanks in it.
  188. *
  189. * Arguments:
  190. *   LPTSTR psz - String to check.
  191. *
  192. *
  193. ************************************************************************/
  194.  
  195. BOOL HasBlanks(
  196.     LPTSTR psz)
  197. {
  198.     while (*psz)
  199.         if (*psz == CHAR_SPACE)
  200.             return TRUE;
  201.         else
  202.             psz = CharNext(psz);
  203.  
  204.     return FALSE;
  205. }
  206.  
  207.  
  208.  
  209. /************************************************************************
  210. * valtoi
  211. *
  212. * Takes a string and returns its integer representation.
  213. * This function handles both hex ("0x1234") and decimal ("1234")
  214. * strings transparently.
  215. *
  216. * Arguments:
  217. *   LPTSTR pszValue = The string to convert.
  218. *
  219. *
  220. ************************************************************************/
  221.  
  222. INT valtoi(
  223.     LPTSTR pszValue)
  224. {
  225.     return (pszValue[0] == CHAR_0 &&
  226.             (pszValue[1] == CHAR_CAP_X || pszValue[1] == CHAR_X)) ?
  227.             axtoi(&pszValue[2]) : awtoi(pszValue);
  228. }
  229.  
  230.  
  231.  
  232. /************************************************************************
  233. * axtoi
  234. *
  235. * This function converts a null terminated ascii string for a
  236. * hex number to its integer value.  Should just be the number
  237. * with no preceeding "0x" or trailing "H".  Garbage will result
  238. * if there are non-hex digits in the string.  Hex digits are:
  239. * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, a, b, c, d, e, f.
  240. * Non-hex digits will be treated like '0'.
  241. *
  242. * Arguments:
  243. *     LPTSTR pch  = The null terminated hex string.
  244. *
  245. * Returns:
  246. *     The integer value represented by the given ascii string.
  247. *
  248. ************************************************************************/
  249.  
  250. INT axtoi(
  251.     LPTSTR pch)
  252. {
  253.     register TCHAR ch;
  254.     register INT n = 0;
  255.  
  256.     while((ch = *pch++) != 0) {
  257.         if (iswdigit(ch))
  258.             ch -= CHAR_0;
  259.         else if (ch >= CHAR_CAP_A && ch <= CHAR_CAP_F)
  260.             ch += (TCHAR)(10 - CHAR_CAP_A);
  261.         else if (ch >= CHAR_A && ch <= CHAR_F)
  262.             ch += (TCHAR)(10 - CHAR_A);
  263.         else
  264.             ch = (TCHAR)0;
  265.  
  266.         n = 16 * n + ch;
  267.     }
  268.  
  269.     return n;
  270. }
  271.  
  272.  
  273.  
  274. /************************************************************************
  275. * Myitoa
  276. *
  277. * This function converts a word to an ascii string.  It builds either
  278. * a decimal string or a hex string, based on whether Hex Mode is on
  279. * when it is called.
  280. *
  281. * Arguments:
  282. *   INT n      - The number to convert.
  283. *   LPTSTR psz - The buffer to put the string in, should have at least
  284. *                17 bytes to take the string.
  285. *
  286. ************************************************************************/
  287.  
  288. VOID Myitoa(
  289.     INT n,
  290.     LPTSTR psz)
  291. {
  292.     if (gfHexMode)
  293.         itoax(n, psz);
  294.     else
  295.         itoaw(n, psz, 10);
  296. }
  297.  
  298.  
  299.  
  300. /************************************************************************
  301. * itoax
  302. *
  303. * This function converts an int, 'n', to an ascii string
  304. * representing it as a 4 digit hex number with "0x" preceeding it.
  305. *
  306. * Arguments:
  307. *   INT n          = The number to convert.
  308. *   LPTSTR pszBuff = The buffer to put the string in, must
  309. *                    be at least 7 characters long to take the string.
  310. *
  311. *
  312. ************************************************************************/
  313.  
  314. VOID itoax(
  315.     INT n,
  316.     LPTSTR pszBuff)
  317. {
  318.     INT i;
  319.     INT j;
  320.  
  321.     pszBuff[0] = CHAR_0;
  322.     pszBuff[1] = CHAR_X;
  323.  
  324.     for (i = 5; i > 1; i--) {
  325.         j = n & 15;
  326.  
  327.         if (j > 9)
  328.             pszBuff[i] = (TCHAR)(j + (CHAR_A - 10));
  329.         else
  330.             pszBuff[i] = (TCHAR)(j + CHAR_0);
  331.  
  332.         n = n >> 4;
  333.     }
  334.  
  335.     pszBuff[6] = CHAR_NULL;
  336. }
  337.  
  338.  
  339.  
  340. /************************************************************************
  341. * NextID
  342. *
  343. * This function returns the next available id.
  344. *
  345. * For dialogs, it starts at 100 and increments by 100.  It will not
  346. * return a value until it finds one that begins a range that is
  347. * not used by any control in any of the dialogs in the res list.
  348. * In other words, it is guaranteed that the number returned and
  349. * the range of the next 99 numbers are not used by any control in
  350. * any dialog in the current res file.
  351. *
  352. * When returning a new id for a control, it usually starts at the
  353. * dialog base, but if there are any controls, it starts at one higher
  354. * than the id of the last control in the dialog that does not have
  355. * one of the special-cased ids (the unused id, IDOK or IDCANCEL).
  356. * It will find the first available id above this.
  357. *
  358. * When returning a default id for a new label, it starts at the dialog
  359. * base (or 100 if there is not a dialog being edited) and starts
  360. * searching for the first available one.  It guarantees that the
  361. * id returned is not used by any control in the current dialog, or
  362. * any other label, or any control in the entire resource list.
  363. *
  364. * Arguments:
  365. *   INT idType      - The type of id desired:
  366. *                     NEXTID_DIALOG  = ID for a new dialog.
  367. *                     NEXTID_CONTROL = ID for a new control.
  368. *                     NEXTID_LABEL   = ID for a new label.
  369. *   NPLABEL plHead  - The current label list to check for conflicts with.
  370. *   INT idExclude   - An id that you specifically want to skip over.
  371. *                     Set to zero if you don't care.
  372. *
  373. * Returns:
  374. *     The "next" unused id value.
  375. *
  376. *
  377. ************************************************************************/
  378.  
  379. INT NextID(
  380.     INT idType,
  381.     NPLABEL plHead,
  382.     INT idExclude)
  383. {
  384.     INT id;
  385.  
  386.     if (idType == NEXTID_CONTROL) {
  387.         /*
  388.          * Start at the base from the dialog plus one.  It is
  389.          * assumed that this routine will not be called for an
  390.          * id for a control if there is not a dialog being
  391.          * edited first.
  392.          */
  393.         id = gcd.npc->id + 1;
  394.  
  395.         /*
  396.          * Keep looping until an unused id is found.
  397.          */
  398.         while (!IsUniqueID(id) || FindID(id, plHead) || id == idExclude)
  399.             id++;
  400.     }
  401.     else if (idType == NEXTID_DIALOG) {
  402.         /*
  403.          * Start at 100.
  404.          */
  405.         id = 100;
  406.  
  407.         /*
  408.          * Keep looping by hundreds until an unused id is found.
  409.          */
  410.         while (!IsUniqueID(id) || FindID(id, plHead) || id == idExclude)
  411.             id += 100;
  412.     }
  413.     else {
  414.         /*
  415.          * We are looking for a default id for a new label.  Start
  416.          * at the dialog base, if there is a dialog being edited.
  417.          */
  418.         if (gfEditingDlg)
  419.             id = gcd.npc->id + 1;
  420.         else
  421.             id = 100;
  422.  
  423.         /*
  424.          * Keep looping until an unused id is found.  The id should
  425.          * not be used by any control in the current dialog, any
  426.          * other label already, or any control in the res file.
  427.          */
  428.         while (FindID(id, plHead) || FindIDInRes(id) || id == idExclude)
  429.             id++;
  430.     }
  431.  
  432.     /*
  433.      * We found an unused one.  Return it.
  434.      */
  435.     return id;
  436. }
  437.  
  438.  
  439.  
  440. /************************************************************************
  441. * IDUsedByCtrl
  442. *
  443. * This function returns TRUE if the given ID is used by any control
  444. * in the current dialog.  This also counts the text field of W_ICON
  445. * controls, if they are ordinals.
  446. *
  447. * Arguments:
  448. *   INT id = The ID to look for.
  449. *
  450. * Returns:
  451. *   TRUE if the id is used, FALSE if not.
  452. *
  453. *
  454. ************************************************************************/
  455.  
  456. STATICFN BOOL IDUsedByCtrl(
  457.     INT id)
  458. {
  459.     register NPCTYPE npc;
  460.  
  461.     for (npc = npcHead; npc; npc = npc->npcNext) {
  462.         if (npc->id == id ||
  463.                 (npc->pwcd->iType == W_ICON &&
  464.                 npc->text &&
  465.                 IsOrd(npc->text) &&
  466.                 id == (INT)OrdID(npc->text)))
  467.             return TRUE;
  468.     }
  469.  
  470.     return FALSE;
  471. }
  472.  
  473.  
  474.  
  475. /************************************************************************
  476. * IsUniqueID
  477. *
  478. * This function returns TRUE if the given id is unique.  A unique
  479. * id is either the special "unused" id value, or it is an id that
  480. * is not already assigned to any other control in the current dialog
  481. * and it is not assigned to any other dialog in the current res list.
  482. *
  483. * Note that this routine does NOT look for duplicates in the include
  484. * file of this id, only for ids that have been used by other controls
  485. * or dialogs already.
  486. *
  487. * Arguments:
  488. *   INT id = The id to verify is unique.
  489. *
  490. * Returns:
  491. *   TRUE if the id is "unique", FALSE if it is not.
  492. *
  493. *
  494. ************************************************************************/
  495.  
  496. BOOL IsUniqueID(
  497.     INT id)
  498. {
  499.     ORDINAL ord;
  500.  
  501.     /*
  502.      * If the id is the special unused id, it is considered unique.
  503.      */
  504.     if (id == IDUNUSED)
  505.         return TRUE;
  506.  
  507.     /*
  508.      * Not unique if another control in the dialog has the same id.
  509.      */
  510.     if (IDUsedByCtrl(id))
  511.         return FALSE;
  512.  
  513.     /*
  514.      * Not unique if another dialog has the same id.
  515.      */
  516.     WriteOrd(&ord, id);
  517.     if (FindDialog((LPTSTR)&ord))
  518.         return FALSE;
  519.  
  520.     return TRUE;
  521. }
  522.  
  523.  
  524.  
  525. /************************************************************************
  526. * Message
  527. *
  528. * This function puts up a message box with a string indexed by idMsg.
  529. *
  530. * Returns:
  531. *     What MessageBox returns.
  532. *
  533. ************************************************************************/
  534.  
  535. INT Message(
  536.     INT idMsg,
  537.     ...)
  538. {
  539.     va_list marker;
  540.     INT RetCode;
  541.     TCHAR szT[CCHTEXTMAX];
  542.     BOOL fDisabledSave;
  543.  
  544.     va_start(marker, idMsg);
  545.     wvsprintf(szT, ids(gamdMessages[idMsg].ids), marker);
  546.  
  547.     fDisabledSave = gfDisabled;
  548.     gfDisabled = TRUE;
  549.     RetCode = MessageBox(NULL, szT, ids(IDS_DLGEDIT),
  550.             (WORD)(gamdMessages[idMsg].fMessageBox | MB_TASKMODAL));
  551.     gfDisabled = fDisabledSave;
  552.  
  553.     va_end(marker);
  554.  
  555.     return RetCode;
  556. }
  557.  
  558.  
  559.  
  560. /************************************************************************
  561. * ClientToScreenRect
  562. *
  563. * This function converts the coordinates in a rectangle from points
  564. * relative to the client area into points that are relative to the
  565. * screen.
  566. *
  567. * Arguments:
  568. *   HWND hwnd - Window handle for the conversion.
  569. *   PRECT prc - Pointer to the rectangle to convert.
  570. *
  571. *
  572. ************************************************************************/
  573.  
  574. VOID ClientToScreenRect(
  575.     HWND hwnd,
  576.     PRECT prc)
  577. {
  578.     ClientToScreen(hwnd, (PPOINT)prc);
  579.     ClientToScreen(hwnd, ((PPOINT)prc) + 1);
  580. }
  581.  
  582.  
  583.  
  584. /************************************************************************
  585. * ScreenToClientRect
  586. *
  587. * This function converts the coordinates in a rectangle from points
  588. * relative to the screen into points that are relative to the given
  589. * window's client area.
  590. *
  591. * Arguments:
  592. *   HWND hwnd - Window handle for the conversion.
  593. *   PRECT prc - Pointer to the rectangle to convert.
  594. *
  595. *
  596. ************************************************************************/
  597.  
  598. VOID ScreenToClientRect(
  599.     HWND hwnd,
  600.     PRECT prc)
  601. {
  602.     ScreenToClient(hwnd, (PPOINT)prc);
  603.     ScreenToClient(hwnd, ((PPOINT)prc) + 1);
  604. }
  605.  
  606.  
  607.  
  608. /************************************************************************
  609. * DUToWinPoint
  610. *
  611. * This function converts the coordinates in the given point from
  612. * dialog units (DU's) to window units for the current dialog.
  613. *
  614. * Arguments:
  615. *   PPOINT ppt - Pointer to the point to convert.
  616. *
  617. *
  618. ************************************************************************/
  619.  
  620. VOID DUToWinPoint(
  621.     PPOINT ppt)
  622. {
  623.     ppt->x = MulDiv(ppt->x, gcd.cxChar, 4);
  624.     ppt->y = MulDiv(ppt->y, gcd.cyChar, 8);
  625. }
  626.  
  627.  
  628.  
  629. /************************************************************************
  630. * WinToDUPoint
  631. *
  632. * This function converts the coordinates in the given point from
  633. * window points to dialog units (DU's) for the current dialog.
  634. *
  635. * Arguments:
  636. *   PPOINT ppt - Pointer to the point to convert.
  637. *
  638. *
  639. ************************************************************************/
  640.  
  641. VOID WinToDUPoint(
  642.     PPOINT ppt)
  643. {
  644.     ppt->x = MulDiv(ppt->x, 4, gcd.cxChar);
  645.     ppt->y = MulDiv(ppt->y, 8, gcd.cyChar);
  646. }
  647.  
  648.  
  649.  
  650. /************************************************************************
  651. * DUToWinRect
  652. *
  653. * This function converts the coordinates in a rectangle from
  654. * dialog units for the current dialog to window units.
  655. *
  656. * Arguments:
  657. *   PRECT prc - Pointer to the rectangle to convert.
  658. *
  659. *
  660. ************************************************************************/
  661.  
  662. VOID DUToWinRect(
  663.     PRECT prc)
  664. {
  665.     DUToWinPoint((PPOINT)prc);
  666.     DUToWinPoint(((PPOINT)prc) + 1);
  667. }
  668.  
  669.  
  670.  
  671. /************************************************************************
  672. * WinToDURect
  673. *
  674. * This function converts the coordinates in a rectangle from
  675. * window units to dialog units for the current dialog.
  676. *
  677. * Arguments:
  678. *   PRECT prc - Pointer to the rectangle to convert.
  679. *
  680. *
  681. ************************************************************************/
  682.  
  683. VOID WinToDURect(
  684.     PRECT prc)
  685. {
  686.     WinToDUPoint((PPOINT)prc);
  687.     WinToDUPoint(((PPOINT)prc) + 1);
  688. }
  689.  
  690.  
  691.  
  692. /************************************************************************
  693. * MapDlgClientPoint
  694. *
  695. * This function converts client points to be relative to the window
  696. * origin instead, or the other way around.  If fFromClient is TRUE,
  697. * the point is considered to be relative to the client origin in
  698. * the dialog, and will be converted to a point relative to the
  699. * window origin instead.
  700. *
  701. * If fFromClient is FALSE, the point is considered to be relative
  702. * to the window origin, and will be mapped to a point that is
  703. * relative to the client origin.
  704. *
  705. * This function assumes that the global grcDlgClient has been
  706. * previously calculated.  It should only be called to map points
  707. * for the current dialog being edited (for which grcDlgClient has
  708. * been calculated).
  709. *
  710. * Arguments:
  711. *   PPOINT ppt       - Pointer to the point to convert.
  712. *   BOOL fFromClient - TRUE if the point is relative to the client origin.
  713. *
  714. *
  715. ************************************************************************/
  716.  
  717. VOID MapDlgClientPoint(
  718.     PPOINT ppt,
  719.     BOOL fFromClient)
  720. {
  721.     if (fFromClient) {
  722.         ppt->x += grcDlgClient.left;
  723.         ppt->y += grcDlgClient.top;
  724.     }
  725.     else {
  726.         ppt->x -= grcDlgClient.left;
  727.         ppt->y -= grcDlgClient.top;
  728.     }
  729. }
  730.  
  731.  
  732.  
  733. /************************************************************************
  734. * MapWindowPoint
  735. *
  736. * This function maps a point from one window to another.  The point
  737. * given is in window coordinates (not client coordinates) and is
  738. * mapped so that it is relative to the destination window.
  739. *
  740. * Arguments:
  741. *   HWND hwndFrom   - Source window.
  742. *   HWND hwndTo     - Destination window.
  743. *   PPOINT ppt      - Pointer to the point to convert.
  744. *
  745. * Comments:
  746. * In Win 3.1 (and NT) the MapWindowPoints call can be used and this one can be removed.
  747. * It is only needed here for compatibility with Win 3.0.
  748. *
  749. ************************************************************************/
  750.  
  751. VOID MapWindowPoint(
  752.     HWND hwndFrom,
  753.     HWND hwndTo,
  754.     PPOINT ppt)
  755. {
  756.     RECT rcFrom;
  757.     RECT rcTo;
  758.  
  759.     GetWindowRect(hwndFrom, &rcFrom);
  760.     GetWindowRect(hwndTo, &rcTo);
  761.  
  762.     ppt->x += rcFrom.left - rcTo.left;
  763.     ppt->y += rcFrom.top - rcTo.top;
  764. }
  765.  
  766.  
  767.  
  768. /************************************************************************
  769. * MyMapWindowRect
  770. *
  771. * This function maps a rectangle from one window to another.  The rectangle
  772. * given is in window coordinates (not client coordinates) and is
  773. * mapped so that it is relative to the destination window.
  774. *
  775. * Arguments:
  776. *   HWND hwndFrom   - Source window.
  777. *   HWND hwndTo     - Destination window.
  778. *   PRECT prc       - Pointer to the rectangle to convert.
  779. *
  780. * Comments:
  781. * In Win 3.1 (and NT) the MapWindowRect call can be used and this one can be removed.
  782. * It is only needed here for compatibility with Win 3.0.
  783. ************************************************************************/
  784.  
  785. VOID MyMapWindowRect(
  786.     HWND hwndFrom,
  787.     HWND hwndTo,
  788.     PRECT prc)
  789. {
  790.     RECT rcFrom;
  791.     RECT rcTo;
  792.  
  793.     GetWindowRect(hwndFrom, &rcFrom);
  794.     GetWindowRect(hwndTo, &rcTo);
  795.  
  796.     OffsetRect(prc, rcFrom.left - rcTo.left, rcFrom.top - rcTo.top);
  797. }
  798.  
  799.  
  800.  
  801. /************************************************************************
  802. * GetChildRect
  803. *
  804. * This function returns the client rectangle for a given child control,
  805. * mapped to its parent window.
  806. *
  807. * Arguments:
  808. *   HWND hwndChild - Child window.
  809. *   PRECT prc      - Where to return the rectangle.
  810. *
  811. ************************************************************************/
  812.  
  813. VOID GetChildRect(
  814.     HWND hwndChild,
  815.     PRECT prc)
  816. {
  817.     HWND hwndParent;
  818.  
  819.     hwndParent = GetParent(hwndChild);
  820.     GetClientRect(hwndChild, prc);
  821.     ClientToScreenRect(hwndChild, prc);
  822.     ScreenToClientRect(hwndParent, prc);
  823. }
  824.  
  825.  
  826.  
  827. /************************************************************************
  828. * CenterWindow
  829. *
  830. * This function centers the given window over its owner.  It ensures
  831. * that the window is entirely within the visible screen, however.
  832. * If the window does not have an owner, it is centered over the
  833. * desktop.
  834. *
  835. * Arguments:
  836. *   HWND hwnd - The window to center.
  837. *
  838. ************************************************************************/
  839.  
  840. VOID CenterWindow(
  841.     HWND hwnd)
  842. {
  843.     RECT rc;
  844.     RECT rcOwner;
  845.     RECT rcCenter;
  846.     HWND hwndOwner;
  847.  
  848.     GetWindowRect(hwnd, &rc);
  849.  
  850.     if (!(hwndOwner = GetWindow(hwnd, GW_OWNER)))
  851.         hwndOwner = GetDesktopWindow();
  852.  
  853.     GetWindowRect(hwndOwner, &rcOwner);
  854.  
  855.     /*
  856.      *  Calculate the starting x,y for the new
  857.      *  window so that it would be centered.
  858.      */
  859.     rcCenter.left = rcOwner.left +
  860.             (((rcOwner.right - rcOwner.left) -
  861.             (rc.right - rc.left))
  862.             / 2);
  863.  
  864.     rcCenter.top = rcOwner.top +
  865.             (((rcOwner.bottom - rcOwner.top) -
  866.             (rc.bottom - rc.top))
  867.             / 2);
  868.  
  869.     rcCenter.right = rcCenter.left + (rc.right - rc.left);
  870.     rcCenter.bottom = rcCenter.top + (rc.bottom - rc.top);
  871.  
  872.     FitRectToScreen(&rcCenter);
  873.  
  874.     SetWindowPos(hwnd, NULL, rcCenter.left, rcCenter.top, 0, 0,
  875.             SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
  876. }
  877.  
  878.  
  879.  
  880. /************************************************************************
  881. * FitRectToScreen
  882. *
  883. * This function ensures that the given rectangle is entirely within
  884. * the visible screen, adjusting it if necessary.
  885. *
  886. * Arguments:
  887. *   PRECT prc - The rectangle.
  888. *
  889. ************************************************************************/
  890.  
  891. VOID FitRectToScreen(
  892.     PRECT prc)
  893. {
  894.     INT cxScreen;
  895.     INT cyScreen;
  896.     INT delta;
  897.  
  898.     cxScreen = GetSystemMetrics(SM_CXSCREEN);
  899.     cyScreen = GetSystemMetrics(SM_CYSCREEN);
  900.  
  901.     if (prc->right > cxScreen) {
  902.         delta = prc->right - prc->left;
  903.         prc->right = cxScreen;
  904.         prc->left = prc->right - delta;
  905.     }
  906.  
  907.     if (prc->left < 0) {
  908.         delta = prc->right - prc->left;
  909.         prc->left = 0;
  910.         prc->right = prc->left + delta;
  911.     }
  912.  
  913.     if (prc->bottom > cyScreen) {
  914.         delta = prc->bottom - prc->top;
  915.         prc->bottom = cyScreen;
  916.         prc->top = prc->bottom - delta;
  917.     }
  918.  
  919.     if (prc->top < 0) {
  920.         delta = prc->bottom - prc->top;
  921.         prc->top = 0;
  922.         prc->bottom = prc->top + delta;
  923.     }
  924. }
  925.  
  926.  
  927.  
  928. /************************************************************************
  929. * ids
  930. *
  931. * This function will return a string, given the string id.  If this is
  932. * the first time that the string has been retrieved, memory will be
  933. * allocated for it and it will be loaded.  After it is loaded once, it
  934. * is then cached in a LPTSTR array and is available for later without
  935. * having to load it again.
  936. *
  937. * Arguments:
  938. *   UINT idString - String ID of the string to retrieve.
  939. *
  940. ************************************************************************/
  941.  
  942. LPTSTR ids(
  943.     UINT idString)
  944. {
  945.     static LPTSTR apsz[CSTRINGS];       // String resource array cache.
  946.     LPTSTR psz;
  947.     INT cch;
  948.  
  949.     if (apsz[idString])
  950.         return apsz[idString];
  951.  
  952.     if (!(psz = MyAlloc(CCHTEXTMAX * sizeof(TCHAR))))
  953.         return szEmpty;
  954.  
  955.     if (!(cch = LoadString(ghInst, idString, psz, CCHTEXTMAX))) {
  956.         MyFree(psz);
  957.         return szEmpty;
  958.     }
  959.  
  960.     apsz[idString] = psz = MyRealloc(psz, (cch + 1) * sizeof(TCHAR));
  961.  
  962.     return (psz ? psz : szEmpty);
  963. }
  964.  
  965.  
  966.  
  967. /************************************************************************
  968. * PixelsToPointSize
  969. *
  970. * This function takes a font height in pixels and converts it to
  971. * the equivalent point size.  Note that the pixel height of a font
  972. * is actually the tmHeight field of the TEXTMETRIC structure minus
  973. * the tmInternalLeading value.
  974. *
  975. * This function relies on the global gcyPixelsPerInch having been
  976. * set before it is called.
  977. *
  978. * Arguments:
  979. *   INT nPixels - Pixel size to convert to point size.
  980. *
  981. ************************************************************************/
  982.  
  983. INT PixelsToPointSize(
  984.     INT nPixels)
  985. {
  986.     return MulDiv(nPixels, 72, gcyPixelsPerInch);
  987. }
  988.  
  989.  
  990.  
  991. /************************************************************************
  992. * PointSizeToPixels
  993. *
  994. * This function takes a given point size and converts it to the
  995. * equivalent pixel text height.  This value can be placed in
  996. * the TEXTMETRIC structure's tmHeight field if it is made negative
  997. * first.  This will cause a CreateFont call to automatically
  998. * subtract the internal leading value before creating the font.
  999. *
  1000. * This function relies on the global gcyPixelsPerInch having been
  1001. * set before it is called.
  1002. *
  1003. * Arguments:
  1004. *   INT nPointSize - Point size to convert to pixels.
  1005. *
  1006. ************************************************************************/
  1007.  
  1008. INT PointSizeToPixels(
  1009.     INT nPointSize)
  1010. {
  1011.     return MulDiv(nPointSize, gcyPixelsPerInch, 72);
  1012. }
  1013.