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 / restodlg.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  30KB  |  972 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: restodlg.c
  14. *
  15. * Routines that take a dialog resource and create the dialog to edit, or
  16. * the other way around.
  17. *
  18. * Fucntions:
  19. *   SynchDialogResource()
  20. *   AllocDialogResource()
  21. *   ResLinkToDialog()
  22. *   ResToDialog()
  23. *   GetiClass()
  24. *   Duplicate()
  25. *   MakeCopyFromRes()
  26. *   TypeFromClassStyle()
  27. *
  28. * Comments:
  29. *
  30. ****************************************************************************/
  31.  
  32. #include "dlgedit.h"
  33. #include "dlgfuncs.h"
  34. #include "dlgextrn.h"
  35.  
  36. STATICFN INT TypeFromClassStyle(INT iClass, DWORD flStyle);
  37.  
  38.  
  39.  
  40. /************************************************************************
  41. * SynchDialogResource
  42. *
  43. * This routine synchronizes the resource buffer with the contents of
  44. * the current dialog being edited.  This may involve deleting the old
  45. * contents of the current dialog prior to adding the new data.
  46. *
  47. * It is ok to call this routine even if there is not an existing dialog
  48. * being edited (it will just return) and it should be called before any
  49. * operation that needs the in memory copy of the dialog to accurately
  50. * reflect the contents of the current dialog, such as just before a
  51. * save to disk.
  52. *
  53. * Returns:
  54. *     TRUE if all goes well (includes the case where nothing was done).
  55. *     FALSE if an error occurs updating the resource.
  56. *
  57. ************************************************************************/
  58.  
  59. BOOL SynchDialogResource(VOID)
  60. {
  61.     PRES pRes;
  62.     PRESLINK prl;
  63.     PRESLINK prlNew;
  64.     PRESLINK prlPrev;
  65.  
  66.     if (!gfEditingDlg)
  67.         return TRUE;
  68.  
  69.     /*
  70.      * Allocate a resource for the current dialog.
  71.      */
  72.     if (!(pRes = AllocDialogResource(FALSE, FALSE)))
  73.         return FALSE;
  74.  
  75.     /*
  76.      * Allocate a new link for it.
  77.      */
  78.     if (!(prlNew = AllocResLink(pRes)))
  79.         return FALSE;
  80.  
  81.     /*
  82.      * Free the local copy of the dialog resource now that the
  83.      * link has been created (and the resource copied to global
  84.      * memory).
  85.      */
  86.     MyFree(pRes);
  87.  
  88.     /*
  89.      * Does a link for the dialog already exist?
  90.      */
  91.     if (gcd.prl) {
  92.         /*
  93.          * Find the existing link and get it's previous link.
  94.          */
  95.         for (prl = gprlHead, prlPrev = NULL; prl && prl != gcd.prl;
  96.                 prlPrev = prl, prl = prl->prlNext)
  97.             ;
  98.  
  99.         /*
  100.          * Start linking it in.
  101.          */
  102.         prlNew->prlNext = gcd.prl->prlNext;
  103.  
  104.         if (prlPrev)
  105.             prlPrev->prlNext = prlNew;
  106.         else
  107.             gprlHead = prlNew;
  108.  
  109.         /*
  110.          * Delete the old link now that it is replaced.
  111.          */
  112.         FreeResLink(gcd.prl);
  113.     }
  114.     else {
  115.         /*
  116.          * Search for the end of the list.  Get a pointer to the last link.
  117.          */
  118.         for (prl = gprlHead, prlPrev = NULL; prl;
  119.                 prlPrev = prl, prl = prl->prlNext)
  120.             ;
  121.  
  122.         /*
  123.          * Add the new link to the end of the list.
  124.          */
  125.         if (prlPrev)
  126.             prlPrev->prlNext = prlNew;
  127.         else
  128.             gprlHead = prlNew;
  129.     }
  130.  
  131.     /*
  132.      * Update our global with the new link.  Clear the "dialog changed"
  133.      * flag.
  134.      */
  135.     gcd.prl = prlNew;
  136.     gfDlgChanged = FALSE;
  137.  
  138.     return TRUE;
  139. }
  140.  
  141.  
  142.  
  143. /************************************************************************
  144. * AllocDialogResource
  145. *
  146. * This function allocates memory and builds a resource file format
  147. * image in it of the current dialog.
  148. *
  149. * Arguments:
  150. *   BOOL fTestMode     - TRUE if a special test mode version of the current
  151. *                        dialog should be created.
  152. *   BOOL fClipboard    - If TRUE, only the selected control(s) will be
  153. *                        placed in the resource.  This is used when putting
  154. *                        controls or groups of controls into the clipboard.
  155. *                        If the dialog is selected, this flag is ignored,
  156. *                        because selecting the dialog implies all the
  157. *                        controls will be written out also.
  158. *
  159. * Returns:
  160. *     Pointer to the resource buffer.
  161. *     NULL if unable to create the resource.
  162. *
  163. ************************************************************************/
  164.  
  165. PRES AllocDialogResource(
  166.     BOOL fTestMode,
  167.     BOOL fClipboard)
  168. {
  169.     NPCTYPE npc;
  170.     INT cControls;
  171.     BOOL fSelectedOnly = FALSE;
  172.     INT cbDlgName;
  173.     INT cbCaption;
  174.     INT cbPointSize;
  175.     INT cbFontName;
  176.     INT cbCD;
  177.     INT cbResHeader;
  178.     INT cbResData;
  179.     INT cbResSize;
  180.     INT cbMenuName;
  181.     INT cbClass;
  182.     INT cbText;
  183.     INT cbAlloc;
  184.     PBYTE pb;
  185.     PRES pResBegin;
  186.     PRES pResBegin2;
  187.     LPTSTR pszClass;
  188.     LPTSTR pszMenu;
  189.     LPTSTR pszText;
  190.     DWORD flStyle;
  191.     PDIALOGBOXHEADER pdbh;
  192.     PCONTROLDATA pcd;
  193.     ORDINAL ordClass;
  194.  
  195.     cControls = cWindows;
  196.     if (fClipboard && !gfDlgSelected) {
  197.         fSelectedOnly = TRUE;
  198.         for (cControls = 0, npc = npcHead; npc; npc = npc->npcNext)
  199.             if (npc->fSelected)
  200.                 cControls++;
  201.     }
  202.  
  203.     /*
  204.      * If testing, don't allow a dialog to be created with any
  205.      * special class, or with the real menu.
  206.      */
  207.     if (fTestMode) {
  208.         pszClass = NULL;
  209.         pszMenu = NULL;
  210.     }
  211.     else {
  212.         pszClass = gcd.di.pszClass;
  213.         pszMenu = gcd.di.pszMenu;
  214.     }
  215.  
  216.     cbDlgName = NameOrdLen(gcd.pszDlgName);
  217.     cbCaption = (gcd.npc->text) ?
  218.             (lstrlen(gcd.npc->text) + 1) * sizeof(TCHAR) : sizeof(TCHAR);
  219.     cbClass = pszClass ? NameOrdLen(pszClass) : sizeof(TCHAR);
  220.     cbMenuName = pszMenu ? NameOrdLen(pszMenu) : sizeof(TCHAR);
  221.  
  222.     if (gcd.fFontSpecified) {
  223.         cbPointSize = sizeof(WORD);
  224.         cbFontName = (lstrlen(gcd.di.szFontName) + 1) * sizeof(TCHAR);
  225.     }
  226.     else {
  227.         cbPointSize = cbFontName = 0;
  228.     }
  229.  
  230.     /*
  231.      * Calculate the size of the resource header.
  232.      */
  233.     cbResHeader = sizeof(RES) +             // The first fixed part.
  234.             sizeof(ORDINAL) +               // The RT_DIALOG ordinal.
  235.             cbDlgName;                      // The dialog's name.
  236.     DWordAlign((PBYTE *)&cbResHeader);      // Pad for the dialog's name.
  237.     cbResHeader += sizeof(RES2);            // The last fixed part.
  238.  
  239.     /*
  240.      * Calculate the size of the resource data.  This will just include
  241.      * the dialog box header right now.
  242.      */
  243.     cbResData = SIZEOF_DIALOGBOXHEADER +    // The first fixed part.
  244.             cbMenuName +                    // The menu.
  245.             cbClass +                       // The class.
  246.             cbCaption +                     // The caption.
  247.             cbPointSize +                   // The point size.
  248.             cbFontName;                     // The font name.
  249.  
  250.     /*
  251.      * Allocate some buffer space.  Be sure to round this up to a DWORD
  252.      * boundary to allow space for padding if necessary, but don't round
  253.      * cbResData field because it will need to be written into the header
  254.      * later, and the value that is written is an exact size (not rounded
  255.      * up).
  256.      */
  257.     cbAlloc = cbResSize = cbResHeader + cbResData;
  258.     DWordAlign((PBYTE *)&cbAlloc);
  259.     if (!(pResBegin = (PRES)MyAlloc(cbAlloc)))
  260.         return NULL;
  261.  
  262.     /*
  263.      * Write the resource header.
  264.      */
  265.     pdbh = (PDIALOGBOXHEADER)WriteResHeader(pResBegin, 0, ORDID_RT_DIALOG,
  266.             gcd.pszDlgName, gcd.di.fResFlags, gcd.di.wLanguage,
  267.             gcd.di.DataVersion, gcd.di.Version, gcd.di.Characteristics);
  268.  
  269.     /*
  270.      * Write out the style.
  271.      */
  272.     flStyle = gcd.npc->flStyle;
  273.     if (fTestMode) {
  274.         flStyle &= ~awcd[W_DIALOG].flStylesTestBad;
  275.         flStyle |= WS_VISIBLE;
  276.     }
  277.  
  278.     pdbh->lStyle = flStyle;
  279.     pdbh->lExtendedStyle = gcd.npc->flExtStyle;
  280.     pdbh->NumberOfItems = (WORD)cControls;
  281.  
  282.     /*
  283.      * Write the coordinates.
  284.      *
  285.      * If we are allocating a template that only has the selected controls
  286.      * in it, we put the value of CONTROLS_ONLY in the "cx" field of the
  287.      * dialog header.  This is what we will check when the user pastes
  288.      * something from the clipboard into a dialog to determine whether
  289.      * to paste the entire dialog, or only the controls within the dialog
  290.      * item array.
  291.      */
  292.     pdbh->x = (WORD)gcd.npc->rc.left;
  293.     pdbh->y = (WORD)gcd.npc->rc.top;
  294.  
  295.     if (fSelectedOnly)
  296.         pdbh->cx = CONTROLS_ONLY;
  297.     else
  298.         pdbh->cx = (WORD)(gcd.npc->rc.right - gcd.npc->rc.left);
  299.  
  300.     pdbh->cy = (WORD)(gcd.npc->rc.bottom - gcd.npc->rc.top);
  301.  
  302.     pb = (PBYTE)pdbh + SIZEOF_DIALOGBOXHEADER;
  303.  
  304.     /*
  305.      * Write the menu name if there is one (we always write at least a null.
  306.      */
  307.     pb = NameOrdCpy((LPTSTR)pb, pszMenu ? pszMenu : szEmpty);
  308.  
  309.     /*
  310.      * Write the class if there is one (we always write at least a null.
  311.      */
  312.     pb = NameOrdCpy((LPTSTR)pb, pszClass ? pszClass : szEmpty);
  313.  
  314.     /*
  315.      * Write the caption if there is one (we always write at least a null).
  316.      */
  317.     pb = WriteSz((LPTSTR)pb, gcd.npc->text ? gcd.npc->text : szEmpty);
  318.  
  319.     /*
  320.      * Write out the font, if there is one specified.
  321.      */
  322.     if (gcd.fFontSpecified) {
  323.         *(PWORD)pb = (WORD)gcd.di.nPointSize;
  324.         pb += sizeof(WORD);
  325.  
  326.         pb = WriteSz((LPTSTR)pb, gcd.di.szFontName);
  327.     }
  328.  
  329.     /*
  330.      * Pad to a DWORD boundary.  This is ok even if there are no controls
  331.      * that follow, because we were sure to allocate on an even dword
  332.      * boundary above.
  333.      */
  334.     DWordPad(&pb);
  335.  
  336.     /*
  337.      * Now do dialog items.
  338.      */
  339.     for (npc = npcHead; npc; npc = npc->npcNext) {
  340.         /*
  341.          * Skip the control if it is NOT selected and we only want the
  342.          * selected controls.
  343.          */
  344.         if (fSelectedOnly && !npc->fSelected)
  345.             continue;
  346.  
  347.         /*
  348.          * If we are testing, we don't want to really create a control
  349.          * with some funny class because it probably won't be found.
  350.          * We will substitute our custom class emulator instead.
  351.          */
  352.         if (fTestMode && npc->pwcd->fEmulated)
  353.             pszClass = szCustomClass;
  354.         else
  355.             pszClass = npc->pwcd->pszClass;
  356.  
  357.         /*
  358.          * Get a pointer to the text.  If this is an icon control and
  359.          * we are going into test mode, change the text field so that
  360.          * it points to an ordinal for DlgEdit's icon to display, or
  361.          * the icon resource will probably not be found when the dialog
  362.          * is created.
  363.          */
  364.         pszText = npc->text;
  365.         if (npc->pwcd->iType == W_ICON && fTestMode)
  366.             pszText = (LPTSTR)&gordIcon;
  367.  
  368.         cbText = pszText ? NameOrdLen(pszText) : sizeof(TCHAR);
  369.         cbClass = pszClass ? NameOrdLen(pszClass) : sizeof(ORDINAL);
  370.  
  371.         cbCD = SIZEOF_CONTROLDATA +         // The fixed portion.
  372.                 cbClass +                   // The class.
  373.                 cbText +                    // The text.
  374.                 sizeof(WORD);               // nExtraStuff field.
  375.  
  376.         /*
  377.          * Since we are adding a new control, we dword align the
  378.          * previous size of the resource data to ensure the new
  379.          * control starts on a dword boundary.
  380.          */
  381.         DWordAlign((PBYTE *)&cbResSize);
  382.  
  383.         /*
  384.          * Allocate room for this control.  This includes room for the
  385.          * template structure, class, text and a byte for the cb field
  386.          * for the create struct data.
  387.          */
  388.         cbAlloc = cbResSize + cbCD;
  389.         DWordAlign((PBYTE *)&cbAlloc);
  390.         pResBegin2 = (PRES)MyRealloc((PBYTE)pResBegin, cbAlloc);
  391.         if (!pResBegin2) {
  392.             MyFree(pResBegin);
  393.             return NULL;
  394.         }
  395.  
  396.         pResBegin = pResBegin2;
  397.         pcd = (PCONTROLDATA)((PBYTE)pResBegin + cbResSize);
  398.         cbResSize += cbCD;
  399.  
  400.         /*
  401.          * Write the style.  If testing, remove any styles that can
  402.          * cause problems, such as ownerdraw styles.  If testing and
  403.          * this is an emulated custom control, always make it with
  404.          * the default styles no matter what the user has specified.
  405.          */
  406.         flStyle = npc->flStyle;
  407.         if (fTestMode) {
  408.             if (npc->pwcd->fEmulated)
  409.                 flStyle = awcd[W_CUSTOM].flStyles;
  410.             else
  411.                 flStyle &= ~npc->pwcd->flStylesTestBad;
  412.         }
  413.  
  414.         pcd->lStyle = flStyle;
  415.         pcd->lExtendedStyle = npc->flExtStyle;
  416.  
  417.         /*
  418.          * Write the coordinates.
  419.          */
  420.         pcd->x = (WORD)npc->rc.left;
  421.         pcd->y = (WORD)npc->rc.top;
  422.         pcd->cx = (WORD)(npc->rc.right - npc->rc.left);
  423.         pcd->cy = (WORD)(npc->rc.bottom - npc->rc.top);
  424.  
  425.         /*
  426.          * Write the id.
  427.          */
  428.         pcd->wId = (WORD)npc->id;
  429.  
  430.         pb = (PBYTE)pcd + SIZEOF_CONTROLDATA;
  431.  
  432.         /*
  433.          * Write the class.  This will be a string, except for the
  434.          * predefined control classes, which all have an ordinal
  435.          * value defined for them.
  436.          */
  437.         if (pszClass) {
  438.             pb = NameOrdCpy((LPTSTR)pb, pszClass);
  439.         }
  440.         else {
  441.             WriteOrd(&ordClass, acsd[awcd[npc->pwcd->iType].iClass].idOrd);
  442.             pb = NameOrdCpy((LPTSTR)pb, (LPTSTR)&ordClass);
  443.         }
  444.  
  445.         /*
  446.          * Write the text.
  447.          */
  448.         pb = NameOrdCpy((LPTSTR)pb, pszText ? pszText : szEmpty);
  449.  
  450.         /*
  451.          * Write out a zero because there are no additional bytes
  452.          * of create struct data.
  453.          */
  454.         *(PWORD)pb = 0;
  455.         pb += sizeof(WORD);
  456.  
  457.         /*
  458.          * Pad to a DWORD boundary.  This is ok even if there are no more
  459.          * controls, because we were sure to allocate on an even dword
  460.          * boundary above.
  461.          */
  462.         DWordPad(&pb);
  463.     }
  464.  
  465.     /*
  466.      * Now go back and fill in the resource data size field.
  467.      */
  468.     pResBegin->DataSize = cbResSize - cbResHeader;
  469.  
  470.     return pResBegin;
  471. }
  472.  
  473.  
  474.  
  475. /************************************************************************
  476. * ResLinkToDialog
  477. *
  478. * This function is used to create a dialog out of a dialog resource
  479. * that has been stored in the resource linked list.
  480. *
  481. * Arguments:
  482. *   PRESLINK prl - Points to the link that describes the dialog to
  483. *                  create.  It is assumed that the resource is a
  484. *                  dialog resource.
  485. *
  486. ************************************************************************/
  487.  
  488. VOID ResLinkToDialog(
  489.     PRESLINK prl)
  490. {
  491.     PRES pRes;
  492.  
  493.     pRes = (PRES)GlobalLock(prl->hRes);
  494.     ResToDialog(pRes, TRUE);
  495.     GlobalUnlock(prl->hRes);
  496.  
  497.     /*
  498.      * If the dialog was successfully created, remember which res link
  499.      * it was created from.
  500.      */
  501.     if (gfEditingDlg)
  502.         gcd.prl = prl;
  503. }
  504.  
  505.  
  506.  
  507. /************************************************************************
  508. * ResToDialog
  509. *
  510. * This function creates a dialog box, complete with controls,
  511. * from a dialog resource template.
  512. *
  513. * Arguments:
  514. *   PRES pRes        - Pointer to the dialog resource to use.
  515. *   BOOL fDoDialog   - TRUE if a new dialog should be created, followed
  516. *                      by the controls.  If this is FALSE, just the
  517. *                      controls will be created and added to the current
  518. *                      dialog.
  519. *
  520. * Returns:
  521. *     TRUE on success, FALSE if an error occured.
  522. *
  523. ************************************************************************/
  524.  
  525. BOOL ResToDialog(
  526.     PRES pRes,
  527.     BOOL fDoDialog)
  528. {
  529.     LPTSTR pszText;
  530.     LPTSTR pszClass;
  531.     INT x;
  532.     INT y;
  533.     INT cx;
  534.     INT cy;
  535.     INT id;
  536.     INT iClass;
  537.     INT cdit;
  538.     INT Type;
  539.     DWORD flStyle;
  540.     DWORD flExtStyle;
  541.     NPCTYPE npc;
  542.     LPTSTR pszMenuName;
  543.     LPTSTR pszFontName;
  544.     INT nPointSize;
  545.     LPTSTR pszDlgName;
  546.     LPTSTR pszCaption;
  547.     PDIALOGBOXHEADER pdbh;
  548.     PCONTROLDATA pcd;
  549.     PWINDOWCLASSDESC pwcd;
  550.     PCUSTLINK pcl;
  551.     PRES2 pRes2;
  552.     DIALOGINFO di;
  553.     CCINFO cci;
  554.  
  555.     /*
  556.      * First check that the pointer is ok.
  557.      */
  558.     if (!pRes)
  559.         return FALSE;
  560.  
  561.     pRes2 = ResourcePart2(pRes);
  562.     pdbh = (PDIALOGBOXHEADER)SkipResHeader(pRes);
  563.  
  564.     /*
  565.      * Parse out the dialog box header.
  566.      * After this point, pcd is pointing to the first dialog control item.
  567.      */
  568.     pcd = ParseDialogBoxHeader(pdbh,
  569.             &flStyle, &flExtStyle, &cdit, &x, &y, &cx, &cy,
  570.             &pszMenuName, &pszClass, &pszCaption,
  571.             &nPointSize, &pszFontName);
  572.  
  573.     /*
  574.      * Are we pasting the entire dialog?
  575.      */
  576.     if (fDoDialog) {
  577.         pszDlgName = ResourceName(pRes);
  578.  
  579.         /*
  580.          * Determine the best base id for the dialog.
  581.          */
  582.         if (IsOrd(pszDlgName))
  583.             id = OrdID(pszDlgName);
  584.         else
  585.             id = NextID(NEXTID_DIALOG, plInclude, 0);
  586.  
  587.         di.fResFlags = pRes2->MemoryFlags;
  588.         di.wLanguage = pRes2->LanguageId;
  589.         di.pszClass = pszClass;
  590.         di.pszMenu = pszMenuName;
  591.         di.DataVersion = pRes2->DataVersion;
  592.         di.Version = pRes2->Version;
  593.         di.Characteristics = pRes2->Characteristics;
  594.         di.nPointSize = nPointSize;
  595.         lstrcpy(di.szFontName, pszFontName ? pszFontName : szEmpty);
  596.  
  597.         /*
  598.          * Create the dialog.
  599.          */
  600.         if (!AddControl(&awcd[W_DIALOG], pszCaption, flStyle, flExtStyle, id,
  601.                 x, y, cx, cy, pszDlgName, &di))
  602.             return FALSE;
  603.     }
  604.  
  605.     while (cdit--) {
  606.         pcd = ParseControlData(pcd, &flStyle, &flExtStyle, &x, &y, &cx, &cy,
  607.                 &id, &pszClass, &pszText);
  608.  
  609.         /*
  610.          * If we are not creating a new dialog, and the id in
  611.          * the resource is already in use, we will use the next
  612.          * available one instead.
  613.          */
  614.         if (!fDoDialog && !IsUniqueID(id))
  615.             id = NextID(NEXTID_CONTROL, plInclude, 0);
  616.  
  617.         /*
  618.          * Fix up the class.  If the class is a predefined ordinal type,
  619.          * we will null out pszClass so it doesn't confuse AddControl
  620.          * into thinking that there is a string class for this control.
  621.          */
  622.         iClass = GetiClass(pszClass);
  623.         Type = TypeFromClassStyle(iClass, flStyle);
  624.         if (IsOrd(pszClass))
  625.             pszClass = NULL;
  626.  
  627.         if (Type == W_CUSTOM) {
  628.             /*
  629.              * Search the list of installed custom controls for one
  630.              * that matches the class.
  631.              */
  632.             for (pcl = gpclHead;
  633.                     pcl && lstrcmpi(pcl->pwcd->pszClass, pszClass) != 0;
  634.                     pcl = pcl->pclNext)
  635.                 ;
  636.  
  637.             /*
  638.              * Was a match found?
  639.              */
  640.             if (pcl) {
  641.                 pwcd = pcl->pwcd;
  642.             }
  643.             else {
  644.                 /*
  645.                  * An existing custom control link for this class was
  646.                  * not found.  We will add an emulated custom control
  647.                  * to support it.  We assume the default style and size
  648.                  * should be what this control has.
  649.                  */
  650.                 lstrcpy(cci.szClass, pszClass);
  651.                 cci.flOptions = 0;
  652.                 *cci.szDesc = TEXT('\0');
  653.                 cci.cxDefault = cx;
  654.                 cci.cyDefault = cy;
  655.                 cci.flStyleDefault = flStyle;
  656.                 cci.flExtStyleDefault = flExtStyle;
  657.                 *cci.szTextDefault = TEXT('\0');
  658.                 cci.cStyleFlags = 0;
  659.                 cci.aStyleFlags = NULL;
  660.                 cci.lpfnStyle = NULL;
  661.                 cci.lpfnSizeToText = NULL;
  662.                 cci.dwReserved1 = 0;
  663.                 cci.dwReserved2 = 0;
  664.  
  665.                 if (pcl = AddCustomLink(&cci, TRUE, FALSE, NULL, NULL))
  666.                     pwcd = pcl->pwcd;
  667.                 else
  668.                     /*
  669.                      * Skip this control and continue creating the
  670.                      * rest of the dialog.
  671.                      */
  672.                     continue;
  673.             }
  674.         }
  675.         else {
  676.             pwcd = &awcd[Type];
  677.         }
  678.  
  679.         /*
  680.          * If we are not creating the entire dialog (we allow existing
  681.          * resource files to be a little messed up), and this control
  682.          * is a default pushbutton, we will then loop through all the
  683.          * existing controls checking for another default pushbutton.
  684.          * If one is found, we convert the default pushbutton being
  685.          * created into a normal pushbutton instead.  It is not allowed
  686.          * to have more than one default pushbuttons in the same dialog.
  687.          */
  688.         if (!fDoDialog && Type == W_PUSHBUTTON &&
  689.                 (flStyle & BS_ALL) == BS_DEFPUSHBUTTON) {
  690.             for (npc = npcHead; npc; npc = npc->npcNext) {
  691.                 if (npc->pwcd->iType == W_PUSHBUTTON &&
  692.                         (npc->flStyle & BS_ALL) == BS_DEFPUSHBUTTON) {
  693.                     flStyle = (flStyle & ~BS_ALL) | BS_PUSHBUTTON;
  694.                     break;
  695.                 }
  696.             }
  697.         }
  698.  
  699.         npc = AddControl(pwcd, pszText, flStyle, flExtStyle, id,
  700.                 x, y, cx, cy, NULL, NULL);
  701.  
  702.         /*
  703.          * If the control creation succeeded, and we are just adding
  704.          * controls (not creating a whole new dialog), select the
  705.          * controls as they are added, but don't do any drawing yet.
  706.          */
  707.         if (!fDoDialog && npc)
  708.             SelectControl2(npc, TRUE);
  709.     }
  710.  
  711.     /*
  712.      * Update the selected rectangle.  This is normally done by
  713.      * SelectControl2 but we told it not to so that the selection
  714.      * could be done faster.  We also select the first control here.
  715.      */
  716.     if (!fDoDialog) {
  717.         SetAnchorToFirstSel(TRUE);
  718.         CalcSelectedRect();
  719.     }
  720.  
  721.     ShowWindow(gcd.npc->hwnd, SW_SHOWNA);
  722.     ToolboxOnTop();
  723.  
  724.     return TRUE;
  725. }
  726.  
  727.  
  728.  
  729. /************************************************************************
  730. * TypeFromClassStyle
  731. *
  732. * This function returns the type of a control (one of the W_ constants)
  733. * based on the class in iClass and the style in flStyle.
  734. *
  735. * Arguments:
  736. *     INT iClass    = The class of the control, as an IC_* defined constant.
  737. *     DWORD flStyle = The style of the control.
  738. *
  739. * Returns:
  740. *     The type of the control (W_* constant).
  741. *     W_NOTHING is the error return.
  742. *
  743. ************************************************************************/
  744.  
  745. STATICFN INT TypeFromClassStyle(
  746.     INT iClass,
  747.     DWORD flStyle)
  748. {
  749.     switch (iClass) {
  750.         case IC_BUTTON:
  751.             return rgmpiClsBtnType[flStyle & BS_ALL];
  752.  
  753.         case IC_EDIT:
  754.             return W_EDIT;
  755.  
  756.         case IC_SCROLLBAR:
  757.             return (flStyle & SBS_VERT) ? W_VERTSCROLL : W_HORZSCROLL;
  758.  
  759.         case IC_STATIC:
  760.             return rgmpiClsStcType[flStyle & SS_ALL];
  761.  
  762.         case IC_LISTBOX:
  763.             return W_LISTBOX;
  764.  
  765.         case IC_COMBOBOX:
  766.             return W_COMBOBOX;
  767.  
  768.         case IC_CUSTOM:
  769.             return W_CUSTOM;
  770.  
  771.         case IC_DIALOG:
  772.             return W_DIALOG;
  773.  
  774.         default:
  775.             return W_NOTHING;
  776.     }
  777. }
  778.  
  779.  
  780.  
  781. /************************************************************************
  782. * GetiClass
  783. *
  784. * This function returns the class identifier number for the
  785. * window class of the control, given the class string from the
  786. * dialog template.
  787. *
  788. * An ordinal class is a special ordinal that is used to identify
  789. * each of the standard control classes.  It is used in the
  790. * dialog template to save space.  The class string passed in
  791. * can be an ordinal and if so, it will be checked against these
  792. * predefined ordinal class values for a match.
  793. *
  794. * Arguments:
  795. *     LPTSTR pszClass - The class string or ordinal.
  796. *
  797. * Returns:
  798. *     The class identifier, one of the IC_* symbols in dlgedit.h.
  799. *     If the class cannot be determined, it assumes it is a custom
  800. *     class and returns IC_CUSTOM.
  801. *
  802. ************************************************************************/
  803.  
  804. INT GetiClass(
  805.     LPTSTR pszClass)
  806. {
  807.     INT i;
  808.     WORD idOrd;
  809.  
  810.     if (IsOrd(pszClass)) {
  811.         idOrd = OrdID(pszClass);
  812.         for (i = 0; i < IC_DIALOG; i++) {
  813.             if (acsd[i].idOrd == idOrd)
  814.                 return i;
  815.         }
  816.     }
  817.     else {
  818.         for (i = 0; i < IC_DIALOG; i++) {
  819.             if (lstrcmpi(ids(acsd[i].idsClass), pszClass) == 0)
  820.                 return i;
  821.         }
  822.     }
  823.  
  824.     /*
  825.      * Not found.  Assume it is a user defined class.
  826.      */
  827.     return IC_CUSTOM;
  828. }
  829.  
  830.  
  831.  
  832. /************************************************************************
  833. * Duplicate
  834. *
  835. * This routine duplicates the current selection.
  836. *
  837. ************************************************************************/
  838.  
  839. VOID Duplicate(VOID)
  840. {
  841.     PRES pRes;
  842.  
  843.     if (gcSelected) {
  844.         /*
  845.          * Store the current selection in a dialog resource.
  846.          */
  847.         if (!(pRes = AllocDialogResource(FALSE, TRUE)))
  848.             return;
  849.  
  850.         MakeCopyFromRes(pRes);
  851.     }
  852. }
  853.  
  854.  
  855.  
  856. /************************************************************************
  857. * MakeCopyFromRes
  858. *
  859. * This function uses the given dialog template to either add a new
  860. * dialog to the current resource file, or drop controls from the
  861. * template into the current dialog.  If copying a dialog, it is created
  862. * right away.  If copying controls, an operation is begun to start
  863. * tracking them to their final destination in the current dialog.
  864. *
  865. * The caller of this function should NOT free pRes.  This will
  866. * be done either before the function returns, or after the drag
  867. * operation is complete.
  868. *
  869. * Arguments:
  870. *   PRES pRes - Points to the dialog resource that contains
  871. *               the dialog or controls to make a copy of.
  872. *
  873. ************************************************************************/
  874.  
  875. VOID MakeCopyFromRes(
  876.     PRES pRes)
  877. {
  878.     PDIALOGBOXHEADER pdbh;
  879.     PCONTROLDATA pcd;
  880.     INT cControls;
  881.     INT i;
  882.     BOOL fFreeData = TRUE;
  883.     INT iType;
  884.     INT iClass;
  885.     INT nBottom;
  886.     INT nBottomLowest;
  887.  
  888.     gpResCopy = pRes;
  889.     pdbh = (PDIALOGBOXHEADER)SkipResHeader(gpResCopy);
  890.  
  891.     /*
  892.      * If cx is CONTROLS_ONLY, then we know that we only
  893.      * want to copy the controls in the template, not
  894.      * the entire dialog plus controls.
  895.      */
  896.     if (pdbh->cx == CONTROLS_ONLY) {
  897.         /*
  898.          * Begin copying in new controls into the current dialog.
  899.          */
  900.         cControls = pdbh->NumberOfItems;
  901.         if (cControls) {
  902.             /*
  903.              * Seed the rectangle with impossible values.
  904.              */
  905.             SetRect(&grcCopy, 32000, 32000, -32000, -32000);
  906.             nBottomLowest = 0;
  907.  
  908.             /*
  909.              * Loop through all the controls, expanding the rectangle
  910.              * to fit around all of them.
  911.              */
  912.             pcd = SkipDialogBoxHeader(pdbh);
  913.             for (i = 0; i < cControls; i++) {
  914.                 iClass = GetiClass((LPTSTR)((PBYTE)pcd + SIZEOF_CONTROLDATA));
  915.                 iType = TypeFromClassStyle(iClass, pcd->lStyle);
  916.  
  917.                 if (grcCopy.left > (INT)pcd->x)
  918.                     grcCopy.left = (INT)pcd->x;
  919.  
  920.                 if (grcCopy.top > (INT)pcd->y)
  921.                     grcCopy.top = (INT)pcd->y;
  922.  
  923.                 if (grcCopy.right < (INT)pcd->x + (INT)pcd->cx)
  924.                     grcCopy.right = (INT)pcd->x + (INT)pcd->cx;
  925.  
  926.                 nBottom = ((INT)pcd->y + (INT)pcd->cy) -
  927.                         GetOverHang(iType, (INT)pcd->cy);
  928.                 if (nBottom > nBottomLowest)
  929.                     nBottomLowest = nBottom;
  930.  
  931.                 if (grcCopy.bottom < (INT)pcd->y + (INT)pcd->cy)
  932.                     grcCopy.bottom = (INT)pcd->y + (INT)pcd->cy;
  933.  
  934.                 pcd = SkipControlData(pcd);
  935.             }
  936.  
  937.             /*
  938.              * Begin dragging the new control(s).  Set a flag so that
  939.              * the resource data is NOT free'd until after the drag
  940.              * is finished.
  941.              */
  942.             DragNewBegin(grcCopy.right - grcCopy.left,
  943.                     grcCopy.bottom - grcCopy.top,
  944.                     grcCopy.bottom - nBottomLowest);
  945.             fFreeData = FALSE;
  946.         }
  947.     }
  948.     else {
  949.         /*
  950.          * Begin copying in a new dialog, complete with controls.
  951.          */
  952.         if (SynchDialogResource()) {
  953.             /*
  954.              * Remove any existing dialog.
  955.              */
  956.             if (gfEditingDlg)
  957.                 DeleteDialog(FALSE);
  958.  
  959.             if (ResToDialog(gpResCopy, TRUE)) {
  960.                 SelectControl(gcd.npc, FALSE);
  961.                 gfResChged = TRUE;
  962.                 ShowFileStatus(FALSE);
  963.             }
  964.         }
  965.     }
  966.  
  967.     if (fFreeData) {
  968.         MyFree(gpResCopy);
  969.         gpResCopy = NULL;
  970.     }
  971. }
  972.