home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / CNRSAMP.ZIP / CNRADV.ZIP / CNRADV.C next >
C/C++ Source or Header  |  1992-12-15  |  96KB  |  2,624 lines

  1. /* ===================================================================*/
  2. /*         Advanced Container Sample program for ColoradOS/2          */
  3. /* ------------------------------------------------------------------ */
  4. /*                                                                    */
  5. /*  Functional Description:                                           */
  6. /*                                                                    */
  7. /*    This program is a follow on to the CNRBAS sample.  It is the    */
  8. /*    CNRBAS sample with many added features.  The features added     */
  9. /*    to this sample include:                                         */
  10. /*                                                                    */
  11. /*    - direct editing                  - scroll to record            */
  12. /*    - context menus                   - scroll to column            */
  13. /*    - processing CN_ENTER             - select/deselect all         */
  14. /*    - IN_USE emphasis                 - sorting                     */
  15. /*    - drag/drop                       - removing records            */
  16. /*    - record sharing                  - data validation             */
  17. /*                                                                    */
  18. /*    This sample does not intend to cover all aspects of container   */
  19. /*    programming, but shows how to do some of the more common and    */
  20. /*    complex aspects prevalent in a container application.           */
  21. /*                                                                    */
  22. /*    This sample was written and tested using OS/2 2.0 with the      */
  23. /*    OS/2 Service Pack.  Although most functions in this sample      */
  24. /*    will run on GA, some do require at least the Service Pack.      */
  25. /*                                                                    */
  26. /*  For: Programming the OS/2 Container Control - ColoradOS/2,        */
  27. /*       January, 11-15 1993, Colorado Springs, Colorado.             */
  28. /*                                                                    */
  29. /*  Compiled using: IBM C Set/2 1.0                                   */
  30. /*                                                                    */
  31. /*  By: Peter P. Brightbill                                           */
  32. /*      Peter F. Haggar                                               */
  33. /*                                                                    */
  34. /*  Copyright (C) 1992, IBM Corporation                               */
  35. /* ===================================================================*/
  36. #define INCL_WIN
  37. #define INCL_DOS
  38. #define INCL_WINWINDOWMGR
  39. #define INCL_WINSYS
  40. #define INCL_WINPOINTERS
  41. #define INCL_WINSTDCNR
  42. #define INCL_WINSTDDRAG
  43. #define INCL_WINMENUS
  44. #define INCL_WINMESSAGEMGR
  45. #define INCL_DOSMODULEMGR
  46. #define INCL_WININPUT
  47. #define INCL_GPILCIDS
  48. #include <os2.h>
  49. #include <stdlib.h>
  50. #include <cnradv.h>
  51.  
  52. void main(void)
  53. {
  54.   HAB   hab;
  55.   HMQ   hmq;
  56.   QMSG  qmsg;
  57.   HWND  hwndFrame;
  58.   HWND  hwndClient;
  59.   ULONG fcf = FCF_TITLEBAR | FCF_SIZEBORDER | FCF_SYSMENU |
  60.               FCF_ICON | FCF_MINMAX | FCF_SHELLPOSITION;
  61.  
  62.   hab = WinInitialize (0);
  63.   hmq = WinCreateMsgQueue (hab, 0);
  64.  
  65.   /* Register the Container sample window class.  Also reserve 4 bytes
  66.    * of memory to store a pointer to our control block.
  67.    */
  68.   WinRegisterClass (hab, "Container Sample",
  69.                     CnrSampleWndProc, 0, 4);
  70.  
  71.   /* Create a standard frame window. */
  72.   hwndFrame = WinCreateStdWindow (HWND_DESKTOP, /* Handle of desktop  */
  73.                            WS_VISIBLE,          /* Window style       */
  74.                            &fcf,                /* Creation flags     */
  75.                            "Container Sample",  /* Client Class name  */
  76.                            "Container Sample",  /* Title Bar text     */
  77.                            0,                   /* client wnd style   */
  78.                            NULLHANDLE,          /* hwnd of resources  */
  79.                            ICON_RES_ID,         /* ID of Resources    */
  80.                            &hwndClient);        /* handle of Client   */
  81.  
  82.   while (WinGetMsg (hab, &qmsg, NULLHANDLE, 0, 0))
  83.   {
  84.     WinDispatchMsg (hab, &qmsg);
  85.   }
  86.  
  87.   WinDestroyWindow (hwndFrame);
  88.   WinDestroyMsgQueue (hmq);
  89.   WinTerminate (hab);
  90. }
  91.  
  92. /*----------------------------------------------------------------------
  93.  Function Name: CnrSampleWndProc
  94.  
  95.  Description:
  96.    This is the window procedure for the client are of our window.  It is
  97.    also the owner of the container control window.
  98.  
  99.  Parameters:
  100.    (HWND)    hwnd - The handle of the client window.
  101.    (ULONG)   msg  - The message to be processed.
  102.    (MRESULT) mp1  - The first message parameter for the message.
  103.    (MRESULT) mp2  - The second message parameter for the message.
  104. ----------------------------------------------------------------------*/
  105. MRESULT EXPENTRY CnrSampleWndProc (HWND hwnd, ULONG msg,
  106.                                    MRESULT mp1, MRESULT mp2)
  107. {
  108.   SWP          swp;
  109.   HPS          hps;
  110.   RECTL        rect;
  111.   POINTL       MousePt;
  112.   ULONG        ulBorder;
  113.   CNRINFO      CnrInfo;
  114.   PSAMPLEINFO  pSampleInfo;
  115.  
  116.   switch (msg)
  117.   {
  118.     case WM_CREATE:
  119.       /* Store the hwnd of this client in a global.  We will use
  120.        * it if and when we create an additional view and share
  121.        * some records from this view.
  122.        */
  123.       vhwndMainClient = hwnd;
  124.  
  125.       /* Create the container window.  If it creates successfully,
  126.        * return FALSE, otherwise return TRUE to indicate an error.
  127.        */
  128.       if (CreateCnr (hwnd))
  129.       {
  130.         return ((MRESULT)FALSE);
  131.       }
  132.       else
  133.       {
  134.         return ((MRESULT)TRUE);
  135.       }
  136.  
  137.     case WM_PAINT:
  138.       hps = WinBeginPaint (hwnd, NULLHANDLE, &rect);
  139.       WinFillRect (hps, &rect, CLR_BLUE);
  140.       WinEndPaint (hps);
  141.     break;
  142.  
  143.     case WM_SIZE:
  144.       /* Add a border around the container by sizing the container
  145.        * to cover most of the client area.  In the area left
  146.        * uncovered, fill it with some color.  (We used BLUE)
  147.        * General note: When setting the position of only 1 window, for
  148.        * performance reasons use WinSetMultWindowPos instead of
  149.        * WinSetWindowPos.  The reason for this is because when
  150.        * setting the position of 1 window with WinSetWindowPos,
  151.        * inside of PMWIN, WinSetWindowPos calls WinSetMultWindowPos
  152.        * with a value of 1.  Therefore, coding as we do below shortens
  153.        * the code path as well as execution time.  This will not
  154.        * make or break your application, but is a good tip...also
  155.        * good as a trivia question at your next office party.
  156.        */
  157.       ulBorder = WinQuerySysValue (HWND_DESKTOP, SV_CXSIZEBORDER);
  158.       swp.fl = SWP_MOVE | SWP_SIZE;
  159.       swp.x = swp.y = ulBorder;
  160.       swp.cx = (SHORT1FROMMP(mp2) - (2 * ulBorder));
  161.       swp.cy = (SHORT2FROMMP(mp2) - (2 * ulBorder));
  162.       swp.hwndInsertBehind = NULLHANDLE;
  163.       swp.hwnd = WinWindowFromID (hwnd, CNR_SAMPLE_ID);
  164.       WinSetMultWindowPos (WinQueryAnchorBlock (hwnd), &swp, 1);
  165.     break;
  166.  
  167.     case WM_DESTROY:
  168.       /* When the user double clicks on the system menu of our frame
  169.        * window, or chooses the QUIT option from our menu, call a
  170.        * function to free up the resources utilized by this application.
  171.        */
  172.       CleanupCnr (hwnd);
  173.     break;
  174.  
  175.     case WM_CONTROL:
  176.       switch (SHORT2FROMMP(mp1))
  177.       {
  178.         /* The use requested a context (popup) menu.  Get the
  179.          * position of the mouse and put up the menu at that
  180.          * location.
  181.          */
  182.         case CN_CONTEXTMENU:
  183.           WinQueryMsgPos (WinQueryAnchorBlock (hwnd), &MousePt);
  184.           ProcessContextMenu (hwnd, MousePt.x,
  185.                               MousePt.y, (PPERSONRECORD)mp2);
  186.         break;
  187.  
  188.         case CN_REALLOCPSZ:
  189.           return ((MRESULT)ProcessDirectEdit (hwnd, (PCNREDITDATA)mp2));
  190.  
  191.         case CN_ENTER:
  192.           ProcessEnter (hwnd,(PNOTIFYRECORDENTER)mp2);
  193.         break;
  194.  
  195.         case CN_INITDRAG:
  196.           /* Get the pointer to our control block. */
  197.           pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  198.  
  199.           ProcessInitDrag( hwnd, pSampleInfo->hwndCnr,
  200.                            ((PCNRDRAGINIT) (PVOIDFROMMP(mp2))) );
  201.         break;
  202.  
  203.         case CN_DROP:
  204.           /* Get the pointer to our control block. */
  205.           pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  206.  
  207.           ProcessDrop( hwnd, pSampleInfo->hwndCnr,
  208.                        (PCNRDRAGINFO)(PVOIDFROMMP(mp2)) );
  209.         break;
  210.  
  211.         case CN_DRAGAFTER:
  212.           /* Get the pointer to our control block. */
  213.           pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  214.  
  215.           /* Set this flag to indicate that CN_DRAGAFTER was last
  216.            * received.  This information is needed when using
  217.            * CA_MIXEDTARGETEMPHASIS and the CN_DROP notification
  218.            * is received.  See ProcessDrop below.
  219.            */
  220.           pSampleInfo->bDragAfter = TRUE;
  221.           return(MRFROM2SHORT(DOR_DROP, DO_MOVE));
  222.  
  223.         case CN_DRAGOVER:
  224.           /* Get the pointer to our control block. */
  225.           pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  226.  
  227.           /* Set this flag to indicate that CN_DRAGOVER was last
  228.            * received.  This information is needed when using
  229.            * CA_MIXEDTARGETEMPHASIS and the CN_DROP notification
  230.            * is received.  See ProcessDrop below.
  231.            */
  232.           pSampleInfo->bDragAfter = FALSE;
  233.           return(MRFROM2SHORT(DOR_DROP, DO_MOVE));
  234.  
  235.         default:
  236.           return (WinDefWindowProc (hwnd, msg, mp1, mp2));
  237.       }
  238.       break;
  239.  
  240.     case WM_COMMAND:
  241.       /* Get the pointer to our control block. */
  242.       pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  243.  
  244.       /* The following messages are received when the user chooses
  245.        * one of the menu options on our action bar.
  246.        */
  247.       switch (SHORT1FROMMP(mp1))
  248.       {
  249.         case TEXTV_ID:
  250.           /* Switch the container to Text view. */
  251.           CnrInfo.flWindowAttr = CV_TEXT | CA_ORDEREDTARGETEMPH;
  252.           WinSendMsg (pSampleInfo->hwndCnr, CM_SETCNRINFO,
  253.                       MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR));
  254.         break;
  255.  
  256.         case TEXTV_FLOWED_ID:
  257.           /* Switch the container to Flowed Text view. */
  258.           CnrInfo.flWindowAttr = CV_TEXT | CV_FLOW |
  259.                                  CA_ORDEREDTARGETEMPH;
  260.           WinSendMsg (pSampleInfo->hwndCnr, CM_SETCNRINFO,
  261.                       MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR));
  262.         break;
  263.  
  264.         case NAMEV_ID:
  265.           /* Switch the container to Name view with a container
  266.            * title.
  267.            */
  268.           CnrInfo.flWindowAttr = CV_NAME | CA_CONTAINERTITLE |
  269.                                  CA_TITLESEPARATOR | CA_TITLEREADONLY |
  270.                                  CA_MIXEDTARGETEMPH;
  271.           CnrInfo.pszCnrTitle = pSampleInfo->pszCnrTitle;
  272.           WinSendMsg (pSampleInfo->hwndCnr, CM_SETCNRINFO,
  273.                       MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR |
  274.                                                     CMA_CNRTITLE));
  275.         break;
  276.  
  277.         case NAMEV_FLOWED_ID:
  278.           /* Switch the container to Flowed Name view with a
  279.            * container title.
  280.            */
  281.           CnrInfo.flWindowAttr = CV_NAME | CV_FLOW | CA_MIXEDTARGETEMPH;
  282.           WinSendMsg (pSampleInfo->hwndCnr, CM_SETCNRINFO,
  283.                       MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR));
  284.         break;
  285.  
  286.         case ICONV_ID:
  287.           /* Set the container to Icon view with a container title,
  288.            * then arrange it so the records display orderly.
  289.            */
  290.           CnrInfo.flWindowAttr = CV_ICON | CA_CONTAINERTITLE |
  291.                                  CA_TITLESEPARATOR | CA_TITLEREADONLY;
  292.           CnrInfo.pszCnrTitle = pSampleInfo->pszCnrTitle;
  293.  
  294.           WinSendMsg (pSampleInfo->hwndCnr, CM_SETCNRINFO,
  295.                       MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR |
  296.                                                     CMA_CNRTITLE));
  297.           WinSendMsg (pSampleInfo->hwndCnr, CM_ARRANGE, NULL, NULL);
  298.         break;
  299.  
  300.         case TREEV_ID:
  301.           /* Switch the container to Tree view.  If this is the
  302.            * first time the user has requested Tree view, populate
  303.            * it first.  Also, add the container title to Tree view.
  304.            */
  305.           if (!pSampleInfo->bTreePopulated)
  306.           {
  307.             if (PopulateTree (hwnd))
  308.             {
  309.               pSampleInfo->bTreePopulated = TRUE;
  310.             }
  311.             else
  312.             {
  313.               return ((MRESULT)NULL);
  314.             }
  315.           }
  316.           CnrInfo.flWindowAttr = CV_TREE | CV_ICON | CA_TREELINE |
  317.                                  CA_CONTAINERTITLE | CA_TITLESEPARATOR |
  318.                                  CA_TITLEREADONLY;
  319.           CnrInfo.pszCnrTitle = pSampleInfo->pszCnrTitle;
  320.  
  321.           WinSendMsg (pSampleInfo->hwndCnr, CM_SETCNRINFO,
  322.                       MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR |
  323.                                                     CMA_CNRTITLE));
  324.         break;
  325.  
  326.         case DETAILSV_ID:
  327.           /* Switch the container to Details view.  Set the
  328.            * splitbar in the middle of the container window.
  329.            */
  330.           WinQueryWindowRect (pSampleInfo->hwndCnr, &rect);
  331.           CnrInfo.flWindowAttr = CV_DETAIL | CA_DETAILSVIEWTITLES |
  332.                                  CA_MIXEDTARGETEMPH;
  333.           CnrInfo.xVertSplitbar = rect.xRight / 2;
  334.           CnrInfo.pFieldInfoLast = pSampleInfo->pFieldInfoLast;
  335.           WinSendMsg (pSampleInfo->hwndCnr, CM_SETCNRINFO,
  336.                       MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR |
  337.                       CMA_XVERTSPLITBAR | CMA_PFIELDINFOLAST));
  338.         break;
  339.  
  340.         case SORT_BY_NAME:
  341.         case SORT_BY_BIRTHDAY:
  342.           if (SHORT1FROMMP(mp1) == SORT_BY_NAME)
  343.           {
  344.             WinSendMsg (pSampleInfo->hwndCnr, CM_SORTRECORD,
  345.                         MPFROMP(pfnCompareName), MPFROMP(NULL) );
  346.           }
  347.           else
  348.           {
  349.             WinSendMsg (pSampleInfo->hwndCnr, CM_SORTRECORD,
  350.                         MPFROMP(pfnCompareBDay), MPFROMP(NULL) );
  351.            }
  352.            WinSendMsg (pSampleInfo->hwndCnr, CM_ARRANGE, NULL, NULL);
  353.         break;
  354.  
  355.         case SCROLL_TO_CURSORED_REC:
  356.         {
  357.           PMINIRECORDCORE  pCursoredRec;
  358.  
  359.           pCursoredRec = (PMINIRECORDCORE)WinSendMsg(pSampleInfo->hwndCnr,
  360.                                       CM_QUERYRECORDEMPHASIS,
  361.                                       MPFROMP((PRECORDCORE)CMA_FIRST),
  362.                                       MPFROMLONG(CRA_CURSORED) );
  363.  
  364.           ScrollToRecord(pSampleInfo->hwndCnr, (PRECORDCORE)pCursoredRec);
  365.         }
  366.         break;
  367.  
  368.         case SCROLL_TO_TOB_COLUMN:
  369.           if (WinSendMsg (pSampleInfo->hwndCnr, CM_QUERYCNRINFO,
  370.                        MPFROMP(&CnrInfo), MPFROMSHORT(sizeof(CNRINFO))))
  371.           {
  372.             if (CnrInfo.flWindowAttr & CV_DETAIL)
  373.             {
  374.               ScrollToColumn (pSampleInfo->hwndCnr,
  375.                               pSampleInfo->pScrollColumn);
  376.             }
  377.           }
  378.         break;
  379.  
  380.         /* This code will select all the records in the container by
  381.          * sending the key event of <CTRL>[/] (Control, forward slash)
  382.          * to the container.  This method is much faster than
  383.          * querying all records and setting their selection emphasis
  384.          * via CM_SETRECORDEMPHASIS.
  385.          */
  386.         case SELECT_ALL:
  387.         {
  388.           BYTE   KeyStateTable[256];
  389.  
  390.           WinSetKeyboardStateTable( HWND_DESKTOP,
  391.                                     (PBYTE)&KeyStateTable[0],
  392.                                     FALSE );
  393.           KeyStateTable[VK_CTRL] |= 0x80;
  394.           WinSetKeyboardStateTable( HWND_DESKTOP,
  395.                                     (PBYTE)(&KeyStateTable[0]),
  396.                                     TRUE );
  397.           WinSendMsg(pSampleInfo->hwndCnr,
  398.                      WM_CHAR,
  399.                      MPFROMP(NULL),
  400.                      MPFROM2SHORT((SHORT)0X002f, (SHORT)0X0000) );
  401.  
  402.           KeyStateTable[VK_CTRL] &= !(0x80);
  403.           WinSetKeyboardStateTable( HWND_DESKTOP,
  404.                                     (PBYTE)(&KeyStateTable[0]),
  405.                                     TRUE );
  406.         }
  407.         break;
  408.  
  409.         /* This code will deselect all the records in the container by
  410.          * sending the key event of <CTRL>[\] (Control, backslash)
  411.          * to the container.  This method is much faster than
  412.          * querying all records and removing their selection emphasis
  413.          * via CM_SETRECORDEMPHASIS.
  414.          */
  415.         case DESELECT_ALL:
  416.         {
  417.           BYTE   KeyStateTable[256];
  418.  
  419.           WinSetKeyboardStateTable( HWND_DESKTOP,
  420.                                     (PBYTE)&KeyStateTable[0],
  421.                                     FALSE );
  422.           KeyStateTable[VK_CTRL] |= 0x80;
  423.           WinSetKeyboardStateTable( HWND_DESKTOP,
  424.                                     (PBYTE)(&KeyStateTable[0]),
  425.                                     TRUE );
  426.           WinSendMsg(pSampleInfo->hwndCnr,
  427.                      WM_CHAR,
  428.                      MPFROMP(NULL),
  429.                      MPFROM2SHORT((SHORT)0X005C, (SHORT)0X0000) );
  430.  
  431.           KeyStateTable[VK_CTRL] &= !(0x80);
  432.           WinSetKeyboardStateTable( HWND_DESKTOP,
  433.                                     (PBYTE)(&KeyStateTable[0]),
  434.                                     TRUE );
  435.         }
  436.         break;
  437.  
  438.         case SAMPLE_MENU_QUIT:
  439.           /* The user has requested to quit the application.  Post
  440.            * a WM_QUIT to ourselves and bail.
  441.            */
  442.           WinPostMsg (hwnd, WM_QUIT, 0, 0);
  443.         break;
  444.  
  445.         case REMOVE_RECORDS:
  446.           RemoveRecords (hwnd);
  447.         break;
  448.  
  449.         default:
  450.           return (WinDefWindowProc (hwnd, msg, mp1, mp2));
  451.       }
  452.       break;
  453.  
  454.     default:
  455.       return (WinDefWindowProc (hwnd, msg, mp1, mp2));
  456.   }
  457.   return (0);
  458. }
  459.  
  460. /*----------------------------------------------------------------------
  461.  Function Name: CreateCnr
  462.  
  463.  Description:
  464.    This function creates a container window, then populates the
  465.    container with 5 records.
  466.  
  467.  Parameters:
  468.    (HWND) hwnd - The handle of the client window that we are creating
  469.                  the container in.
  470. ----------------------------------------------------------------------*/
  471. BOOL CreateCnr (HWND hwnd)
  472. {
  473.   HWND         hwndCnr;
  474.   PSAMPLEINFO  pSampleInfo;
  475.   CNRINFO      CnrInfo;
  476.   BOOL         rc = TRUE;
  477.  
  478.   /* Create the container window. */
  479.   hwndCnr = WinCreateWindow (hwnd,                    /* Parent       */
  480.                          WC_CONTAINER,                /* Class        */
  481.                          NULL,                        /* Window text  */
  482.                          WS_VISIBLE | CCS_EXTENDSEL | /* Window style */
  483.                          CCS_MINIRECORDCORE,
  484.                          0,0,                         /* x,y          */
  485.                          0,0,                         /* cx, cy       */
  486.                          hwnd,                        /* Owner        */
  487.                          HWND_TOP,                    /* placement    */
  488.                          CNR_SAMPLE_ID,               /* Window ID    */
  489.                          NULL,                        /* Control data */
  490.                          NULL);                       /* presparams   */
  491.  
  492.   /* If the window is created successfully, allocate some memory
  493.    * for our control block and initialize it to 0.  Then, set the
  494.    * information in it and store a pointer to this structure in the
  495.    * window words we reserved on the WinRegisterClass call.
  496.    */
  497.   if (hwndCnr)
  498.   {
  499.     pSampleInfo = malloc (sizeof(SAMPLEINFO));
  500.     if (pSampleInfo)
  501.     {
  502.       memset (pSampleInfo, 0, sizeof(SAMPLEINFO));
  503.       pSampleInfo->hwndCnr = hwndCnr;
  504.       WinSetWindowPtr (hwnd, QWL_USER, pSampleInfo);
  505.  
  506.       /* Give the container a title and a horizontal separator to
  507.        * separate the title from the viewport.  Store a pointer to
  508.        * the container title in our control block since we will
  509.        * need it whenever the user switches to a view that we want
  510.        * to display the title in.
  511.        */
  512.       pSampleInfo->pszCnrTitle = malloc(TEXT_SIZE);
  513.       if (pSampleInfo->pszCnrTitle)
  514.       {
  515.         strcpy (pSampleInfo->pszCnrTitle,
  516.                 "Advanced Container\r\nSample Program");
  517.         CnrInfo.pszCnrTitle = pSampleInfo->pszCnrTitle;
  518.         CnrInfo.flWindowAttr = CV_ICON | CA_CONTAINERTITLE |
  519.                                CA_TITLESEPARATOR | CA_TITLEREADONLY;
  520.         WinSendMsg (hwndCnr,
  521.                     CM_SETCNRINFO,
  522.                     MPFROMP(&CnrInfo),
  523.                     MPFROMLONG(CMA_CNRTITLE | CMA_FLWINDOWATTR));
  524.  
  525.         /* Populate the container with 5 records.  This function will
  526.          * also set up the details view information for each record.
  527.          */
  528.         if (!PopulateCnr (hwnd))
  529.         {
  530.           rc = FALSE;
  531.         }
  532.       }
  533.       else
  534.       {
  535.         rc = FALSE;  /* Out of memory error */
  536.       }
  537.     }
  538.     else
  539.     {
  540.       rc = FALSE; /* Out of memory error */
  541.     }
  542.   }
  543.   else
  544.   {
  545.     rc = FALSE; /* Creation error */
  546.   }
  547.  
  548.   if (!rc)
  549.   {
  550.     if (hwndCnr)
  551.     {
  552.       WinDestroyWindow (hwndCnr);
  553.     }
  554.     if (pSampleInfo)
  555.     {
  556.       if (pSampleInfo->pszCnrTitle)
  557.       {
  558.         free (pSampleInfo->pszCnrTitle);
  559.       }
  560.       free (pSampleInfo);
  561.     }
  562.   }
  563.   else
  564.   {
  565.     /* Give the container the focus and return. */
  566.     WinSetFocus (HWND_DESKTOP, hwndCnr);
  567.   }
  568.   return (rc);
  569. }
  570.  
  571. /*----------------------------------------------------------------------
  572.  Function Name: PopulateCnr
  573.  
  574.  Description:
  575.    This function allocates 5 container records, assigns the appropriate
  576.    information, including the information needed for details view and
  577.    finally inserts the 5 records into the container.
  578.  
  579.  Parameters:
  580.    (HWND) hwnd - The handle of the client window.
  581. ----------------------------------------------------------------------*/
  582. BOOL PopulateCnr (HWND hwnd)
  583. {
  584.   PSAMPLEINFO    pSampleInfo;
  585.   PPERSONRECORD  pPersonRec;
  586.   PPERSONRECORD  pPersonRecFirst;
  587.   HPOINTER       hptrPersonIcon;
  588.   USHORT         i;
  589.   RECORDINSERT   RecordInsert;
  590.   BOOL           rc = TRUE;
  591.   ULONG          ulNumRecords = 5;
  592.  
  593.   /* Get the pointer to our control block. */
  594.   pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  595.  
  596.   /* Load the person icon that will be used by the container in views
  597.    * which show icons.  These icons are used multiple times, but
  598.    * be sure to only load them once and reuse the HPOINTER.
  599.    */
  600.   pSampleInfo->hptrPersonIcon = WinLoadPointer (HWND_DESKTOP,
  601.                                                 NULLHANDLE,
  602.                                                 ID_PERSON_ICON);
  603.  
  604.   /* Load the job icon that will be used for the child records
  605.    * in Tree View.
  606.    */
  607.   pSampleInfo->hptrJobIcon = WinLoadPointer (HWND_DESKTOP,
  608.                                              NULLHANDLE,
  609.                                              ID_JOB_ICON);
  610.  
  611.   /* Allocate and insert the fieldinfo structures. */
  612.   if (!SetupAndAddFieldInfos (hwnd))
  613.   {
  614.     return (FALSE);
  615.   }
  616.  
  617.   /* Allocate a linked list of records.  Note how MP1 is calculated.
  618.    * It is the number of ADDITIONAL bytes that you want the container
  619.    * to allocate per record.  Since PERSONRECORD contains a
  620.    * MINIRECORDCORE, we subtract it out.  Now if you ever change the
  621.    * size of PERSONRECORD by adding or subtracting any extra fields,
  622.    * this code will not have to change.
  623.    */
  624.   pPersonRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
  625.                                        CM_ALLOCRECORD,
  626.                                        MPFROMLONG(sizeof(PERSONRECORD) -
  627.                                        sizeof(MINIRECORDCORE)),
  628.                                        MPFROMLONG(ulNumRecords));
  629.  
  630.   /* Save a pointer to the front of the list of records returned.  This
  631.    * will be used when we insert the records at the bottom of this
  632.    * function.
  633.    */
  634.   pPersonRecFirst = pPersonRec;
  635.  
  636.   /* Loop through all the records assigning the necessary
  637.    * information.
  638.    */
  639.   i = 1;
  640.   while ((pPersonRec) && (rc))
  641.   {
  642.     pPersonRec->MiniRec.hptrIcon = pSampleInfo->hptrPersonIcon;
  643.     pPersonRec->MiniRec.pszIcon = malloc (TEXT_SIZE);
  644.  
  645.     /* If we successfully allocated the memory, copy the text string
  646.      * to the pszIcon field and go to the next record in the linked
  647.      * list.
  648.      */
  649.     if (pPersonRec->MiniRec.pszIcon)
  650.     {
  651.       /* Set up the record specific data for each record, including
  652.        * the details view data.
  653.        */
  654.       switch (i)
  655.       {
  656.         case 1:
  657.           strcpy (pPersonRec->MiniRec.pszIcon, "Peter Haggar");
  658.           strcpy (pPersonRec->szMiddleInit, "F");
  659.           pPersonRec->pszMiddleInit = pPersonRec->szMiddleInit;
  660.           pPersonRec->DateOfBirth.month = 2;
  661.           pPersonRec->DateOfBirth.day   = 3;
  662.           pPersonRec->DateOfBirth.year  = 65;
  663.           pPersonRec->TimeOfBirth.hours   = 3;
  664.           pPersonRec->TimeOfBirth.minutes = 30;
  665.           pPersonRec->TimeOfBirth.seconds = 0;
  666.           pPersonRec->CurrentAge = 27;
  667.           pPersonRec->usJob = JR_DEVELOPMENT;
  668.         break;
  669.  
  670.         case 2:
  671.           strcpy (pPersonRec->MiniRec.pszIcon, "Peter Brightbill");
  672.           strcpy (pPersonRec->szMiddleInit, "P");
  673.           pPersonRec->pszMiddleInit = pPersonRec->szMiddleInit;
  674.           pPersonRec->DateOfBirth.month = 1;
  675.           pPersonRec->DateOfBirth.day   = 23;
  676.           pPersonRec->DateOfBirth.year  = 65;
  677.           pPersonRec->TimeOfBirth.hours   = 17;
  678.           pPersonRec->TimeOfBirth.minutes = 10;
  679.           pPersonRec->TimeOfBirth.seconds = 47;
  680.           pPersonRec->CurrentAge = 27;
  681.           pPersonRec->usJob = JR_DEVELOPMENT;
  682.         break;
  683.  
  684.         case 3:
  685.           strcpy (pPersonRec->MiniRec.pszIcon, "Tai Nam");
  686.           strcpy (pPersonRec->szMiddleInit, "W");
  687.           pPersonRec->pszMiddleInit = pPersonRec->szMiddleInit;
  688.           pPersonRec->DateOfBirth.month = 2;
  689.           pPersonRec->DateOfBirth.day   = 3;
  690.           pPersonRec->DateOfBirth.year  = 60;
  691.           pPersonRec->TimeOfBirth.hours   = 10;
  692.           pPersonRec->TimeOfBirth.minutes = 56;
  693.           pPersonRec->TimeOfBirth.seconds = 19;
  694.           pPersonRec->CurrentAge = 32;
  695.           pPersonRec->usJob = JR_DEVELOPMENT;
  696.         break;
  697.  
  698.         case 4:
  699.           strcpy (pPersonRec->MiniRec.pszIcon, "Joe Shmo");
  700.           strcpy (pPersonRec->szMiddleInit, "D");
  701.           pPersonRec->pszMiddleInit = pPersonRec->szMiddleInit;
  702.           pPersonRec->DateOfBirth.month = 10;
  703.           pPersonRec->DateOfBirth.day   = 29;
  704.           pPersonRec->DateOfBirth.year  = 70;
  705.           pPersonRec->TimeOfBirth.hours   = 23;
  706.           pPersonRec->TimeOfBirth.minutes = 5;
  707.           pPersonRec->TimeOfBirth.seconds = 36;
  708.           pPersonRec->CurrentAge = 22;
  709.           pPersonRec->usJob = JR_SUPPORT;
  710.         break;
  711.  
  712.         case 5:
  713.           strcpy (pPersonRec->MiniRec.pszIcon, "John Public");
  714.           strcpy (pPersonRec->szMiddleInit, "Q");
  715.           pPersonRec->pszMiddleInit = pPersonRec->szMiddleInit;
  716.           pPersonRec->DateOfBirth.month = 5;
  717.           pPersonRec->DateOfBirth.day   = 17;
  718.           pPersonRec->DateOfBirth.year  = 61;
  719.           pPersonRec->TimeOfBirth.hours   = 19;
  720.           pPersonRec->TimeOfBirth.minutes = 3;
  721.           pPersonRec->TimeOfBirth.seconds = 9;
  722.           pPersonRec->CurrentAge = 31;
  723.           pPersonRec->usJob = JR_SUPPORT;
  724.         break;
  725.       }
  726.       pPersonRec = (PPERSONRECORD)pPersonRec->MiniRec.preccNextRecord;
  727.       i++;
  728.     }
  729.     else
  730.     {
  731.       rc = FALSE;
  732.     }
  733.   }
  734.  
  735.   /* Now that we have the data assigned, insert the records in the
  736.    * container.  First set up the RECORDINSERT structure which tells
  737.    * the container how to insert the records.
  738.    */
  739.   RecordInsert.cb = sizeof(RECORDINSERT);
  740.   RecordInsert.pRecordOrder = (PRECORDCORE)CMA_FIRST;
  741.   RecordInsert.pRecordParent = NULL;
  742.   RecordInsert.zOrder = CMA_TOP;
  743.   RecordInsert.cRecordsInsert = ulNumRecords;
  744.   RecordInsert.fInvalidateRecord = TRUE;
  745.  
  746.   /* Since the container will be coming up in Icon view, after inserting
  747.    * the records, send the CM_ARRANGE message to arrange the icons in
  748.    * the container viewport.
  749.    */
  750.   if (WinSendMsg (pSampleInfo->hwndCnr,
  751.                   CM_INSERTRECORD,
  752.                   MPFROMP(pPersonRecFirst),
  753.                   MPFROMP(&RecordInsert)))
  754.   {
  755.     WinSendMsg (pSampleInfo->hwndCnr, CM_ARRANGE, NULL, NULL);
  756.   }
  757.   else
  758.   {
  759.     rc = FALSE;
  760.   }
  761.  
  762.   return (rc);
  763. }
  764.  
  765. /*----------------------------------------------------------------------
  766.  Function Name: SetupAndAddFieldInfos
  767.  
  768.  Description:
  769.    This function allocates, sets up, and inserts the 6 FieldInfo
  770.    structures that are used to describe each column for the Details
  771.    view.
  772.  
  773.  Parameters:
  774.    (HWND) hwnd - The handle of the client window.
  775. ----------------------------------------------------------------------*/
  776. BOOL SetupAndAddFieldInfos (HWND hwnd)
  777. {
  778.   PSAMPLEINFO      pSampleInfo;
  779.   PFIELDINFO       pFieldInfo;
  780.   PFIELDINFO       pFieldInfoFirst;
  781.   FIELDINFOINSERT  FieldInfoInsert;
  782.   BOOL             rc = TRUE;
  783.   USHORT           usNumFieldInfo = 6;
  784.   USHORT           i = 1;
  785.  
  786.   /* Get the pointer to our control block. */
  787.   pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  788.  
  789.   /* Allocate the 6 FieldInfo structures used for the columns in
  790.    * Details view.
  791.    */
  792.   pFieldInfo = WinSendMsg (pSampleInfo->hwndCnr,
  793.                            CM_ALLOCDETAILFIELDINFO,
  794.                            MPFROMSHORT(usNumFieldInfo),
  795.                            NULL);
  796.  
  797.   pFieldInfoFirst = pFieldInfo;
  798.  
  799.   /* Loop through each FieldInfo and assign the data as necessary.
  800.    * Pay particular attention to the offStruct field.  This tells
  801.    * the container at what offset from the beginning of the
  802.    * RECORDCORE/MINIRECORDCORE to find the data that is to be
  803.    * displayed in the column.
  804.    */
  805.   while ((pFieldInfo) && (rc))
  806.   {
  807.     switch (i)
  808.     {
  809.       case 1:
  810.         pFieldInfo->flTitle = CFA_BITMAPORICON;
  811.         pFieldInfo->pTitleData = (PVOID)pSampleInfo->hptrPersonIcon;
  812.         pFieldInfo->flData = CFA_BITMAPORICON | CFA_HORZSEPARATOR |
  813.                              CFA_SEPARATOR;
  814.         pFieldInfo->offStruct = FIELDOFFSET(PERSONRECORD,
  815.                                             MiniRec.hptrIcon);
  816.       break;
  817.  
  818.       case 2:
  819.         pSampleInfo->pFieldInfoLast = pFieldInfo;
  820.         pFieldInfo->flTitle = CFA_STRING;
  821.         pFieldInfo->pTitleData = malloc(TEXT_SIZE);
  822.         if (pFieldInfo->pTitleData)
  823.         {
  824.           strcpy(pFieldInfo->pTitleData, "Name");
  825.           pFieldInfo->flData = CFA_STRING | CFA_HORZSEPARATOR;
  826.           pFieldInfo->offStruct = FIELDOFFSET(PERSONRECORD,
  827.                                               MiniRec.pszIcon);
  828.         }
  829.         else
  830.         {
  831.           rc = FALSE;
  832.         }
  833.       break;
  834.  
  835.       case 3:
  836.         pFieldInfo->flTitle = CFA_STRING;
  837.         pFieldInfo->pTitleData = malloc(TEXT_SIZE);
  838.         if (pFieldInfo->pTitleData)
  839.         {
  840.           strcpy(pFieldInfo->pTitleData, "Middle Initial");
  841.           pFieldInfo->flData = CFA_STRING | CFA_CENTER |
  842.                                CFA_HORZSEPARATOR;
  843.           pFieldInfo->offStruct = FIELDOFFSET(PERSONRECORD,
  844.                                               pszMiddleInit);
  845.         }
  846.         else
  847.         {
  848.           rc = FALSE;
  849.         }
  850.       break;
  851.  
  852.       case 4:
  853.         pFieldInfo->flTitle = CFA_STRING;
  854.         pFieldInfo->pTitleData = malloc(TEXT_SIZE);
  855.         if (pFieldInfo->pTitleData)
  856.         {
  857.           strcpy(pFieldInfo->pTitleData, "Date of Birth");
  858.           pFieldInfo->flData = CFA_DATE | CFA_RIGHT | CFA_HORZSEPARATOR;
  859.           pFieldInfo->offStruct = FIELDOFFSET(PERSONRECORD,
  860.                                               DateOfBirth);
  861.         }
  862.         else
  863.         {
  864.           rc = FALSE;
  865.         }
  866.       break;
  867.  
  868.       case 5:
  869.         pSampleInfo->pScrollColumn = pFieldInfo;
  870.         pFieldInfo->flTitle = CFA_STRING;
  871.         pFieldInfo->pTitleData = malloc(TEXT_SIZE);
  872.         if (pFieldInfo->pTitleData)
  873.         {
  874.           strcpy(pFieldInfo->pTitleData, "Time of Birth");
  875.           pFieldInfo->flData = CFA_TIME | CFA_RIGHT | CFA_HORZSEPARATOR;
  876.           pFieldInfo->offStruct = FIELDOFFSET(PERSONRECORD,
  877.                                               TimeOfBirth);
  878.         }
  879.         else
  880.         {
  881.           rc = FALSE;
  882.         }
  883.       break;
  884.  
  885.       case 6:
  886.         pFieldInfo->flTitle = CFA_STRING | CFA_CENTER;
  887.         pFieldInfo->pTitleData = malloc(TEXT_SIZE);
  888.         if (pFieldInfo->pTitleData)
  889.         {
  890.           strcpy(pFieldInfo->pTitleData, "Current\r\nAge");
  891.           pFieldInfo->flData = CFA_ULONG | CFA_RIGHT |
  892.                                CFA_HORZSEPARATOR;
  893.           pFieldInfo->offStruct = FIELDOFFSET(PERSONRECORD,
  894.                                               CurrentAge);
  895.         }
  896.         else
  897.         {
  898.           rc = FALSE;
  899.         }
  900.       break;
  901.     }
  902.     pFieldInfo = pFieldInfo->pNextFieldInfo;
  903.     i++;
  904.   }
  905.  
  906.   if (rc)
  907.   {
  908.     FieldInfoInsert.cb = sizeof(FIELDINFOINSERT);
  909.     FieldInfoInsert.pFieldInfoOrder = (PFIELDINFO)CMA_FIRST;
  910.     FieldInfoInsert.cFieldInfoInsert = usNumFieldInfo;
  911.     FieldInfoInsert.fInvalidateFieldInfo = FALSE;
  912.     WinSendMsg (pSampleInfo->hwndCnr, CM_INSERTDETAILFIELDINFO,
  913.                 MPFROMP(pFieldInfoFirst), MPFROMP(&FieldInfoInsert));
  914.   }
  915.  
  916.   return (rc);
  917. }
  918.  
  919. /*----------------------------------------------------------------------
  920.  Function Name: PopulateTree
  921.  
  922.  Description:
  923.    This functions adds 3 child records to each of the records that are
  924.    currently in the container.
  925.  
  926.  Parameters:
  927.    (HWND) hwnd - The handle of the client window.
  928. ----------------------------------------------------------------------*/
  929. BOOL PopulateTree (HWND hwnd)
  930. {
  931.   PSAMPLEINFO    pSampleInfo;
  932.   PPERSONRECORD  pPersonRec;
  933.   BOOL           rc = TRUE;
  934.  
  935.   /* Get the pointer to our control block. */
  936.   pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  937.  
  938.   /* Get the first root item in the tree.  Add its children, then
  939.    * continue adding the children of the next root level item.
  940.    */
  941.   pPersonRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
  942.                                           CM_QUERYRECORD,
  943.                                           NULL,
  944.                               MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
  945.  
  946.   while ((pPersonRec) && (rc))
  947.   {
  948.     if (AddChildren (hwnd, pPersonRec))
  949.     {
  950.       pPersonRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
  951.                                               CM_QUERYRECORD,
  952.                                               MPFROMP(pPersonRec),
  953.                                  MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
  954.     }
  955.     else
  956.     {
  957.       rc = FALSE;
  958.     }
  959.   }
  960.  
  961.   return (rc);
  962. }
  963.  
  964. /*----------------------------------------------------------------------
  965.  Function Name: AddChildren
  966.  
  967.  Description:
  968.    This function allocates the record memory for each child record and
  969.    inserts them as children of the parent record passed in as the
  970.    second parameter.
  971.  
  972.  Parameters:
  973.    (HWND) hwnd                - The handle of the client window.
  974.    (PPERSONRECORD) pParentRec - The parent record for the children
  975.                                 records we are creating.
  976. ----------------------------------------------------------------------*/
  977. BOOL AddChildren (HWND hwnd, PPERSONRECORD pParentRec)
  978. {
  979.   PSAMPLEINFO    pSampleInfo;
  980.   PPERSONRECORD  pChildRec;
  981.   PPERSONRECORD  pChildRecFirst;
  982.   RECORDINSERT   RecordInsert;
  983.   BOOL           rc = TRUE;
  984.   ULONG          ulNumChildren = 3;
  985.  
  986.   /* Get the pointer to our control block. */
  987.   pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  988.  
  989.   /* Allocate the 3 records which will be used as children. */
  990.   pChildRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
  991.                                        CM_ALLOCRECORD,
  992.                                        MPFROMLONG(sizeof(PERSONRECORD) -
  993.                                        sizeof(MINIRECORDCORE)),
  994.                                        MPFROMLONG(ulNumChildren));
  995.  
  996.   pChildRecFirst = pChildRec;
  997.  
  998.   /* Go through each record and assign the data as necessary. */
  999.   pChildRec->MiniRec.hptrIcon = pSampleInfo->hptrJobIcon;
  1000.   pChildRec->MiniRec.pszIcon = malloc(TEXT_SIZE);
  1001.   if (pChildRec->MiniRec.pszIcon)
  1002.   {
  1003.     if (pParentRec->usJob == JR_DEVELOPMENT)
  1004.     {
  1005.       strcpy (pChildRec->MiniRec.pszIcon, "Design");
  1006.     }
  1007.     else
  1008.     {
  1009.       strcpy (pChildRec->MiniRec.pszIcon, "Planning");
  1010.     }
  1011.   }
  1012.   else
  1013.   {
  1014.     rc = FALSE;
  1015.   }
  1016.  
  1017.   if (rc)
  1018.   {
  1019.     pChildRec = (PPERSONRECORD)pChildRec->MiniRec.preccNextRecord;
  1020.     pChildRec->MiniRec.hptrIcon = pSampleInfo->hptrJobIcon;
  1021.     pChildRec->MiniRec.pszIcon = malloc(TEXT_SIZE);
  1022.     if (pChildRec->MiniRec.pszIcon)
  1023.     {
  1024.       if (pParentRec->usJob == JR_DEVELOPMENT)
  1025.       {
  1026.         strcpy (pChildRec->MiniRec.pszIcon, "Coding");
  1027.       }
  1028.       else
  1029.       {
  1030.         strcpy (pChildRec->MiniRec.pszIcon, "Function\r\nTest");
  1031.       }
  1032.     }
  1033.     else
  1034.     {
  1035.       rc = FALSE;
  1036.     }
  1037.   }
  1038.  
  1039.   if (rc)
  1040.   {
  1041.     pChildRec = (PPERSONRECORD)pChildRec->MiniRec.preccNextRecord;
  1042.     pChildRec->MiniRec.hptrIcon = pSampleInfo->hptrJobIcon;
  1043.     pChildRec->MiniRec.pszIcon = malloc(TEXT_SIZE);
  1044.     if (pChildRec->MiniRec.pszIcon)
  1045.     {
  1046.       /* \r\n or \n will work to create a multi line entry. */
  1047.       if (pParentRec->usJob == JR_DEVELOPMENT)
  1048.       {
  1049.         strcpy (pChildRec->MiniRec.pszIcon, "Unit\nTest");
  1050.       }
  1051.       else
  1052.       {
  1053.         strcpy (pChildRec->MiniRec.pszIcon, "Documentation");
  1054.       }
  1055.     }
  1056.     else
  1057.     {
  1058.       rc = FALSE;
  1059.     }
  1060.   }
  1061.  
  1062.   /* If no errors, set up the RECORDINSERT structure and insert the
  1063.    * child records.  Notice that we set the pRecordParent field of
  1064.    * the RECORDINSERT structure to the parent record passed in.
  1065.    */
  1066.   if (rc)
  1067.   {
  1068.      RecordInsert.cb = sizeof(RECORDINSERT);
  1069.      RecordInsert.pRecordOrder = (PRECORDCORE)CMA_FIRST;
  1070.      RecordInsert.pRecordParent = (PRECORDCORE)pParentRec;
  1071.      RecordInsert.zOrder = CMA_TOP;
  1072.      RecordInsert.cRecordsInsert = ulNumChildren;
  1073.      RecordInsert.fInvalidateRecord = TRUE;
  1074.  
  1075.      WinSendMsg (pSampleInfo->hwndCnr,
  1076.                  CM_INSERTRECORD,
  1077.                  MPFROMP(pChildRecFirst),
  1078.                  MPFROMP(&RecordInsert));
  1079.   }
  1080.   return (rc);
  1081. }
  1082.  
  1083. /*----------------------------------------------------------------------
  1084.  Function Name: CleanupCnr
  1085.  
  1086.  Description:
  1087.    This function is called when this application is closed and will
  1088.    free all the memory and resources used by this application.
  1089.  
  1090.  Parameters:
  1091.    (HWND) hwnd - The handle of the client window.
  1092.  
  1093.  Notes:
  1094.    It is not necessary to remove and free all of the container records
  1095.    when the application is closed.  The container, as part of its
  1096.    WM_DESTROY processing, will do this for you.
  1097. ----------------------------------------------------------------------*/
  1098. VOID CleanupCnr (HWND hwnd)
  1099. {
  1100.   PSAMPLEINFO    pSampleInfo;
  1101.   PPERSONRECORD  pPersonRec;
  1102.   PFIELDINFO     pFieldInfo;
  1103.  
  1104.   /* Get the pointer to our control block. */
  1105.   pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  1106.  
  1107.   /* Make sure it is still valid */
  1108.   if (pSampleInfo)
  1109.   {
  1110.     /* Zip through all the records and free the memory we previously
  1111.      * allocated for the text string.
  1112.      */
  1113.     pPersonRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
  1114.                                             CM_QUERYRECORD,
  1115.                                             NULL,
  1116.                                MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
  1117.     while (pPersonRec)
  1118.     {
  1119.       if (pPersonRec->MiniRec.pszIcon)
  1120.       {
  1121.         free (pPersonRec->MiniRec.pszIcon);
  1122.       }
  1123.  
  1124.       /* For each record, we need to also clean up the memory
  1125.        * used for its child records.
  1126.        */
  1127.       CleanupChildren (hwnd, pPersonRec);
  1128.  
  1129.       /* Get the next record and continue. */
  1130.       pPersonRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
  1131.                                               CM_QUERYRECORD,
  1132.                                               MPFROMP(pPersonRec),
  1133.                                  MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
  1134.     }
  1135.  
  1136.     /* Zip through all the fieldinfo's and free the memory we previously
  1137.      * allocated for the title text strings.
  1138.      */
  1139.     pFieldInfo = WinSendMsg (pSampleInfo->hwndCnr,
  1140.                              CM_QUERYDETAILFIELDINFO,
  1141.                              NULL,
  1142.                              MPFROMSHORT(CMA_FIRST));
  1143.     while (pFieldInfo)
  1144.     {
  1145.       /* If the title data for this column is a string, free the
  1146.        * string memory.
  1147.        */
  1148.       if (!(pFieldInfo->flTitle & CFA_BITMAPORICON))
  1149.       {
  1150.         if (pFieldInfo->pTitleData)
  1151.         {
  1152.           free (pFieldInfo->pTitleData);
  1153.         }
  1154.       }
  1155.  
  1156.       /* Go to the next fieldinfo in the container. */
  1157.       pFieldInfo = WinSendMsg (pSampleInfo->hwndCnr,
  1158.                                CM_QUERYDETAILFIELDINFO,
  1159.                                MPFROMP(pFieldInfo),
  1160.                                MPFROMSHORT(CMA_NEXT));
  1161.     }
  1162.  
  1163.     /* Free the icons we used and menus we used. */
  1164.     WinDestroyPointer (pSampleInfo->hptrPersonIcon);
  1165.     WinDestroyPointer (pSampleInfo->hptrJobIcon);
  1166.     WinDestroyWindow (pSampleInfo->hwndWindowMenu);
  1167.     WinDestroyWindow (pSampleInfo->hwndRecordMenu);
  1168.  
  1169.     /* Free the container title text string memory we stored in our
  1170.      * control block.
  1171.      */
  1172.     if (pSampleInfo->pszCnrTitle)
  1173.     {
  1174.       free (pSampleInfo->pszCnrTitle);
  1175.     }
  1176.  
  1177.     /* Finally, free the SAMPLEINFO control block. */
  1178.     free (pSampleInfo);
  1179.   }
  1180.   return;
  1181. }
  1182.  
  1183. /*----------------------------------------------------------------------
  1184.  Function Name: CleanupChildren
  1185.  
  1186.  Description:
  1187.    This function will free the resources of the child records used in
  1188.    the Tree view.
  1189.  
  1190.  Parameters:
  1191.    (HWND) hwnd                - The handle of the client window.
  1192.    (PPERSONRECORD) pParentRec - Pointer to the parent record whose
  1193.                                 child records we will be processing.
  1194. ----------------------------------------------------------------------*/
  1195. VOID CleanupChildren (HWND hwnd, PPERSONRECORD pParentRec)
  1196. {
  1197.   PSAMPLEINFO    pSampleInfo;
  1198.   PPERSONRECORD  pChildRec;
  1199.  
  1200.   /* Get the pointer to our control block. */
  1201.   pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  1202.  
  1203.   /* Get the first child record for the parent record passed in. */
  1204.   pChildRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
  1205.                                          CM_QUERYRECORD,
  1206.                                          MPFROMP(pParentRec),
  1207.                            MPFROM2SHORT(CMA_FIRSTCHILD, CMA_ITEMORDER));
  1208.  
  1209.   /* Go through each child record, freeing the memory for the text
  1210.    * string in each.
  1211.    */
  1212.   while (pChildRec)
  1213.   {
  1214.     if (pChildRec->MiniRec.pszIcon)
  1215.     {
  1216.       free (pChildRec->MiniRec.pszIcon);
  1217.     }
  1218.  
  1219.     pChildRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
  1220.                                            CM_QUERYRECORD,
  1221.                                            MPFROMP(pChildRec),
  1222.                               MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
  1223.   }
  1224.   return;
  1225. }
  1226.  
  1227. /*----------------------------------------------------------------------
  1228.  Function Name: ProcessContextMenu
  1229.  
  1230.  Description:
  1231.    This will do the necessary processing to bring up a context
  1232.    menu in the container window.  If the record clicked on is selected,
  1233.    a list is built of all the currently selected records in the
  1234.    container window.  This is done because when a context menu is
  1235.    requested on a selected record, the action chosen applies to all
  1236.    selected records.
  1237.  
  1238.  Parameters:
  1239.    (HWND) hwnd               - The handle of the client window.
  1240.    (LONG) xPos               - x position of the mouse.
  1241.    (LONG) yPos               - y position of the mouse.
  1242.    (PPERSONRECORD pPersonRec - pointer to the record the user clicked
  1243.                                on.  If they clicked on white space,
  1244.                                this value is NULL.
  1245. ----------------------------------------------------------------------*/
  1246. VOID ProcessContextMenu (HWND hwnd, LONG xPos, LONG yPos,
  1247.                          PPERSONRECORD pPersonRec)
  1248. {
  1249.   PSAMPLEINFO  pSampleInfo;
  1250.   PVOID        pOffset;
  1251.  
  1252.   /* Get the pointer to our control block. */
  1253.   pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  1254.  
  1255.   /* If the user clicked on a record, check to see if it is a
  1256.    * selected record.  If so, call GetSelectedRecords to build
  1257.    * a linked list of all selected records.  We will then be
  1258.    * able to use this list later if and when the user selects
  1259.    * an action on the menu.
  1260.    * If the record is not selected, store it as a list of 1.
  1261.    */
  1262.   if (pPersonRec)
  1263.   {
  1264.     if (pPersonRec->MiniRec.flRecordAttr & CRA_SELECTED)
  1265.     {
  1266.       pSampleInfo->pMenuRecord = GetSelectedRecords (hwnd);
  1267.     }
  1268.     else
  1269.     {
  1270.       pPersonRec->MiniRec.preccNextRecord = NULL;
  1271.       pSampleInfo->pMenuRecord = pPersonRec;
  1272.     }
  1273.   }
  1274.   else
  1275.   {
  1276.     /* This user clicked on white space....set our field to NULL. */
  1277.     pSampleInfo->pMenuRecord = NULL;
  1278.   }
  1279.  
  1280.   /* If the user requested a popup menu whilst over a record, load
  1281.    * the record context menu.  Otherwise, they clicked on white
  1282.    * space so load the window menu.
  1283.    */
  1284.   if (pPersonRec)
  1285.   {
  1286.     if (!pSampleInfo->hwndRecordMenu)
  1287.     {
  1288.       DosGetResource (NULLHANDLE, RT_MENU, RECORD_POPUP_MENU, &pOffset);
  1289.       pSampleInfo->hwndRecordMenu = WinCreateMenu (HWND_DESKTOP,
  1290.                                                    pOffset);
  1291.       DosFreeResource (pOffset);
  1292.     }
  1293.   }
  1294.   else if (!pSampleInfo->hwndWindowMenu)
  1295.   {
  1296.     DosGetResource (NULLHANDLE, RT_MENU, WIN_POPUP_MENU, &pOffset);
  1297.     pSampleInfo->hwndWindowMenu = WinCreateMenu (HWND_DESKTOP, pOffset);
  1298.     DosFreeResource (pOffset);
  1299.   }
  1300.  
  1301.   WinPopupMenu (HWND_DESKTOP, hwnd,
  1302.                 pPersonRec ? pSampleInfo->hwndRecordMenu :
  1303.                              pSampleInfo->hwndWindowMenu,
  1304.                 xPos, yPos, 0, PU_HCONSTRAIN | PU_VCONSTRAIN |
  1305.                 PU_KEYBOARD | PU_MOUSEBUTTON1 | PU_MOUSEBUTTON2 |
  1306.                 PU_MOUSEBUTTON3);
  1307.   return;
  1308. }
  1309.  
  1310. /*----------------------------------------------------------------------
  1311.  Function Name: ProcessDirectEdit
  1312.  
  1313.  Description:
  1314.    This function handles the direct editing processing resulting
  1315.    from the CN_REALLOCPSZ notification.  This function will also
  1316.    perform data validation if the user is directly editing the
  1317.    Middle Initial column in details view.
  1318.  
  1319.  Parameters:
  1320.    (HWND) hwnd                 - The handle of the client window.
  1321.    (PCNREDITDATA) pCnrEditData - Pointer to the CNREDITDATA structure
  1322.                                  which was passed back from the
  1323.                                  container on the CN_REALLOCPSZ message.
  1324. ----------------------------------------------------------------------*/
  1325. BOOL ProcessDirectEdit (HWND hwnd, PCNREDITDATA pCnrEditData)
  1326. {
  1327.   CHAR  szNewText[2];
  1328.  
  1329.   /* Check to see if the user is directly editing the Middle Initial
  1330.    * column of the details view.  If so, we will need to verify
  1331.    * that the data that is entered is valid.
  1332.    */
  1333.   if (pCnrEditData->pFieldInfo)
  1334.   {
  1335.     if (pCnrEditData->pFieldInfo->offStruct ==
  1336.         FIELDOFFSET (PERSONRECORD, pszMiddleInit))
  1337.     {
  1338.       /* Since we are processing the middle initial field, first
  1339.        * check to be sure it is only 2 characters.  The 2
  1340.        * characters represent the letter and the NULL terminator.
  1341.        * NOTE: This 2 character business will not cut it with
  1342.        * double-byte languages.
  1343.        */
  1344.       if (pCnrEditData->cbText == 2)
  1345.       {
  1346.         /* Get the new text the user entered out of the MLE. */
  1347.         WinQueryWindowText (WinWindowFromID (pCnrEditData->hwndCnr,
  1348.                                              CID_MLE),
  1349.                             pCnrEditData->cbText, (PSZ)szNewText);
  1350.  
  1351.         /* Since we now have the new text, verify that it is valid.
  1352.          * We will only accept a string that is 2 characters long
  1353.          * and contains alphabetic characters.  If it is ok, return
  1354.          * TRUE to inform the container to accept the direct edit
  1355.          * operation and display the new data the user entered.
  1356.          */
  1357.         if (isalpha(szNewText[0]))
  1358.         {
  1359.           return (TRUE);
  1360.         }
  1361.         else
  1362.         {
  1363.           /* Data entered was not alphabetic.  Return FALSE to inform
  1364.            * the container to cancel the direct edit operation.
  1365.            */
  1366.           return (FALSE);
  1367.         }
  1368.       }
  1369.       else
  1370.       {
  1371.         /* Data is not 2 characters in length.  Return FALSE to
  1372.          * inform the container to cancel the direct edit operation.
  1373.          */
  1374.         return (FALSE);
  1375.       }
  1376.     }
  1377.   }
  1378.  
  1379.   /* If we get to here, the data being edited was not from a details
  1380.    * view column, or it was not the Middle initial column.  Process
  1381.    * the new text string by freeing memory occupying the old one and
  1382.    * allocating a new memory block for the new text string.  If we
  1383.    * successfully allocate the memory, return TRUE to inform the
  1384.    * container to update itself to display the new text string.
  1385.    */
  1386.   free (*(pCnrEditData->ppszText));
  1387.   *(pCnrEditData->ppszText) = malloc(pCnrEditData->cbText);
  1388.   if (*(pCnrEditData->ppszText))
  1389.   {
  1390.     return (TRUE);
  1391.   }
  1392.   else
  1393.   {
  1394.     return (FALSE);
  1395.   }
  1396. }
  1397.  
  1398. /*----------------------------------------------------------------------
  1399.  Function Name: ProcessEnter
  1400.  
  1401.  Description:
  1402.    This function handles the case when a record is double-clicked
  1403.    on with the mouse, or if the enter key is pressed when a record
  1404.    is selected.  If this happens when the container is in Tree View,
  1405.    and the record selected is a parent record, we will open an
  1406.    additional container view and insert the child records of the
  1407.    record clicked on.  This will demonstrate the record sharing
  1408.    feature of the container.
  1409.  
  1410.  Parameters:
  1411.    (HWND) hwnd                       - The handle of the client window.
  1412.    (PNOTIFYRECORDENTER) pRecordEnter - Structure passed back from the
  1413.                                        container.
  1414. ----------------------------------------------------------------------*/
  1415. VOID ProcessEnter (HWND hwnd, PNOTIFYRECORDENTER pRecordEnter)
  1416. {
  1417.   PSAMPLEINFO  pSampleInfo;
  1418.   CNRINFO      CnrInfo;
  1419.  
  1420.   /* Get the pointer to our control block. */
  1421.   pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  1422.  
  1423.   /* Query the CNRINFO structure used by the container to check to
  1424.    * see what view the container is currently in.  We will only
  1425.    * process this if we are in the TREE-ICON view.
  1426.    */
  1427.   if (WinSendMsg (pRecordEnter->hwndCnr, CM_QUERYCNRINFO,
  1428.                   MPFROMP(&CnrInfo), MPFROMSHORT(sizeof(CNRINFO))))
  1429.   {
  1430.     if ((CnrInfo.flWindowAttr & (CV_TREE)) &&
  1431.         (CnrInfo.flWindowAttr & (CV_ICON)))
  1432.     {
  1433.       /* Check to see if the record that was clicked on is a root
  1434.        * level record.
  1435.        */
  1436.       if (!WinSendMsg (pRecordEnter->hwndCnr, CM_QUERYRECORD,
  1437.                        MPFROMP(pRecordEnter->pRecord),
  1438.                        MPFROM2SHORT(CMA_PARENT, CMA_ITEMORDER)))
  1439.       {
  1440.         /* Save the record that was clicked on in our control block. */
  1441.         pSampleInfo->pRecordOpen = (PPERSONRECORD)pRecordEnter->pRecord;
  1442.  
  1443.         /* Increment the use count for this record. */
  1444.         pSampleInfo->pRecordOpen->usUseCount++;
  1445.  
  1446.         /* Give the record that was clicked on IN-USE emphasis. */
  1447.         WinSendMsg (pRecordEnter->hwndCnr, CM_SETRECORDEMPHASIS,
  1448.                     MPFROMP(pRecordEnter->pRecord),
  1449.                     MPFROM2SHORT(TRUE, CRA_INUSE));
  1450.  
  1451.         if (!CreateAdditionalView (hwnd))
  1452.         {
  1453.           ; /* report error */
  1454.         }
  1455.       }
  1456.     }
  1457.   }
  1458.   return;
  1459. }
  1460.  
  1461. /*----------------------------------------------------------------------
  1462.  Function Name: CreateAdditionalView
  1463.  
  1464.  Description:
  1465.    This function creates an additional frame and client window that
  1466.    will be used to hold the records that are being shared from the
  1467.    main container window.
  1468.  
  1469.  Parameters:
  1470.    (HWND) hwnd - The handle of the client window.
  1471. ----------------------------------------------------------------------*/
  1472. BOOL CreateAdditionalView (HWND hwnd)
  1473. {
  1474.   HWND  hwndFrame;
  1475.   HWND  hwndClient;
  1476.   BOOL  rc = TRUE;
  1477.   SWP   swp;
  1478.   ULONG fcf = FCF_TITLEBAR | FCF_SIZEBORDER | FCF_SYSMENU |
  1479.               FCF_ICON | FCF_MINMAX;
  1480.  
  1481.   /* Register the Additional Container window class. */
  1482.   WinRegisterClass (WinQueryAnchorBlock(hwnd), "Additional View",
  1483.                     AdditionalCnrWndProc, 0, 0);
  1484.  
  1485.   hwndFrame = WinCreateStdWindow (HWND_DESKTOP, /* Handle of desktop  */
  1486.                            WS_VISIBLE,          /* Window style       */
  1487.                            &fcf,                /* Creation flags     */
  1488.                            "Additional View",   /* Client Class name  */
  1489.                            "Additional View",   /* Title Bar text     */
  1490.                            0,                   /* client wnd style   */
  1491.                            NULLHANDLE,          /* hwnd of resources  */
  1492.                            ICON_RES_ID,         /* ID of Resources    */
  1493.                            &hwndClient);        /* handle of Client   */
  1494.   if (hwndFrame)
  1495.   {
  1496.     swp.fl = SWP_MOVE | SWP_SIZE;
  1497.     swp.x = (WinQuerySysValue (HWND_DESKTOP, SV_CXSCREEN) / 2);
  1498.     swp.y = (WinQuerySysValue (HWND_DESKTOP, SV_CYSCREEN) / 2);
  1499.     swp.cx = (swp.x / 2);
  1500.     swp.cy = (swp.y / 2);
  1501.     swp.hwndInsertBehind = NULLHANDLE;
  1502.     swp.hwnd = hwndFrame;
  1503.     WinSetMultWindowPos (WinQueryAnchorBlock(hwnd), &swp, 1);
  1504.   }
  1505.   else
  1506.   {
  1507.     rc = FALSE;
  1508.   }
  1509.   return (rc);
  1510. }
  1511.  
  1512. /*----------------------------------------------------------------------
  1513.  Function Name: AdditionalCnrWndProc
  1514.  
  1515.  Description:
  1516.    This is the window procedure for the additional container window
  1517.    that is used to hold the records that are shared from the
  1518.    main container window.
  1519.  
  1520.  Parameters:
  1521.    (HWND)    hwnd - The handle of the additional client window.
  1522.    (ULONG)   msg  - The message to be processed.
  1523.    (MRESULT) mp1  - The first message parameter for the message.
  1524.    (MRESULT) mp2  - The second message parameter for the message.
  1525. ----------------------------------------------------------------------*/
  1526. MRESULT EXPENTRY AdditionalCnrWndProc (HWND hwnd, ULONG msg,
  1527.                                        MRESULT mp1, MRESULT mp2)
  1528. {
  1529.   RECTL          rect;
  1530.   HPS            hps;
  1531.   SWP            swp;
  1532.   HWND           hwndMainCnr;
  1533.   HWND           hwndAdditionalCnr;
  1534.   PPERSONRECORD  pInUseRec;
  1535.  
  1536.   switch (msg)
  1537.   {
  1538.     case WM_CREATE:
  1539.       if (SetupAdditionalCnr (hwnd))
  1540.       {
  1541.         return ((MRESULT)FALSE);
  1542.       }
  1543.       else
  1544.       {
  1545.         return ((MRESULT)TRUE);
  1546.       }
  1547.  
  1548.     case WM_PAINT:
  1549.       hps = WinBeginPaint (hwnd, NULLHANDLE, &rect);
  1550.       WinFillRect (hps, &rect, SYSCLR_BACKGROUND);
  1551.       WinEndPaint (hps);
  1552.     break;
  1553.  
  1554.     case WM_SIZE:
  1555.       swp.fl = SWP_MOVE | SWP_SIZE;
  1556.       swp.x = swp.y = 0;
  1557.       swp.cx = SHORT1FROMMP(mp2);
  1558.       swp.cy = SHORT2FROMMP(mp2);
  1559.       swp.hwndInsertBehind = NULLHANDLE;
  1560.       swp.hwnd = WinWindowFromID (hwnd, CNR_ADDITIONAL_ID);
  1561.       WinSetMultWindowPos (WinQueryAnchorBlock (hwnd), &swp, 1);
  1562.     break;
  1563.  
  1564.     case WM_CLOSE:
  1565.       /* Remove IN_USE emphasis from the source record in the main
  1566.        * container window.
  1567.        */
  1568.       hwndMainCnr = WinWindowFromID (vhwndMainClient, CNR_SAMPLE_ID);
  1569.       hwndAdditionalCnr = WinWindowFromID (hwnd, CNR_ADDITIONAL_ID);
  1570.       pInUseRec = FindInUseRecord (hwndAdditionalCnr, hwndMainCnr);
  1571.  
  1572.       /* Decrement the use count of this record since we have closed
  1573.        * one of its views.  If the use count is 0, meaning all of
  1574.        * its views are closed, remove IN_USE emphasis.
  1575.        */
  1576.       pInUseRec->usUseCount--;
  1577.       if (!pInUseRec->usUseCount)
  1578.       {
  1579.         WinSendMsg (hwndMainCnr, CM_SETRECORDEMPHASIS,
  1580.                     MPFROMP(pInUseRec), MPFROM2SHORT(FALSE, CRA_INUSE));
  1581.       }
  1582.  
  1583.       /* Destroy this additional frame window without taking down
  1584.        * our entire application.
  1585.        */
  1586.       WinDestroyWindow (WinQueryWindow (hwnd, QW_PARENT));
  1587.       return ((MRESULT)FALSE);
  1588.  
  1589.     default:
  1590.       return (WinDefWindowProc (hwnd, msg, mp1, mp2));
  1591.   }
  1592.   return (0);
  1593. }
  1594.  
  1595. /*----------------------------------------------------------------------
  1596.  Function Name: SetupAdditionalCnr
  1597.  
  1598.  Description:
  1599.    This function is called as a result of receiving a WM_CREATE
  1600.    message to our additional client window.  This function will
  1601.    create a second container window and share the child records
  1602.    of the record that was selected in our main container window.
  1603.  
  1604.  Parameters:
  1605.    (HWND) hwnd - The handle of the additional client window.
  1606. ----------------------------------------------------------------------*/
  1607. BOOL SetupAdditionalCnr (HWND hwnd)
  1608. {
  1609.   PSAMPLEINFO    pSampleInfo;
  1610.   PPERSONRECORD  pChildRec;
  1611.   RECORDINSERT   RecordInsert;
  1612.   HWND           hwndAdditionalCnr;
  1613.   CNRINFO        CnrInfo;
  1614.   BOOL           rc = TRUE;
  1615.   PPERSONRECORD  pChildRecFirst = NULL;
  1616.   PPERSONRECORD  pChildRecCur = NULL;
  1617.   ULONG          ulNumRecords = 0;
  1618.  
  1619.   hwndAdditionalCnr = WinCreateWindow (hwnd,          /* Parent       */
  1620.                          WC_CONTAINER,                /* Class        */
  1621.                          NULL,                        /* Window text  */
  1622.                          WS_VISIBLE | CCS_EXTENDSEL | /* Window style */
  1623.                          CCS_MINIRECORDCORE | CCS_READONLY,
  1624.                          0,0,                         /* x,y          */
  1625.                          0,0,                         /* cx, cy       */
  1626.                          hwnd,                        /* Owner        */
  1627.                          HWND_TOP,                    /* placement    */
  1628.                          CNR_ADDITIONAL_ID,           /* Window ID    */
  1629.                          NULL,                        /* Control data */
  1630.                          NULL);                       /* presparams   */
  1631.  
  1632.   if (hwndAdditionalCnr)
  1633.   {
  1634.     /* Get a pointer to the main client window's control block where
  1635.      * the pointer to its container window is stored.
  1636.      */
  1637.     pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (vhwndMainClient,
  1638.                                                   QWL_USER);
  1639.  
  1640.     /* Set up the container title for the additional view.  We will
  1641.      * use the icon name.
  1642.      */
  1643.     CnrInfo.pszCnrTitle = pSampleInfo->pRecordOpen->MiniRec.pszIcon;
  1644.     CnrInfo.flWindowAttr = CV_ICON | CA_CONTAINERTITLE |
  1645.                            CA_TITLESEPARATOR | CA_TITLELEFT;
  1646.  
  1647.     WinSendMsg (hwndAdditionalCnr, CM_SETCNRINFO,
  1648.                 MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR |
  1649.                                               CMA_CNRTITLE));
  1650.  
  1651.     /* Query the first child from the main container window.  Then
  1652.      * loop through and build a linked list of all the children for
  1653.      * this specific parent record.
  1654.      */
  1655.     pChildRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
  1656.                                        CM_QUERYRECORD,
  1657.                                        MPFROMP(pSampleInfo->pRecordOpen),
  1658.                              MPFROM2SHORT(CMA_FIRSTCHILD, CMA_ITEMORDER));
  1659.  
  1660.     while (pChildRec)
  1661.     {
  1662.       ulNumRecords++;
  1663.       if (pChildRecFirst)
  1664.       {
  1665.         pChildRecCur->MiniRec.preccNextRecord=(PMINIRECORDCORE)pChildRec;
  1666.       }
  1667.       else
  1668.       {
  1669.         pChildRecFirst = pChildRec;
  1670.       }
  1671.       pChildRecCur = pChildRec;
  1672.       pChildRecCur->MiniRec.preccNextRecord = NULL;
  1673.  
  1674.       pChildRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
  1675.                                              CM_QUERYRECORD,
  1676.                                              MPFROMP(pChildRec),
  1677.                                    MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
  1678.     }
  1679.  
  1680.     /* Now insert the linked list we just built into the secondary
  1681.      * container window we just created.  Notice we are not reallocating
  1682.      * the record memory, but reusing the existing records in another
  1683.      * container window...ie record sharing.
  1684.      */
  1685.     RecordInsert.cb = sizeof(RECORDINSERT);
  1686.     RecordInsert.pRecordOrder = (PRECORDCORE)CMA_FIRST;
  1687.     RecordInsert.pRecordParent = NULL;
  1688.     RecordInsert.zOrder = CMA_TOP;
  1689.     RecordInsert.cRecordsInsert = ulNumRecords;
  1690.     RecordInsert.fInvalidateRecord = TRUE;
  1691.  
  1692.     if (WinSendMsg (hwndAdditionalCnr, CM_INSERTRECORD,
  1693.                     MPFROMP(pChildRecFirst), MPFROMP(&RecordInsert)))
  1694.     {
  1695.       WinSendMsg (hwndAdditionalCnr, CM_ARRANGE, NULL, NULL);
  1696.     }
  1697.     else
  1698.     {
  1699.       rc = FALSE;
  1700.       WinDestroyWindow (hwndAdditionalCnr);
  1701.     }
  1702.   }
  1703.   else
  1704.   {
  1705.     rc = FALSE;
  1706.   }
  1707.  
  1708.   /* Give the new container the focus. */
  1709.   WinSetFocus (HWND_DESKTOP, hwndAdditionalCnr);
  1710.   return (rc);
  1711. }
  1712.  
  1713. /*----------------------------------------------------------------------
  1714.  Function Name: pfnCompareName
  1715.  
  1716.  Description:
  1717.    This is the comparison function called by the container when a sort
  1718.    by name is requested.  This function simply compares the text
  1719.    strings of the two records given.
  1720.  
  1721.  Parameters:
  1722.    (PRECORDCORE)preccFirst    - The first of two records to compare.
  1723.    (PRECORDCORE)preccSecond   - The second of two records to compare.
  1724.    (PVOID)pStorage            - Application defined pointer(NULL).
  1725. ----------------------------------------------------------------------*/
  1726. SHORT EXPENTRY pfnCompareName (PRECORDCORE preccFirst,
  1727.                                PRECORDCORE preccSecond, PVOID pStorage )
  1728. {
  1729.   return (strcmp(preccFirst->pszIcon, preccSecond->pszIcon));
  1730. }
  1731.  
  1732. /*----------------------------------------------------------------------
  1733.  Function Name: pfnCompareBDay
  1734.  
  1735.  Description:
  1736.    This is the comparison function called by the container when a sort
  1737.    by birthday is requested.  This function simply compares the
  1738.    DateOfBirth fields of each PERSONRECORD to determine which is older.
  1739.    It first looks at the year of birth, then the month and day if
  1740.    necessary.
  1741.  
  1742.  Parameters:
  1743.    (PRECORDCORE)preccFirst    - The first of two records to compare.
  1744.    (PRECORDCORE)preccSecond   - The second of two records to compare.
  1745.    (PVOID)pStorage            - Application defined pointer(NULL).
  1746. ----------------------------------------------------------------------*/
  1747. SHORT EXPENTRY pfnCompareBDay (PRECORDCORE preccFirst,
  1748.                                PRECORDCORE preccSecond, PVOID pStorage)
  1749. {
  1750.   if ( ((PPERSONRECORD)preccFirst)->DateOfBirth.year !=
  1751.        ((PPERSONRECORD)preccSecond)->DateOfBirth.year )
  1752.     return( ((PPERSONRECORD)preccFirst)->DateOfBirth.year -
  1753.             ((PPERSONRECORD)preccSecond)->DateOfBirth.year );
  1754.  
  1755.   else if  ( ((PPERSONRECORD)preccFirst)->DateOfBirth.month !=
  1756.              ((PPERSONRECORD)preccSecond)->DateOfBirth.month )
  1757.     return( ((PPERSONRECORD)preccFirst)->DateOfBirth.month -
  1758.             ((PPERSONRECORD)preccSecond)->DateOfBirth.month );
  1759.  
  1760.   else if  ( ((PPERSONRECORD)preccFirst)->DateOfBirth.day !=
  1761.              ((PPERSONRECORD)preccSecond)->DateOfBirth.day )
  1762.     return( ((PPERSONRECORD)preccFirst)->DateOfBirth.day -
  1763.             ((PPERSONRECORD)preccSecond)->DateOfBirth.day );
  1764.   else
  1765.     return ( 0 );
  1766. }
  1767.  
  1768. /*----------------------------------------------------------------------
  1769.  Function Name: ScrollToRecord
  1770.  
  1771.  Description:
  1772.    This function is used to bring a particular record in the container
  1773.    to the top-left corner of the current viewport.  It will always
  1774.    scroll the record into view, however it may be impossible to scroll
  1775.    it to the top-left corner.  This may happen when a record is
  1776.    positioned at the extreme right edge of the container's workspace.
  1777.  
  1778.  Parameters:
  1779.    (HWND)hwndCnr              - The container window handle.
  1780.    (PRECORDCORE)pRecord       - The record to be scrolled to.
  1781. ----------------------------------------------------------------------*/
  1782. VOID ScrollToRecord(HWND hwndCnr, PRECORDCORE pRecord)
  1783. {
  1784.   RECTL             rclViewport;
  1785.   RECTL             rclItem;
  1786.   QUERYRECORDRECT   QueryRecordRect;
  1787.   LONG              lMargin = 4L;
  1788.   CNRINFO           CnrInfo;
  1789.  
  1790.   WinSendMsg (hwndCnr,
  1791.               CM_QUERYCNRINFO,
  1792.               MPFROMP(&CnrInfo),
  1793.               MPFROMSHORT(sizeof(CNRINFO)) );
  1794.  
  1795.   /* Query the container for the position of the target record relative
  1796.    * to the viewport.  We want the rectangle containing both the icon
  1797.    * and the text of the record.
  1798.    */
  1799.   QueryRecordRect.cb       = sizeof(QUERYRECORDRECT);
  1800.   QueryRecordRect.pRecord  = pRecord;
  1801.   QueryRecordRect.fRightSplitWindow = FALSE;
  1802.   if (CnrInfo.flWindowAttr & CV_TEXT)
  1803.     QueryRecordRect.fsExtent = CMA_TEXT;
  1804.   else
  1805.     QueryRecordRect.fsExtent = CMA_ICON | CMA_TEXT;
  1806.  
  1807.   WinSendMsg (hwndCnr,
  1808.               CM_QUERYRECORDRECT,
  1809.               MPFROMP(&rclItem),
  1810.               MPFROMP(&QueryRecordRect) );
  1811.  
  1812.   /* Query the container for the size of the current viewport.
  1813.    * This is necessary because the position of the record
  1814.    * is relative to the bottom left corner of the viewport.
  1815.    */
  1816.   WinSendMsg (hwndCnr,
  1817.               CM_QUERYVIEWPORTRECT,
  1818.               MPFROMP(&rclViewport),
  1819.               MPFROM2SHORT(CMA_WINDOW, FALSE) );
  1820.  
  1821.   /* If in details view, the viewport must be adjusted for the
  1822.    * horizontal scroll bar.
  1823.    * NOTE: This if statement is only needed when running OS/2 2.0
  1824.    *       GA or the Service Pack.  This if statement should be
  1825.    *       removed for future versions of OS/2.
  1826.    */
  1827.   if (CnrInfo.flWindowAttr & CV_DETAIL)
  1828.   {
  1829.     LONG   lcyScrollBar;
  1830.  
  1831.     lcyScrollBar = WinQuerySysValue(HWND_DESKTOP, SV_CYHSCROLL);
  1832.     rclViewport.yBottom += lcyScrollBar;
  1833.     rclViewport.yTop    += lcyScrollBar;
  1834.   }
  1835.  
  1836.   /*  Scroll the container vertically to bring the record to a position
  1837.    *  just below the top of the viewport.
  1838.    */
  1839.   WinSendMsg (hwndCnr,
  1840.               CM_SCROLLWINDOW,
  1841.               MPFROMSHORT(CMA_VERTICAL),
  1842.               MPFROMLONG(rclViewport.yTop - rclItem.yTop - lMargin) );
  1843.  
  1844.   /*  Scroll the container horizontally to bring the record to a
  1845.    *  position just to the right of the left edge of the viewport.
  1846.    */
  1847.   WinSendMsg (hwndCnr,
  1848.               CM_SCROLLWINDOW,
  1849.               MPFROMSHORT(CMA_HORIZONTAL),
  1850.               MPFROMLONG(rclItem.xLeft - lMargin) );
  1851.   return;
  1852. }
  1853.  
  1854. /*----------------------------------------------------------------------
  1855.  Function Name: ScrollToColumn
  1856.  
  1857.  Description:
  1858.    This function is used to bring a particular column in the container
  1859.    details view to the left edge of the current viewport.  It will
  1860.    always scroll the column into view, however it may be impossible
  1861.    to scroll it all the way to the left edge.
  1862.    NOTE: This routine requires the Service Pack version.
  1863.          There are workarounds in this function for the Service
  1864.          Pack version of OS/2.  For future releases the suggested
  1865.          alternative should be used.
  1866.  
  1867.  Parameters:
  1868.    (HWND)hwndCnr              - The container window handle.
  1869.    (PFIELDINFO)pColumn        - The column to be scrolled to.
  1870. ----------------------------------------------------------------------*/
  1871. VOID ScrollToColumn(HWND hwndCnr, PFIELDINFO pColumn)
  1872. {
  1873.   POINTL      ColPos;
  1874.   RECTL       rclViewport;
  1875.   BOOL        LeftDVWnd;
  1876.   BOOL        SplitWnd;
  1877.   LONG        WindowOffset;
  1878.  
  1879.   /* Query the Column position in workspace coordinates and also get
  1880.    * information like if the window is split and which split window
  1881.    * the column is in.
  1882.    */
  1883.   QueryColumnPos(hwndCnr, pColumn, &ColPos, &LeftDVWnd, &SplitWnd);
  1884.  
  1885.   /*  Query the container for the viewports current workspace position
  1886.    */
  1887.   if (LeftDVWnd)
  1888.     WinSendMsg (hwndCnr,
  1889.                 CM_QUERYVIEWPORTRECT,
  1890.                 MPFROMP(&rclViewport),
  1891.                 MPFROM2SHORT(CMA_WORKSPACE, FALSE) );
  1892.   else
  1893.   {
  1894.     /* For the service pack, the right window's workspace position must
  1895.      * be adjusted by the offset from the container window origin.
  1896.      * NOTE: For future releases of OS/2, this WindowOffset adjustment
  1897.      *       should be removed.
  1898.      */
  1899.     WinSendMsg (hwndCnr,
  1900.                 CM_QUERYVIEWPORTRECT,
  1901.                 MPFROMP(&rclViewport),
  1902.                 MPFROM2SHORT(CMA_WINDOW, TRUE) );
  1903.     WindowOffset = rclViewport.xLeft;
  1904.     WinSendMsg (hwndCnr,
  1905.                 CM_QUERYVIEWPORTRECT,
  1906.                 MPFROMP(&rclViewport),
  1907.                 MPFROM2SHORT(CMA_WORKSPACE, TRUE) );
  1908.     rclViewport.xLeft -= WindowOffset;
  1909.   }
  1910.  
  1911.   /* If the details view is split, then CM_HORZSCROLLSPLITWINDOW
  1912.    * should be used, otherwise CM_SCROLLWINDOW should be used.
  1913.    * We need to scroll the difference between the columns left
  1914.    * side and the left side of the viewport.
  1915.    * NOTE: For the service pack, use addition not subtraction.
  1916.    *       For future releases of OS/2 the commented lines
  1917.    *       should be used instead of addition.
  1918.    */
  1919.   if (SplitWnd)
  1920.     if (LeftDVWnd)
  1921.       WinSendMsg (hwndCnr,
  1922.                   CM_HORZSCROLLSPLITWINDOW,
  1923.                   MPFROMSHORT(CMA_LEFT),
  1924.                /* MPFROMLONG(ColPos.x - rclViewport.xLeft) ); */
  1925.                   MPFROMLONG(ColPos.x + rclViewport.xLeft) );
  1926.     else
  1927.       WinSendMsg (hwndCnr,
  1928.                   CM_HORZSCROLLSPLITWINDOW,
  1929.                   MPFROMSHORT(CMA_RIGHT),
  1930.                /* MPFROMLONG(ColPos.x - rclViewport.xLeft) ); */
  1931.                   MPFROMLONG(ColPos.x + rclViewport.xLeft) );
  1932.   else
  1933.     WinSendMsg (hwndCnr,
  1934.                 CM_SCROLLWINDOW,
  1935.                 MPFROMSHORT(CMA_HORIZONTAL),
  1936.              /* MPFROMLONG(ColPos.x - rclViewport.xLeft) ); */
  1937.                 MPFROMLONG(ColPos.x + rclViewport.xLeft) );
  1938.   return;
  1939. }
  1940.  
  1941. /*----------------------------------------------------------------------
  1942.  Function Name: QueryColumnPos
  1943.  
  1944.  Description:
  1945.    NOTE: This function utilizes the CMA_DATAWIDTH feature described
  1946.          in the OS/2 service pack readme file.  This function requires
  1947.          the user to be running at least the service pack level of OS/2.
  1948.    This function is used to calculate the left and right position
  1949.    of the given column in workspace coordinates.  Note that the
  1950.    POINTL.x is used for the left side and that POINTL.y is used
  1951.    for the columns right side.
  1952.  
  1953.  Parameters:
  1954.    (HWND)hwndCnr              - The container window handle.
  1955.    (PFIELDINFO)pColumn        - The column to calculate position for.
  1956.  
  1957.  The following parameters describe the return value from this function.
  1958.    (PPOINTL)pPointL           - pPointL.x will be the left side of
  1959.                                           the column in workspace coord.
  1960.                                 pPointL.y will be the right side of
  1961.                                           the column in workspace coord.
  1962.    (PBOOL)pLeftDVW            - TRUE -> the column is in the left window
  1963.                                         or the container is unsplit.
  1964.                                 FALSE -> the column is in the right window
  1965.                                          of a split container.
  1966.    (PBOOL)pbSplit             - TRUE-> the container details view is split
  1967.                                 FALSE-> the container details view is
  1968.                                          not split.
  1969. ----------------------------------------------------------------------*/
  1970. void QueryColumnPos(HWND hwndCnr, PFIELDINFO pColumn,
  1971.                     PPOINTL pPointL, PBOOL pLeftDVW, PBOOL pbSplit)
  1972. {
  1973.   CNRINFO        CnrInfo;
  1974.   FONTMETRICS    FontMetrics;
  1975.   HPS            hps;
  1976.   PFIELDINFO     pFI, pLastVisibleFI;
  1977.   LONG           LeftSide=0, RightSide=0, LeftMargin, RightMargin;
  1978.  
  1979.   WinSendMsg (hwndCnr,
  1980.               CM_QUERYCNRINFO,
  1981.               MPFROMP(&CnrInfo),
  1982.               MPFROMSHORT(sizeof(CNRINFO)) );
  1983.  
  1984.   /* Set default values for the LeftDVW and bSplit booleans and
  1985.    * determine if it is actually different.
  1986.    */
  1987.  
  1988.   *pLeftDVW = TRUE;
  1989.   *pbSplit  = FALSE;
  1990.   if (CnrInfo.pFieldInfoLast)
  1991.   {
  1992.     *pbSplit  = TRUE;
  1993.     pFI = CnrInfo.pFieldInfoLast;
  1994.     while ( pFI )
  1995.     {
  1996.       pFI =  WinSendMsg (hwndCnr,
  1997.                          CM_QUERYDETAILFIELDINFO,
  1998.                          MPFROMP(pFI),
  1999.                          MPFROMSHORT(CMA_NEXT) );
  2000.       if ( pFI == pColumn )
  2001.       {
  2002.         *pLeftDVW = FALSE;
  2003.         pFI = NULL;
  2004.       }
  2005.     }
  2006.   }
  2007.  
  2008.   /* If the column is invisible, stop now.
  2009.    */
  2010.   if (pColumn->flData & CFA_INVISIBLE)
  2011.     return;
  2012.  
  2013.   /* Query the font metrics so we can calculate the margins as
  2014.    * described in the OS/2 service pack readme file.
  2015.    */
  2016.   hps = WinGetPS( hwndCnr );
  2017.   GpiQueryFontMetrics( hps, sizeof(FONTMETRICS), &FontMetrics);
  2018.   WinReleasePS( hps );
  2019.   LeftMargin  = (3 * FontMetrics.lAveCharWidth) / 2;
  2020.   RightMargin = (3 * FontMetrics.lAveCharWidth) - LeftMargin;
  2021.  
  2022.   /* Determine the stopping point for our algorithm below.
  2023.    */
  2024.   if ( *pLeftDVW )
  2025.     pFI = QueryNextVisibleCol( hwndCnr, (PFIELDINFO)CMA_FIRST );
  2026.   else
  2027.     pFI = QueryNextVisibleCol( hwndCnr, CnrInfo.pFieldInfoLast );
  2028.  
  2029.   if ( *pLeftDVW  && CnrInfo.pFieldInfoLast)
  2030.     if (CnrInfo.pFieldInfoLast->flData & CFA_INVISIBLE)
  2031.       pLastVisibleFI = QueryPrevVisibleCol(hwndCnr,
  2032.                                            CnrInfo.pFieldInfoLast );
  2033.     else
  2034.       pLastVisibleFI = CnrInfo.pFieldInfoLast;
  2035.   else
  2036.     pLastVisibleFI = QueryPrevVisibleCol(hwndCnr,
  2037.                                          (PFIELDINFO)CMA_LAST );
  2038.  
  2039.   /* Calculate the first column.
  2040.    */
  2041.   RightSide = ( (LONG)(WinSendMsg (hwndCnr,
  2042.                                    CM_QUERYDETAILFIELDINFO,
  2043.                                    MPFROMP(pFI),
  2044.                                    MPFROMSHORT(CMA_DATAWIDTH) )) );
  2045.   RightSide += FontMetrics.lAveCharWidth + RightMargin;
  2046.  
  2047.   /* Continue looping and calculating each column's position until
  2048.    * we reach the desired column.
  2049.    */
  2050.   while ( pFI  && (pFI != pColumn) )
  2051.   {
  2052.     USHORT  Width;
  2053.  
  2054.     LeftSide = RightSide;
  2055.     pFI = QueryNextVisibleCol( hwndCnr, pFI );
  2056.  
  2057.     Width = (LONG)(WinSendMsg (hwndCnr,
  2058.                                CM_QUERYDETAILFIELDINFO,
  2059.                                MPFROMP(pFI),
  2060.                                MPFROMSHORT(CMA_DATAWIDTH) ));
  2061.  
  2062.     RightSide += Width;
  2063.     RightSide += RightMargin + LeftMargin;
  2064.   }
  2065.  
  2066.   /* If the column is the last visible column in its viewport then
  2067.    * the right margin must be adjusted accordingly.
  2068.    */
  2069.   if ( pFI == pLastVisibleFI )
  2070.     RightSide += FontMetrics.lAveCharWidth - RightMargin;
  2071.  
  2072.   /* Return to sender.
  2073.    */
  2074.   pPointL->x = LeftSide;
  2075.   pPointL->y = RightSide;
  2076.   return;
  2077. }
  2078.  
  2079. /*----------------------------------------------------------------------
  2080.  Function Name: QueryNextVisibleCol
  2081.  
  2082.  Description:
  2083.    This function is used to retrieve the next visible column in the
  2084.    container from the given one.  It is possible to have invisible
  2085.    columns in the container so this function skips over these.  It
  2086.    also allows CMA_FIRST to be passed in so that it will return the
  2087.    first visible column in this case.
  2088.  
  2089.  Parameters:
  2090.    (HWND)hwndCnr              - The container window handle.
  2091.    (PFIELDINFO)pColumn        - The column from which to start from.
  2092. ----------------------------------------------------------------------*/
  2093. PFIELDINFO QueryNextVisibleCol( HWND hwndCnr, PFIELDINFO pFI)
  2094. {
  2095.   /* If CMA_FIRST, get the first visible column in the container.
  2096.    */
  2097.   if ( pFI == (PFIELDINFO)CMA_FIRST )
  2098.   {
  2099.     pFI =  WinSendMsg (hwndCnr,
  2100.                        CM_QUERYDETAILFIELDINFO,
  2101.                        NULL,
  2102.                        MPFROMSHORT(CMA_FIRST) );
  2103.     if (!(pFI->flData & CFA_INVISIBLE) || !pFI )
  2104.       return( pFI );
  2105.   }
  2106.  
  2107.   /* Continue querying the next column until a visible column is found
  2108.    * or we reach the end.
  2109.    */
  2110.   pFI =  WinSendMsg (hwndCnr,
  2111.                      CM_QUERYDETAILFIELDINFO,
  2112.                      MPFROMP(pFI),
  2113.                      MPFROMSHORT(CMA_NEXT) );
  2114.   while ( pFI && (pFI->flData & CFA_INVISIBLE) )
  2115.   {
  2116.     pFI =  WinSendMsg (hwndCnr,
  2117.                        CM_QUERYDETAILFIELDINFO,
  2118.                        MPFROMP(pFI),
  2119.                        MPFROMSHORT(CMA_NEXT) );
  2120.   }
  2121.   return( pFI );
  2122. }
  2123.  
  2124. /*----------------------------------------------------------------------
  2125.  Function Name: QueryPrevVisibleCol
  2126.  
  2127.  Description:
  2128.    This function is used to retrieve the previous visible column in the
  2129.    container from the given one.  It is possible to have invisible
  2130.    columns in the container so this function skips over these.  It
  2131.    also allows CMA_LAST to be passed in so that it will return the
  2132.    last visible column in this case.
  2133.  
  2134.  Parameters:
  2135.    (HWND)hwndCnr              - The container window handle.
  2136.    (PFIELDINFO)pColumn        - The column from which to start from.
  2137. ----------------------------------------------------------------------*/
  2138. PFIELDINFO QueryPrevVisibleCol( HWND hwndCnr, PFIELDINFO pFI)
  2139. {
  2140.   /* If CMA_LAST, get the last visible column in the container.
  2141.    */
  2142.   if ( pFI == (PFIELDINFO)CMA_LAST )
  2143.   {
  2144.     pFI =  WinSendMsg (hwndCnr,
  2145.                        CM_QUERYDETAILFIELDINFO,
  2146.                        NULL,
  2147.                        MPFROMSHORT(CMA_LAST) );
  2148.     if (!(pFI->flData & CFA_INVISIBLE) || !pFI )
  2149.       return( pFI );
  2150.   }
  2151.  
  2152.   /* Continue querying the previous column until a visible column is found
  2153.    * or we reach the beginning.
  2154.    */
  2155.   pFI =  WinSendMsg (hwndCnr,
  2156.                      CM_QUERYDETAILFIELDINFO,
  2157.                      MPFROMP(pFI),
  2158.                      MPFROMSHORT(CMA_PREV) );
  2159.   while ( pFI && (pFI->flData & CFA_INVISIBLE) )
  2160.   {
  2161.     pFI =  WinSendMsg (hwndCnr,
  2162.                        CM_QUERYDETAILFIELDINFO,
  2163.                        MPFROMP(pFI),
  2164.                        MPFROMSHORT(CMA_PREV) );
  2165.   }
  2166.   return( pFI );
  2167. }
  2168.  
  2169. /*----------------------------------------------------------------------
  2170.  Function Name: QueryColumnFromRect
  2171.  
  2172.  Description:
  2173.    This function is not called by this application but is provided
  2174.    as additional information.
  2175.  
  2176.    This function is similar to the container's CM_QUERYRECORDFROMRECT.
  2177.    It takes a rectangle in window coordinates and starting from a
  2178.    given column will search for the next column overlapping this
  2179.    rectangle.  Note that this is like CMA_PARTIAL in the
  2180.    CM_QUERYRECORDFROMRECT message.
  2181.  
  2182.    Due to time constraints, this function was only written to handle
  2183.    the CMA_PARTIAL and an unsplit details view.  However, it is
  2184.    a little tedious but not difficult to add the remaining functions.
  2185.    Notice that this function ignores the top and bottom values of the
  2186.    rectangle as only the right and left values are pertinent.
  2187.  
  2188.  Parameters:
  2189.    (HWND)hwndCnr              - The container window handle.
  2190.    (PFIELDINFO)pStartFI       - The column from which to start from.
  2191.    (PRECTL)pRect              - The rectangle used to query with
  2192.                                  (In container window coordinates)
  2193. ----------------------------------------------------------------------*/
  2194. PFIELDINFO QueryColumnFromRect( HWND hwndCnr, PFIELDINFO pStartFI,
  2195.                                 PRECTL pRect )
  2196. {
  2197.   PFIELDINFO  pFI;
  2198.   POINTL      ColPos;
  2199.   BOOL        bleft, bSplit;
  2200.   RECTL       rclViewport;
  2201.   RECTL       SearchRect;
  2202.  
  2203.  
  2204.   /*  Adjust the given rectangle in window coordinates to workspace
  2205.    *  coordinates.
  2206.    */
  2207.   WinSendMsg (hwndCnr,
  2208.               CM_QUERYVIEWPORTRECT,
  2209.               MPFROMP(&rclViewport),
  2210.               MPFROM2SHORT(CMA_WORKSPACE, FALSE) );
  2211.   pRect->xLeft  += rclViewport.xLeft;
  2212.   pRect->xRight += rclViewport.xLeft;
  2213.  
  2214.   pFI = QueryNextVisibleCol( hwndCnr, pStartFI );
  2215.  
  2216.   while (pFI)
  2217.   {
  2218.     /* Query the columns position.
  2219.      */
  2220.     QueryColumnPos( hwndCnr, pFI, &ColPos, &bleft, &bSplit);
  2221.  
  2222.     /* This if statement basically checks to see if there is any
  2223.      * overlap between the column and the given rectangle.  We
  2224.      * are providing the CMA_PARTIAL case only.
  2225.      */
  2226.     if ( ((ColPos.x <= pRect->xLeft) && (pRect->xLeft <= ColPos.y)) ||
  2227.          ((ColPos.x <= pRect->xRight) && (pRect->xRight <= ColPos.y)) ||
  2228.          ((pRect->xLeft <= ColPos.x) && (ColPos.y <= pRect->xRight)) )
  2229.       return( pFI );
  2230.  
  2231.     pFI = QueryNextVisibleCol( hwndCnr, pFI );
  2232.   }
  2233.   return( NULL );
  2234. }
  2235.  
  2236. /*----------------------------------------------------------------------
  2237.  Function Name: ProcessInitDrag
  2238.  
  2239.  Description:
  2240.    This function is called when CN_INITDRAG is received from the
  2241.    container.  It sets up the DRAGINFO, DRAGITEM, and DRAGIMAGE
  2242.    structures and initiates drag by calling DrgDrag.
  2243.    Note: This drag example only provides dragging of one item at a
  2244.          time.  CUA '91 suggests that if a selected item is dragged
  2245.          then all the selected items should be dragged.
  2246.  
  2247.  Parameters:
  2248.    (HWND)hwnd                 - The container's owner window handle.
  2249.    (HWND)hwndCnr              - The container window handle.
  2250.    (PCNRDRAGINT)pCnrDragInit  - The rectangle used to query with
  2251. ----------------------------------------------------------------------*/
  2252. VOID ProcessInitDrag( HWND hwnd, HWND hwndCnr,
  2253.                       PCNRDRAGINIT pCnrDragInit )
  2254. {
  2255.   PDRAGINFO   pDragInfo;
  2256.   DRAGITEM    DragItem;
  2257.   HWND        hwndDest;
  2258.   DRAGIMAGE   DragImage;
  2259.  
  2260.   /* Only start drag if there is a record under pointer. */
  2261.   if (pCnrDragInit->pRecord)
  2262.   {
  2263.     /* Allocate Drag Info structure. Set the drag source window hwnd.
  2264.      * Set the DragItem and the DragImage.
  2265.      */
  2266.     pDragInfo = DrgAllocDraginfo ( 1 );
  2267.     pDragInfo->hwndSource = hwndCnr;
  2268.  
  2269.     SetDragItem( hwndCnr, pDragInfo, pCnrDragInit->pRecord);
  2270.  
  2271.     SetDragImage(hwndCnr, (PDRAGIMAGE) &DragImage,
  2272.                  pCnrDragInit, pCnrDragInit->pRecord);
  2273.  
  2274.     /* Call drag to start dragging. */
  2275.     hwndDest = DrgDrag (hwndCnr,
  2276.                         pDragInfo,
  2277.                         (PDRAGIMAGE) &DragImage,
  2278.                         1,
  2279.                         VK_BUTTON2,
  2280.                         (PVOID) 0x80000000);
  2281.   }
  2282. }
  2283.  
  2284. /*----------------------------------------------------------------------
  2285.  Function Name: SetDragItem
  2286.  
  2287.  Description:
  2288.    This function is called to setup the DRAGITEM structure and then
  2289.    call DrgSetDragitem.
  2290.  
  2291.  Parameters:
  2292.    (HWND)hwndCnr              - The container window handle.
  2293.    (PDRAGINFO)pDragInfo       - The DragInfo structure.
  2294.    (PRECORDCORE)pRecord       - The record being dragged.
  2295. ----------------------------------------------------------------------*/
  2296. VOID SetDragItem(HWND hwndCnr, PDRAGINFO pDragInfo, PRECORDCORE pRecord)
  2297. {
  2298.   DRAGITEM dragItem;
  2299.  
  2300.   /* Setup the DragItem structure. */
  2301.   dragItem.hwndItem = hwndCnr;
  2302.   dragItem.ulItemID = (ULONG) pRecord;
  2303.   dragItem.fsControl      = 0;
  2304.   dragItem.fsSupportedOps = 0;
  2305.  
  2306.   /* Set the rendering mechanism and formats for the item. */
  2307.   dragItem.hstrRMF = DrgAddStrHandle ("<DRM_PRINT, DRF_TEXT>");
  2308.  
  2309.   /* Setup icon string for item being dragged. */
  2310.   dragItem.hstrSourceName = DrgAddStrHandle((PSZ) pRecord->pszIcon);
  2311.  
  2312.   /* Call drag to set the item into the drag info structure. */
  2313.   DrgSetDragitem (pDragInfo, &dragItem, sizeof(DRAGITEM), 0);
  2314. }
  2315.  
  2316.  
  2317. /*----------------------------------------------------------------------
  2318.  Function Name: SetDragImage
  2319.  
  2320.  Description:
  2321.    This function is called to setup the DRAGIMAGE structure.
  2322.  
  2323.  Parameters:
  2324.    (HWND)hwndCnr              - The container window handle.
  2325.    (PDRAGIMAGE)pDragImage     - The DragImage structure.
  2326.    (PCNRDRAGINIT)pCnrDragInit - The container's DragInit structure.
  2327.    (PRECORDCORE)pRecord       - The record being dragged.
  2328. ----------------------------------------------------------------------*/
  2329. VOID SetDragImage(HWND hwndCnr, PDRAGIMAGE pDragImage,
  2330.                   PCNRDRAGINIT pCnrDragInit, PRECORDCORE pRecord)
  2331. {
  2332.   CNRINFO     CnrInfo;
  2333.  
  2334.   WinSendMsg (hwndCnr, CM_QUERYCNRINFO,
  2335.               MPFROMP(&CnrInfo), MPFROMSHORT(sizeof(CNRINFO)));
  2336.  
  2337.   /* Initialize DragImage structure with information about image
  2338.    * being dragged.
  2339.    */
  2340.   pDragImage->cb = sizeof( DRAGIMAGE );
  2341.   pDragImage->cptl = 0;
  2342.   pDragImage->fl = DRG_ICON;
  2343.   pDragImage->cxOffset = HIUSHORT(pCnrDragInit->cx);
  2344.   pDragImage->cyOffset = HIUSHORT(pCnrDragInit->cy);
  2345.  
  2346.   if ((pCnrDragInit->pRecord->hptrIcon) &&
  2347.       !(CnrInfo.flWindowAttr & CV_MINI))
  2348.   {
  2349.     /* Drag regular icon. */
  2350.     pDragImage->hImage = pRecord->hptrIcon;
  2351.   }
  2352.   else
  2353.   if ((pCnrDragInit->pRecord->hptrMiniIcon) &&
  2354.       (CnrInfo.flWindowAttr & CV_MINI))
  2355.   {
  2356.     /* Drag regular mini icon. */
  2357.     pDragImage->hImage = pRecord->hptrMiniIcon;
  2358.   }
  2359.   else
  2360.   {
  2361.     /* Drag system pointer. */
  2362.     pDragImage->hImage = WinQuerySysPointer(HWND_DESKTOP,
  2363.                                             SPTR_APPICON,FALSE);
  2364.   }
  2365.   /* Setup image offsets from the pointer hotspot. */
  2366.   pDragImage->cxOffset = (SHORT)pCnrDragInit->cx;
  2367.   pDragImage->cyOffset = (SHORT)pCnrDragInit->cy;
  2368. }
  2369.  
  2370. /*----------------------------------------------------------------------
  2371.  Function Name: ProcessDrop
  2372.  
  2373.  Description:
  2374.    This function is called when the container sends the CN_DROP
  2375.    notification.  This function moves the items in icon view.
  2376.    It will do the same for other non-tree views when the record
  2377.    is not being dropped on another record.  This sample program
  2378.    keeps track whether CN_DRAGOVER or CN_DRAGAFTER was last
  2379.    received so that we can act appropriately when CA_MIXEDTARGETEMPH
  2380.    is being used.
  2381.  
  2382.  Parameters:
  2383.    (HWND)hwnd                 - The container's owner window handle.
  2384.    (HWND)hwndCnr              - The container window handle.
  2385.    (PCNRDRAGINFO)pCnrDragInfo - The container's DragInfo structure.
  2386. ----------------------------------------------------------------------*/
  2387. VOID ProcessDrop( HWND hwnd, HWND hwndCnr, PCNRDRAGINFO pCnrDragInfo )
  2388. {
  2389.   PDRAGINFO     pDragInfo;
  2390.   CNRINFO       CnrInfo;
  2391.   RECORDINSERT  RecordInsert;
  2392.   PRECORDCORE   pRecord;
  2393.   PDRAGITEM     pDragItem;
  2394.  
  2395.  
  2396.   pDragInfo =  pCnrDragInfo->pDragInfo;
  2397.   DrgAccessDraginfo(pDragInfo);
  2398.  
  2399.   /* Check if dragging within  same container window. */
  2400.   if (hwndCnr == pDragInfo->hwndSource)
  2401.   {
  2402.     WinSendMsg (hwndCnr, CM_QUERYCNRINFO,
  2403.                 MPFROMP(&CnrInfo), MPFROMSHORT(sizeof(CNRINFO)));
  2404.  
  2405.     /* Query the drag item pointer for the dropped item. */
  2406.     pDragItem =  DrgQueryDragitemPtr (pDragInfo, 0);
  2407.  
  2408.     /* Get the record pointer from the drag item structure. */
  2409.     pRecord = (PRECORDCORE)pDragItem->ulItemID;
  2410.  
  2411.     /* Check if the current view is icon. */
  2412.     if (CnrInfo.flWindowAttr & CV_ICON)
  2413.     {
  2414.       /* Erase the record from previous location. */
  2415.       WinSendMsg (hwndCnr, CM_ERASERECORD, MPFROMP(pRecord), NULL);
  2416.  
  2417.       /* Update icon position with the new drop position. Add a
  2418.        * variance such that records are not dropped on top of each
  2419.        * other.
  2420.        */
  2421.       pRecord->ptlIcon.x = pDragInfo->xDrop;
  2422.       pRecord->ptlIcon.y = pDragInfo->yDrop;
  2423.  
  2424.       /* Map the drop coordinates to the Container window. */
  2425.       WinMapWindowPoints (HWND_DESKTOP, hwndCnr, &(pRecord->ptlIcon),
  2426.                           (SHORT)1);
  2427.  
  2428.       pRecord->ptlIcon.x += CnrInfo.ptlOrigin.x;
  2429.       pRecord->ptlIcon.y += CnrInfo.ptlOrigin.y;
  2430.  
  2431.       /* Paint the record in its new position within the Container. */
  2432.       WinSendMsg (hwndCnr, CM_INVALIDATERECORD, MPFROMP(&pRecord),
  2433.                   MPFROMSHORT(1));
  2434.     }
  2435.     else
  2436.     {
  2437.       PSAMPLEINFO      pSampleInfo;
  2438.  
  2439.       /* Get the pointer to our control block. */
  2440.       pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  2441.  
  2442.       /* Process the item as Name or Text view. The insertion will
  2443.        * only occur if we are not dropping the record on itself
  2444.        * and dragafter was the last drag message received.
  2445.        */
  2446.       if ((pRecord != pCnrDragInfo->pRecord) &&
  2447.           (pSampleInfo->bDragAfter) && (pCnrDragInfo->pRecord))
  2448.       {
  2449.         WinSendMsg (hwndCnr, CM_REMOVERECORD, MPFROMP(&pRecord),
  2450.                     MPFROM2SHORT(1,FALSE));
  2451.  
  2452.         /* Setup information for CM_INSERTRECORD. */
  2453.         RecordInsert.cb = (ULONG)sizeof(RECORDINSERT);
  2454.         RecordInsert.pRecordOrder = pCnrDragInfo->pRecord;
  2455.         RecordInsert.zOrder = (USHORT)CMA_TOP;
  2456.         RecordInsert.cRecordsInsert = (USHORT)1;
  2457.         RecordInsert.fInvalidateRecord = FALSE;
  2458.         RecordInsert.pRecordParent = NULL;
  2459.  
  2460.         WinSendMsg (hwndCnr, CM_INSERTRECORD, MPFROMP(pRecord),
  2461.                     MPFROMP(&RecordInsert));
  2462.  
  2463.         /* Paint the Container window. */
  2464.         WinSendMsg (hwndCnr, CM_INVALIDATERECORD,
  2465.                     NULL, MPFROM2SHORT(0, CMA_REPOSITION));
  2466.       }
  2467.       else if (pRecord !=  pCnrDragInfo->pRecord)
  2468.       {
  2469.         /* Process for dragover last received...The dragged record
  2470.          * was dropped on the target.  Left as an exercise for the
  2471.          * interested application developer.
  2472.          */
  2473.       }
  2474.     }
  2475.   }
  2476.   DrgFreeDraginfo (pDragInfo);
  2477. }
  2478.  
  2479. /*----------------------------------------------------------------------
  2480.  Function Name: RemoveRecords
  2481.  
  2482.  Description:
  2483.    This function will remove the records specified in the pMenuRecord
  2484.    linked list.  The pMenuRecord linked list was created to represent
  2485.    all the records that a particular context menu operation applied to.
  2486.  
  2487.  Parameters:
  2488.    (HWND) hwnd - The handle of the client window.
  2489. ----------------------------------------------------------------------*/
  2490. VOID RemoveRecords (HWND hwnd)
  2491. {
  2492.   PSAMPLEINFO    pSampleInfo;
  2493.   PPERSONRECORD  pPersonRecNext;
  2494.   PPERSONRECORD  pPersonRec;
  2495.  
  2496.   /* Get the pointer to our control block. */
  2497.   pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  2498.  
  2499.   /* This value is a pointer to a linked list of records the
  2500.    * context menu applies to.  Remove each of the records in
  2501.    * this linked list.
  2502.    */
  2503.   if (pSampleInfo->pMenuRecord)
  2504.   {
  2505.     pPersonRec = pSampleInfo->pMenuRecord;
  2506.     while (pPersonRec)
  2507.     {
  2508.       pPersonRecNext=(PPERSONRECORD)pPersonRec->MiniRec.preccNextRecord;
  2509.       WinSendMsg (pSampleInfo->hwndCnr, CM_REMOVERECORD,
  2510.                   MPFROMP(&pPersonRec), MPFROM2SHORT(1, CMA_FREE));
  2511.       pPersonRec = pPersonRecNext;
  2512.     }
  2513.  
  2514.     /* Before invalidating the records, we must query the latest and
  2515.      * most up-to-date information for them.  Remember that the
  2516.      * CM_INVALIDATERECORD message will copy the current contents
  2517.      * of flRecordAttr and ptlIcon to the internal storage area
  2518.      * maintained for each record.  Since we are using record
  2519.      * sharing, this call is necessary.  Since these records may
  2520.      * have been shared, it is possible that the external values
  2521.      * for flRecordAttr and ptlIcon are not valid.  We need to
  2522.      * query the valid information from the container first, so we
  2523.      * don't cause any problems.
  2524.      */
  2525.     WinSendMsg (pSampleInfo->hwndCnr, CM_QUERYRECORDINFO,
  2526.                 NULL, MPFROMSHORT(0));
  2527.  
  2528.     /* Invaidate all the records. */
  2529.     WinSendMsg (pSampleInfo->hwndCnr, CM_INVALIDATERECORD,
  2530.                 NULL, MPFROM2SHORT(0, CMA_REPOSITION));
  2531.   }
  2532.   return;
  2533. }
  2534.  
  2535. /*----------------------------------------------------------------------
  2536.  Function Name: GetSelectedRecords
  2537.  
  2538.  Description:
  2539.    This function builds a linked list of currently selected records
  2540.    that exist in the container and returns a pointer to the head of
  2541.    the list.  This function is called when the user requests a
  2542.    context menu on a record that is selected.  When this happens, the
  2543.    menu should apply to all currently selected records.
  2544.  
  2545.  Parameters:
  2546.    (HWND) hwnd - The handle of the client window.
  2547. ----------------------------------------------------------------------*/
  2548. PPERSONRECORD GetSelectedRecords (HWND hwnd)
  2549. {
  2550.   PSAMPLEINFO    pSampleInfo;
  2551.   PPERSONRECORD  pPersonRec;
  2552.   PPERSONRECORD  pPersonRecFirst = NULL;
  2553.   PPERSONRECORD  pPersonRecCur = NULL;
  2554.  
  2555.   /* Get the pointer to our control block. */
  2556.   pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
  2557.  
  2558.   /* Get the first selected record in the container. */
  2559.   pPersonRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
  2560.                                           CM_QUERYRECORDEMPHASIS,
  2561.                                           (MPARAM)CMA_FIRST,
  2562.                                           MPFROMSHORT(CRA_SELECTED));
  2563.  
  2564.   /* Build a linked list of selected records.  When the list is
  2565.    * complete, return a pointer to the head of the list.
  2566.    */
  2567.   while (pPersonRec)
  2568.   {
  2569.     if (pPersonRecFirst)
  2570.     {
  2571.       pPersonRecCur->MiniRec.preccNextRecord =
  2572.                               (PMINIRECORDCORE)pPersonRec;
  2573.     }
  2574.     else
  2575.     {
  2576.       pPersonRecFirst = pPersonRec;
  2577.     }
  2578.     pPersonRecCur = pPersonRec;
  2579.     pPersonRecCur->MiniRec.preccNextRecord = NULL;
  2580.  
  2581.     /* Get the next selected record in the container. */
  2582.     pPersonRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
  2583.                                             CM_QUERYRECORDEMPHASIS,
  2584.                                             MPFROMP(pPersonRec),
  2585.                                             MPFROMSHORT(CRA_SELECTED));
  2586.   }
  2587.   return (pPersonRecFirst);
  2588. }
  2589.  
  2590. /*----------------------------------------------------------------------
  2591.  Function Name: FindInUseRecord
  2592.  
  2593.  Description:
  2594.    This function is called whenever an additional view is closed.  Since
  2595.    we added IN_USE emphasis to the record that was clicked on in the
  2596.    main container window, we need to find the record that IN_USE
  2597.    emphasis was applied to.  We do so by looking at the records in the
  2598.    main container window that have IN_USE emphasis already applied.
  2599.    If their icon string matches the title of the additional view,
  2600.    we have found the record which matches the view being closed.
  2601.  
  2602.  Parameters:
  2603.    (HWND) hwnd  - The handle of the client window.
  2604. ----------------------------------------------------------------------*/
  2605. PPERSONRECORD FindInUseRecord (HWND hwndAdditionalCnr, HWND hwndMainCnr)
  2606. {
  2607.   CNRINFO        CnrInfo;
  2608.   PPERSONRECORD  pInUseRec;
  2609.  
  2610.   WinSendMsg (hwndAdditionalCnr, CM_QUERYCNRINFO,
  2611.               MPFROMP(&CnrInfo), MPFROMSHORT(sizeof(CNRINFO)));
  2612.  
  2613.   pInUseRec = WinSendMsg (hwndMainCnr, CM_QUERYRECORDEMPHASIS,
  2614.                          (MPARAM)CMA_FIRST, MPFROMSHORT(CRA_INUSE));
  2615.  
  2616.   while ((pInUseRec) &&
  2617.          (strcmp (CnrInfo.pszCnrTitle, pInUseRec->MiniRec.pszIcon)))
  2618.   {
  2619.     pInUseRec = WinSendMsg (hwndMainCnr, CM_QUERYRECORDEMPHASIS,
  2620.                            MPFROMP(pInUseRec), MPFROMSHORT(CRA_INUSE));
  2621.   }
  2622.   return (pInUseRec);
  2623. }
  2624.