home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: InfoMgt / InfoMgt.zip / shr93.zip / ADDRBKV.C < prev    next >
Text File  |  1993-10-06  |  31KB  |  947 lines

  1. /*
  2.  * OS/2 Work Place Shell Sample Program - ShrAddressBook view code
  3.  *
  4.  * Copyright (C) 1993 IBM Corporation
  5.  *
  6.  *   DISCLAIMER OF WARRANTIES.  The following [enclosed] code is
  7.  *   sample code created by IBM Corporation.  This sample code is
  8.  *   not part of any standard or IBM product and is provided to you
  9.  *   solely for the purpose of assisting you in the development of
  10.  *   your applications.  The code is provided "AS IS".  ALL
  11.  *   WARRANTIES ARE EXPRESSLY DISCLAIMED, INCLUDING THE IMPLIED
  12.  *   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  13.  *   PURPOSE.  IBM shall not be liable for any damages arising out
  14.  *   of your use of the sample code, even if IBM has been advised of
  15.  *   the possibility of such damages.
  16.  *
  17.  * The address book view window structure is shown below
  18.  * along with the corresponding window procedures:
  19.  *
  20.  *   hwndFrame (ShrAddressBookFrameWndProc)
  21.  *        |
  22.  *   hwndNotebook (WC_NOTEBOOK)
  23.  *        |
  24.  *   hwndCnrOwner (ShrFrameCnrOwnerWndProc)
  25.  *        |
  26.  *   hwndCnr (WC_CONTAINER)
  27.  *
  28.  * All the notebook pages point to the same window, hwndCnrOwner.  
  29.  * The contents of the container is filtered based on the current top
  30.  * page.  
  31.  *
  32.  * Note: Add to this source file the line:
  33.  *
  34.  *       #define ADDRESSBOOKINDETAILSVIEW
  35.  *
  36.  * To include the code that changes the address book's container view
  37.  * to details view instead of the default icon view.
  38.  */
  39.  
  40. #define SOM_NoTest
  41.  
  42. #include "share.h"
  43. #include "addrbk.h"
  44. #include "addrbkv.h"
  45. #include "notify.h"
  46. #include "person.h"
  47.  
  48. /*
  49.  * A pointer to the view data for the Address Book is kept
  50.  * in the extra window word (QW_USER) of the frame.
  51.  */
  52.  
  53. #define setAddressBookViewDataFromFrame(h,p) \
  54.     WinSetWindowULong(h,QWL_USER,(ULONG)p)
  55. #define queryAddressBookViewDataFromFrame(h) \
  56.     (PADDRESSBOOKVIEWDATA) WinQueryWindowULong(h,QWL_USER)
  57.  
  58. /*
  59.  * Below is the data that is kept for each Address
  60.  * Book's Indexed View.  It would be better if this structure
  61.  * was converted to a SOM object with methods like shrOpen,
  62.  * shrClose, shrPersonAdded, etc.
  63.  */
  64.  
  65. typedef struct
  66. {
  67.   SOMAny *somSelf;
  68.   HWND hwndCnr;
  69.   HWND hwndNotebook;
  70. } ADDRESSBOOKVIEWDATA, *PADDRESSBOOKVIEWDATA;
  71.  
  72. /*
  73.  * ShrFilterAddressBookRecord is a container filter function.
  74.  *
  75.  * It allows objects who's first character below to the set
  76.  * of characters for the page.  For example, "Smith" belongs in 
  77.  * "S - V" page.  The pszIndexChars parameter would point to
  78.  * the string "STUV" when filtering for that page (see CM_FILTER
  79.  * message for more details).
  80.  */
  81.  
  82. extern BOOL EXPENTRY ShrFilterAddressBookRecord
  83.     (PMINIRECORDCORE pRecord, PSZ pszIndexChars)
  84. {
  85.   BOOL bInclude = FALSE;
  86.   PSZ pszTitle;
  87.   CHAR chFirst;
  88.   SOMAny *somObject;
  89.  
  90.   /*
  91.    * In theory, no non-person objects can get into the
  92.    * address book since ShrAddressBook's wpDragOver
  93.    * method refuses non-person objects.
  94.    *
  95.    * But check anyway just in case the user opened a command line
  96.    * and copied something to the address book subdirectory.
  97.    *
  98.    * Note that _somIsA is not used here.  That is because later
  99.    * developers may want to implement a new-and-improved version
  100.    * of the person object that is not a direct descendant of
  101.    * ShrPerson.  But note there is a problem -- ShrPerson
  102.    * has many methods that are not "name lookup" methods, ie, they
  103.    * use the offset method, so creating a new person class that is
  104.    * not a subclass of ShrPerson would require considerable care since
  105.    * it would have to be certain to have its version of ShrPerson
  106.    * methods in the same order in the method table as ShrPerson's.
  107.    */
  108.  
  109.   somObject = _shrclsValidateObject(ShrFindAddressBookClass(),
  110.       OBJECT_FROM_PREC(pRecord));
  111.  
  112.   if (somObject)
  113.   {
  114.     pszTitle = _wpQueryTitle(somObject);
  115.  
  116.     /*
  117.      * When the user creates a new person for the address
  118.      * book, it is given the default name "Last name, First name".
  119.      *
  120.      * We'd like to make it easy to find the newly created person,
  121.      * so the user doesn't have to search through the pages 
  122.      * (ie, the "J - L" page would contain "Last name, First name").  
  123.      * So add a filter check for the default name, and if this object
  124.      * has it, allow it on all pages.
  125.      */
  126.  
  127.     if (strstr(pszTitle, vszPersonNewTitle))
  128.     {
  129.       bInclude = TRUE;
  130.     }
  131.     else
  132.     {
  133.       chFirst = toupper(pszTitle[0]);
  134.   
  135.       /*
  136.        * The index characters (pszIndexChars) is a list of those
  137.        * characters allowed in the address book page that is 
  138.        * currently active. For example, the "A - C" page index 
  139.        * characters string is "ABC", "V - Z" page uses "VWXYZ".
  140.        *
  141.        * If it is NULL, then we're filtering for the "Misc" page
  142.        * which contains all others that don't belong in the
  143.        * "A" to "Z" pages (eg, "1st Federal Bank").  We check if
  144.        * the first character doesn't fit in the "A" to "Z" pages
  145.        * by searching "vszAllIndexChars" which contains the 
  146.        * characters "ABCDEFGHIJKLMNOPQRSTUVWXYZ".
  147.        */
  148.   
  149.       if (pszIndexChars)
  150.         bInclude = BOOLFROMP(strchr(pszIndexChars, chFirst));
  151.       else
  152.         bInclude = !strchr(vszAllIndexChars, chFirst);
  153.     }
  154.   }
  155.  
  156.   return bInclude;
  157. }
  158.  
  159. /*
  160.  * ShrSortAddressBookRecord is a container sort function.
  161.  * It simply sorts by object title.  
  162.  *
  163.  * See the pSortRecord field in the CNRINFO structure, 
  164.  * the CM_SETCNRINFO message, and the CM_SORTRECORD message for
  165.  * more details.
  166.  */
  167.  
  168. extern SHORT EXPENTRY ShrSortAddressBookRecord
  169.     (PMINIRECORDCORE pRecord1, PMINIRECORDCORE pRecord2, PVOID pUnused)
  170. {
  171.   return stricmp(_wpQueryTitle(OBJECT_FROM_PREC(pRecord1)),
  172.       _wpQueryTitle(OBJECT_FROM_PREC(pRecord2)));
  173. }
  174.  
  175. /*
  176.  * ShrCnrInsertObject inserts the object into the given container.
  177.  *
  178.  * No positioning is done, so CCS_AUTOPOSITION should be specified
  179.  * when creating the container.
  180.  */
  181.  
  182. extern PMINIRECORDCORE ShrCnrInsertObject
  183.     (SOMAny *somInsertObject, HWND hwndCnr)
  184. {
  185.   RECORDINSERT recordInsert;
  186.   POINTL ptlIcon;
  187.   PMINIRECORDCORE pMiniRecord;
  188.  
  189.   /*
  190.    * Note the record is inserted without invalidating.
  191.    * This is because the container records will later
  192.    * be filtered with CM_FILTER which invalidates all
  193.    * the records (see ShrAddressBookUpdateTopPage for
  194.    * more details).
  195.    */
  196.  
  197.   ptlIcon.x = ptlIcon.y = 0;
  198.  
  199.   memset(&recordInsert, 0, sizeof(recordInsert));
  200.   recordInsert.cb = sizeof(RECORDINSERT);
  201.   recordInsert.pRecordOrder = (PRECORDCORE) CMA_END;
  202.   recordInsert.zOrder = CMA_TOP;
  203.   recordInsert.cRecordsInsert = 1;
  204.   recordInsert.fInvalidateRecord = FALSE;
  205.  
  206.   pMiniRecord = _wpCnrInsertObject(somInsertObject, hwndCnr,
  207.       &ptlIcon, NULL, &recordInsert);
  208.  
  209.   return pMiniRecord;
  210. }
  211.  
  212. /*
  213.  * ShrAddressBookUpdateTopPage handles the user switching to a new
  214.  * page in the address book.
  215.  */
  216.  
  217. extern VOID ShrAddressBookUpdateTopPage
  218.     (SOMAny *somSelf, HWND hwndNotebook, HWND hwndCnr)
  219. {
  220.   ULONG ulPageId;
  221.   PSZ pszIndexChars;
  222.   BOOKTEXT booktext;
  223.   CNRINFO cnrInfo;
  224.  
  225.   /*
  226.    * The notebook page data points to a static string containing
  227.    * the list of valid first-characters for the page, eg:
  228.    * "ABC" for "A - C".  If the index characters pointer is NULL,
  229.    * then the page is the "Misc" page, ie, it holds all the persons
  230.    * whose names do not fit into one of the other pages (eg,
  231.    * "1st Federal Bank").
  232.    */
  233.  
  234.   ulPageId = (ULONG) WinSendMsg(hwndNotebook, BKM_QUERYPAGEID,
  235.         MPFROMLONG(BKA_TOP), MPFROMLONG(BKA_TOP));
  236.   pszIndexChars = (PSZ) WinSendMsg(hwndNotebook, BKM_QUERYPAGEDATA,
  237.         MPFROMLONG(ulPageId), NULL);
  238.   WinSendMsg(hwndCnr, CM_FILTER,
  239.         MPFROMP(ShrFilterAddressBookRecord), pszIndexChars);
  240.  
  241.   /*
  242.    * OS/2 2.0 GA container had a bug which did not re-sort the
  243.    * containers when the icon text was changed even though
  244.    * CMA_PSORTRECORD was specified on CM_SETCNRINFO.
  245.    *
  246.    * Make an extra call to CM_SORTRECORD in case we're running
  247.    * on a 2.0 machine.
  248.    */
  249.  
  250.   WinSendMsg(hwndCnr, CM_SORTRECORD,
  251.         MPFROMP(ShrSortAddressBookRecord), NULL);
  252.  
  253.   /*
  254.    * The address book container title is set to the tab of the
  255.    * top page to make it a little more obvious what page the user
  256.    * is dealing with.
  257.    * 
  258.    * Note the container title buffer must be static, ie, not on the
  259.    * stack, since the container does not make a copy of it
  260.    * but strictly stores your pointer to it.
  261.    */
  262.  
  263.   booktext.pString = vszAddressBookCnrTitle;
  264.   booktext.textLen = sizeof(vszAddressBookCnrTitle);
  265.   WinSendMsg(hwndNotebook, 
  266.         BKM_QUERYTABTEXT, MPFROMLONG(ulPageId),
  267.         &booktext);
  268.  
  269.   WinSendMsg(hwndCnr, CM_QUERYCNRINFO, MPFROMP(&cnrInfo),
  270.       MPFROMSHORT(sizeof(cnrInfo)));
  271.  
  272.   cnrInfo.flWindowAttr |= CA_CONTAINERTITLE |
  273.       CA_TITLEREADONLY | CA_TITLESEPARATOR;
  274.   cnrInfo.pszCnrTitle = vszAddressBookCnrTitle;
  275.  
  276.   WinSendMsg(hwndCnr, CM_SETCNRINFO, MPFROMP(&cnrInfo), 
  277.       MPFROMLONG(CMA_FLWINDOWATTR | CMA_CNRTITLE));
  278. }
  279.  
  280. /*
  281.  * ShrAddressBookPersonAdded is called when the open view 
  282.  * notified via SHRN_ADDRESSBOOKPERSONADDED
  283.  * page in the address book.
  284.  
  285.  * Note: The address book view is notified because it has 
  286.  * registered interest via the shrAddInterestedWindow method
  287.  * (see ShrNotifier for more details).
  288.  */
  289.  
  290. extern VOID ShrAddressBookPersonAdded
  291.     (SOMAny *somSelf, HWND hwndNotebook, HWND hwndCnr, 
  292.     SOMAny *somNewPerson)
  293. {
  294.   ShrCnrInsertObject(somNewPerson, hwndCnr);
  295.   ShrAddressBookUpdateTopPage(somSelf, hwndNotebook, hwndCnr);
  296. }
  297.  
  298. /*
  299.  * ShrAddressBookDestroy is called when the address book frame
  300.  * receives WM_DESTROY.
  301.  */
  302.  
  303. extern VOID ShrAddressBookDestroy
  304.     (SOMAny *somSelf, HWND hwndFrame, HWND hwndCnr)
  305. {
  306.   /*
  307.    * This view is going away, so remove interest in the model.
  308.    */
  309.  
  310.   _shrRemoveInterestedWindow(_shrQueryNotifier(somSelf), hwndFrame);
  311.  
  312.   /*
  313.    * Remove all the WPS object container records from
  314.    * the container before closing (otherwise
  315.    * the default container destroy action is to free the records,
  316.    * which screws up WPS since its records are shared).
  317.    */
  318.  
  319.   ShrEmptyObjectCnr(hwndCnr);
  320.  
  321.   /*
  322.    * Save the colors, fonts, size and position of the
  323.    * address book frame.
  324.    */
  325.  
  326.   WinStoreWindowPos(vszAppName, vszIndexedViewWindowPosKeyName,
  327.       hwndFrame);
  328.  
  329.   /*
  330.    * Remove and free the USAGE_OPENVIEW item from the address
  331.    * book use list.
  332.    */
  333.  
  334.   ShrClosingView(somSelf, hwndFrame);
  335. }
  336.  
  337. /*
  338.  * ShrCnrRegisterOwner is used to register a FRAME window as
  339.  * a view of a WPS object.  Calling this method associates the
  340.  * frame with the object so WPS can figure out what frames belong
  341.  * to which objects during drag operations.
  342.  *
  343.  * Note: wpRegisterView will subclass the frame window to add
  344.  * standard WPS container object behavior (drag/drop, context
  345.  * menus, etc).  The wpRegisterView method expects top-level frame
  346.  * windows since it adds it to the Window List.  But we have to
  347.  * call it anyway to get WPS to subclass this frame, even though
  348.  * it isn't a top level window.  As fate would have it, the call
  349.  * to WinAddSwitchEntry fails for this frame is not top-level.
  350.  *
  351.  * See ShrOpenAddressBook for more details.
  352.  *
  353.  * A better long-term solution would be for WPS to add 
  354.  * wpCnrRegisterOwner method that does all that wpRegisterView
  355.  * does today EXCEPT for trying to add the frame to the Window List.
  356.  * (see "Problem 1" in WPS-PGM.TXT for more details).
  357.  */
  358.  
  359. extern BOOL ShrCnrRegisterOwner(SOMAny *somObject, HWND hwndCnrOwner)
  360. {
  361.   return _wpRegisterView(somObject, hwndCnrOwner, vszNullString);
  362. }
  363.  
  364. /*
  365.  * ShrAddressBookFrameWndProc
  366.  */
  367.  
  368. extern MRESULT EXPENTRY ShrAddressBookFrameWndProc
  369.     (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  370. {
  371.   MRESULT mr = NULL;
  372.   PADDRESSBOOKVIEWDATA pViewData;
  373.  
  374.   switch (msg)
  375.   {
  376.     /*
  377.      * SHRN_ADDRESSBOOKPERSONADDED is sent when a person
  378.      * is added to the address book model.  Update the
  379.      * view to match.
  380.      */
  381.  
  382.     case SHRN_ADDRESSBOOKPERSONADDED:
  383.       pViewData = queryAddressBookViewDataFromFrame(hwnd);
  384.  
  385.       ShrAddressBookPersonAdded(pViewData->somSelf,
  386.               pViewData->hwndNotebook, pViewData->hwndCnr,
  387.               (SOMAny *) mp2);
  388.       break;
  389.  
  390.     /*
  391.      * WM_CONTROL is sent when an owned control has something
  392.      * about it that has changed.  We're only interested in the
  393.      * page turning activity of the notebook.
  394.      */
  395.  
  396.     case WM_CONTROL:
  397.       pViewData = queryAddressBookViewDataFromFrame(hwnd);
  398.  
  399.       if (SHORT1FROMMP(mp1) == FID_CLIENT)
  400.       {
  401.         if (SHORT2FROMMP(mp1) == BKN_PAGESELECTED)
  402.           ShrAddressBookUpdateTopPage(pViewData->somSelf,
  403.               pViewData->hwndNotebook, pViewData->hwndCnr);
  404.       }
  405.       else
  406.       {
  407.         mr = ShrDefWPSCnrOwnerProc(hwnd, msg, mp1, mp2);
  408.       }
  409.       break;
  410.  
  411.     /*
  412.      * WM_DESTROY is sent as the last message to the window.
  413.      * Make certain WM_DESTROY is forwarded to the superclass
  414.      * so frame resources are freed.
  415.      *
  416.      * Here we have to check the view data pointer because
  417.      * the frame may have been successfully created, but
  418.      * one of the children could not be created.  Under
  419.      * all other cases this check is not necessary.
  420.      */
  421.  
  422.     case WM_DESTROY:
  423.       pViewData = queryAddressBookViewDataFromFrame(hwnd);
  424.  
  425.       if (pViewData)
  426.       {
  427.         ShrAddressBookDestroy(pViewData->somSelf,
  428.             hwnd, pViewData->hwndCnr);
  429.         _wpFreeMem(pViewData->somSelf, (PBYTE) pViewData);
  430.       }
  431.  
  432.       ShrDefWPSCnrOwnerProc(hwnd, msg, mp1, mp2);
  433.       break;
  434.  
  435.     /*
  436.      * WM_SYSCOMMAND is sent from the system menu.  We only
  437.      * handle the close case.
  438.      */
  439.  
  440.     case WM_SYSCOMMAND:
  441.       if (SHORT1FROMMP(mp1) == SC_CLOSE)
  442.         WinDestroyWindow(hwnd);
  443.       else
  444.         mr = ShrDefWPSCnrOwnerProc(hwnd, msg, mp1, mp2);
  445.       break;
  446.  
  447.     /*
  448.      * WM_CLOSE is sent by the wpClose method to all open views
  449.      * of this object just before the object is deleted.
  450.      * Destroy the open view.
  451.      */
  452.  
  453.     case WM_CLOSE:
  454.       WinDestroyWindow(hwnd);
  455.       break;
  456.  
  457.     default:
  458.       mr = ShrDefWPSCnrOwnerProc(hwnd, msg, mp1, mp2);
  459.       break;
  460.   }
  461.  
  462.   return mr;
  463. }
  464.  
  465. /*
  466.  * ShrOpenAddressBook creates the address book view and displays it.
  467.  */
  468.  
  469. extern HWND ShrOpenAddressBook(SOMAny *somSelf)
  470. {
  471.   HWND hwndFrame = NULLHANDLE;
  472.   PADDRESSBOOKVIEWDATA pViewData = NULL;
  473.   BOOL bHoldingSem = FALSE;
  474.   HWND hwndNotebook;
  475.   HWND hwndCnrOwner;
  476.   HWND hwndCnr;
  477.   FRAMECDATA fcdata;
  478.   HPS hps;
  479.   SHORT sHeight = 0, sWidth = 0, sTemp;
  480.   ULONG i, ulPageId;
  481.   RECTL rectl;
  482.   SOMAny *somObject;
  483.   CNRINFO cnrInfo;
  484.   SWP swp;
  485. #ifdef ADDRESSBOOKINDETAILSVIEW
  486.   FIELDINFOINSERT fieldInfoInsert;
  487.   PCLASSFIELDINFO pCFI, pClassFieldInfo = NULL;
  488.   PFIELDINFO pFI, pFirstFI;
  489.   ULONG cColumns, offTotal;
  490. #endif
  491.  
  492.   /*
  493.    * NOTE!  It is very important that exception handlers be coded in 
  494.    * routines that request mutex semaphores!  The exception condition
  495.    * should relinquish the semaphore if it was successfully
  496.    * requested.  Otherwise, later requestors of the semaphore could
  497.    * wait indefinitely, typically resulting in a hang of one of the
  498.    * WPS threads or the entire WPS.
  499.    *
  500.    * ShrStartTryBlock is a macro that installs an exception
  501.    * handler for this function.  See SHARE.H for more details.
  502.    */
  503.  
  504.   ShrStartTryBlock;
  505.  
  506.   /*
  507.    * Create the Address Book view.  It's parent/child 
  508.    * window structure is:
  509.    *
  510.    *   hwndFrame, unowned (ShrAddressBookFrameWndProc)
  511.    *      |
  512.    *    hwndNotebook, owned by hwndFrame (WC_NOTEBOOK)
  513.    *       |
  514.    *     hwndCnrOwner, owned by hwndNotebook (ShrFrameCnrOwnerWndProc)
  515.    *        |
  516.    *      hwndCnr, owned by hwndCnrOwner (WC_CONTAINER)
  517.    */
  518.  
  519.   memset(&fcdata, 0, sizeof(fcdata));
  520.   fcdata.cb = sizeof(FRAMECDATA);
  521.   fcdata.flCreateFlags = FCF_TITLEBAR | FCF_SYSMENU | FCF_MINMAX |
  522.       FCF_SIZEBORDER;
  523.  
  524.   hwndFrame = WinCreateWindow(HWND_DESKTOP,
  525.      WC_FRAME, vszNullString,
  526.      WS_ANIMATE, 0,0,0,0, NULLHANDLE, HWND_TOP, 0, &fcdata, NULL);
  527.  
  528.   /*
  529.    * The notebook is initially created unowned to avoid
  530.    * unnecessary updates when the first page is inserted, ie,
  531.    * WM_CONTROL (BKN_PAGESELECTED).
  532.    *
  533.    * Once all the pages are inserted, the notebook owner
  534.    * is set to the top frame (hwndFrame).
  535.    */
  536.  
  537.   hwndNotebook = WinCreateWindow(hwndFrame, WC_NOTEBOOK,
  538.       vszNullString,
  539.       (WS_VISIBLE | BKS_SPIRALBIND | BKS_SQUARETABS |
  540.       BKS_BACKPAGESBR | BKS_MAJORTABBOTTOM  |
  541.       BKS_TABTEXTCENTER), 0,0,0,0,
  542.       NULLHANDLE, HWND_TOP, FID_CLIENT, NULL, NULL);
  543.  
  544.   /* OS/2 GA bug:
  545.    *
  546.    * WPS assumes that the container is a direct child of
  547.    * the frame, and that the frame has a FID_SYSMENU.  If
  548.    * it does not have a system menu, then the context menu 
  549.    * created by clicking on the whitespace of the container 
  550.    * (which brings up the address book context menu) erroneously 
  551.    * sends its menu selections to the desktop in OS/2 2.0 GA.
  552.    *
  553.    * In OS/2 2.1 beta, the menu selections are sent to the
  554.    * correct window, but context menu emphasis is not set.
  555.    *
  556.    * This bug has been report to Boca (4/2/93).
  557.    */
  558.  
  559.   fcdata.flCreateFlags = 0;
  560.   hwndCnrOwner = WinCreateWindow(hwndNotebook,
  561.      vszWcFrameCnrOwner, vszNullString,
  562.      0, 0,0,0,0, hwndNotebook, HWND_TOP, 0, &fcdata, NULL);
  563.  
  564.   /*
  565.    * If a container is to hold WPS objects (ie, it will be used as 
  566.    * hwndCnr parameter in calls to the wpCnrInsertObject 
  567.    * and wpCnrRemoveObject method), ITS OWNER AND PARENT MUST BE
  568.    * A SUBCLASS OF WC_FRAME.  In addition, the container ID must
  569.    * be FID_CLIENT.
  570.    *
  571.    * WPS makes assumptions about the container owner that are only
  572.    * valid for WC_FRAME subclasses.
  573.    */
  574.  
  575.   hwndCnr = WinCreateWindow(hwndCnrOwner,
  576.       WC_CONTAINER, vszNullString,
  577.       (WS_VISIBLE | CCS_EXTENDSEL | CCS_AUTOPOSITION |
  578.       CCS_VERIFYPOINTERS | CCS_MINIRECORDCORE),
  579.       0,0,0,0, hwndCnrOwner, HWND_BOTTOM,
  580.       FID_CLIENT, NULL, NULL);
  581.  
  582.   /*
  583.    * Allocate and save the view's instance data.  It would be a better
  584.    * implementation to create a "view" object as a subclass of 
  585.    * SOMObject with methods like _shrOpenView, _shrCloseView,
  586.    * _shrPageSelected, etc.
  587.    *
  588.    * I'll leave that as an exercise for the user.
  589.    */
  590.  
  591.   pViewData = (PADDRESSBOOKVIEWDATA) 
  592.       _wpAllocMem(somSelf, sizeof(ADDRESSBOOKVIEWDATA), NULL);
  593.   pViewData->somSelf = somSelf;
  594.   pViewData->hwndNotebook = hwndNotebook;
  595.   pViewData->hwndCnr = hwndCnr;
  596.   setAddressBookViewDataFromFrame(hwndFrame, pViewData);
  597.  
  598.   /*
  599.    * When a view is registered with wpRegisterView, it
  600.    * is subclassed and its window procedure pointer saved
  601.    * in a reserved window words.  The WPS subclass handles
  602.    * all the CN_ messages for WPS object container records
  603.    * inserted into our contents container, along with the
  604.    * system menu code to display the context menu, and
  605.    * a few WM_COMMAND (delete, copy, move, etc).
  606.    *
  607.    * We're going to re-subclass so ShrAddressBookFrameWndProc
  608.    * gets called before the WPS version.  This is necessary
  609.    * because the WPS version does not forward any
  610.    * WM_CONTROL messages to its superclass, and
  611.    * ShrAddressBookFrameWndProc needs the notebook notifications.
  612.    *
  613.    * Note the top frame was registered with the default
  614.    * frame window procedure -- that will be the superclass
  615.    * the WPS window procedure calls when it doesn't
  616.    * handle a message, ie:
  617.    *
  618.    *   1. ShrAddressBookFrameWndProc, first, followed by
  619.    *   2. WPS container owner window subclass, followed by
  620.    *   3. WC_FRAME's window procedure, followed by
  621.    *   4. WinDefWindowProc last
  622.    *
  623.    * WARNING: The notebook subclasses the page on
  624.    * BKM_SETPAGEWINDOWHWND if it is the topmost page
  625.    * and when a page is made the top page by the user clicking
  626.    * on a tab.  Later, when the page is no longer the top
  627.    * page, the notebook tries to unsubclass the window
  628.    * by replacing QWP_PFNWP with the pointer that was
  629.    * there before.  So WPS must subclass first so the
  630.    * notebook does not undo the WPS subclass.
  631.    */
  632.  
  633.   ShrOpeningView(somSelf, hwndFrame, 
  634.       ID_OPENADDRESSINDEXVIEW, vszAddressBookViewTitle);
  635.   WinSubclassWindow(hwndFrame, ShrAddressBookFrameWndProc);
  636.  
  637.   /*
  638.    * The WPS window procedure requires that the container
  639.    * owner and parent be the same.  Otherwise, the container
  640.    * will not have standard WPS container behavior.
  641.    *
  642.    * Note: The BKM_SETPAGEWINDOWHWND message changes
  643.    * hwndCnrOwner's parent to one of the notebooks
  644.    * children.  This ensures that the page window draws
  645.    * only within the top page boundaries.
  646.    */
  647.  
  648.   ShrCnrRegisterOwner(somSelf, hwndCnrOwner);
  649.  
  650.   /*
  651.    * Add the notebook pages, set their page data, and calculate
  652.    * the size of the largest tab.
  653.    */
  654.  
  655.   hps = WinGetPS(hwndNotebook);
  656.  
  657.   for (i = 0; i < CINDEXTABS; i++)
  658.   {
  659.     ulPageId = (ULONG) WinSendMsg(hwndNotebook, BKM_INSERTPAGE, 0,
  660.         MPFROM2SHORT((BKA_MAJOR|BKA_AUTOPAGESIZE), BKA_LAST));
  661.  
  662.     /*
  663.      * All the pages of the notebook are associated the same
  664.      * container.  The contents of the container is filtered based 
  665.      * on the current top page.  
  666.      */
  667.  
  668.     WinSendMsg(hwndNotebook, BKM_SETPAGEWINDOWHWND,
  669.         MPFROMP(ulPageId), MPFROMLONG(hwndCnrOwner));
  670.     WinSendMsg(hwndNotebook, BKM_SETTABTEXT,
  671.         MPFROMLONG(ulPageId), vaszIndexTabs[i]);
  672.  
  673.     /*
  674.      * There is one extra page for those persons that don't
  675.      * belong in any other page (eg, '1st Federal Bank').
  676.      * All the other pages have page data that points to a
  677.      * string containing valid first characters for person's
  678.      * on their page, eg, "ABC" for the "A - C" page.
  679.      *
  680.      * See ShrFilterAddressBookRecord and the CM_FILTER
  681.      * message for more details.
  682.      *
  683.      * Note that CINDEXCHARS is one less than CINDEXTABS.
  684.      * The extra tab is for the "Misc" page, and its page data
  685.      * pointer is NULL.
  686.      */
  687.  
  688.     if (i < CINDEXCHARS)
  689.       WinSendMsg(hwndNotebook, BKM_SETPAGEDATA,
  690.           MPFROMLONG(ulPageId), vaszIndexChars[i]);
  691.  
  692.     /*
  693.      * Calculate the largest tab size.  All the tabs in the notebook
  694.      * are the same size, and we don't want to clip the tab text.
  695.      *
  696.      * WinDrawText determines the size needed to properly draw tab
  697.      * text.  The actual drawing of the tabs is done by the notebook.
  698.      */
  699.  
  700.     rectl.xLeft = rectl.yBottom = 0;
  701.     rectl.xRight = rectl.yTop = 1;
  702.     WinDrawText(hps, -1, vaszIndexTabs[i], 
  703.         &rectl, 0, 0, DT_QUERYEXTENT);
  704.   
  705.     sTemp = (SHORT) (rectl.xRight - rectl.xLeft);
  706.     if (sTemp > sWidth)
  707.       sWidth = sTemp;
  708.   
  709.     sTemp = (SHORT) (rectl.yTop - rectl.yBottom);
  710.     if (sTemp > sHeight)
  711.       sHeight = sTemp;
  712.   }
  713.  
  714.   WinReleasePS(hps);
  715.  
  716.   /*
  717.    * Set the major tab size to the size of the largest tab
  718.    * text, and set the minor tabs to (0,0) since the address
  719.    * book view doesn't use them.  Note the fudge factor added
  720.    * below to allow for tab cursor emphasis.
  721.    */
  722.  
  723.   WinSendMsg(hwndNotebook, BKM_SETDIMENSIONS,
  724.       MPFROM2SHORT(sWidth+8, sHeight+8), MPFROMSHORT(BKA_MAJORTAB));
  725.   WinSendMsg(hwndNotebook, BKM_SETDIMENSIONS,
  726.       MPFROM2SHORT(0, 0), MPFROMSHORT(BKA_MINORTAB));
  727.  
  728.   /*
  729.    * Set the container sort function.
  730.    *
  731.    * The container sort function assures that each record
  732.    * is sorted when inserted into the container.  Setting the
  733.    * pSortRecord in the CNRINFO structure will sort the container
  734.    * as records are inserted until CM_SETCNRINFO is called again
  735.    * with CMA_PSORTRECORD and pSortRecord == NULL.
  736.    *
  737.    * CM_SORTRECORD, on the other hand, is a one-shot sort that 
  738.    * doesn't affect subsequent record insertions.  CM_FILTER works
  739.    * the same way as CM_SORTRECORD, ie, it must be called after 
  740.    * record insertions to re-filter the records.
  741.    */
  742.  
  743.   cnrInfo.cb = sizeof(cnrInfo);
  744.   cnrInfo.pSortRecord = (PVOID) ShrSortAddressBookRecord;
  745.   WinSendMsg(hwndCnr, CM_SETCNRINFO, MPFROMP(&cnrInfo), 
  746.       MPFROMLONG(CMA_PSORTRECORD));
  747.  
  748. #ifdef ADDRESSBOOKINDETAILSVIEW
  749.   cnrInfo.flWindowAttr = CV_DETAIL;
  750.   WinSendMsg(hwndCnr, CM_SETCNRINFO, MPFROMP(&cnrInfo), 
  751.       MPFROMLONG(CMA_FLWINDOWATTR));
  752.  
  753.   /*
  754.    * Setup details columns.
  755.    */
  756.  
  757.   pClassFieldInfo = NULL;
  758.  
  759.   cColumns = _wpclsQueryDetailsInfo(ShrFindPersonClass(),
  760.       &pClassFieldInfo, 0);
  761.  
  762.   pFirstFI = WinSendMsg(hwndCnr,
  763.       CM_ALLOCDETAILFIELDINFO, MPFROMSHORT(cColumns), NULL);
  764.  
  765.   if (pFirstFI)
  766.   {
  767.     offTotal = sizeof(MINIRECORDCORE);
  768.  
  769.     for (i=0, pFI=pFirstFI, pCFI=pClassFieldInfo;
  770.          i < cColumns;
  771.          i++, pCFI=pCFI->pNextFieldInfo, pFI=pFI->pNextFieldInfo)
  772.     {
  773.       pFI->cb = sizeof(FIELDINFO);
  774.  
  775.       /*
  776.        * Remove the separators between columns
  777.        */
  778.  
  779.       pFI->flData = pCFI->flData & ~CFA_SEPARATOR;
  780.  
  781.       /* There is a special case for the first 2 columns.  The first
  782.        * 2 columns consist of an Icon and the title.  We special case
  783.        * them so that they point to the same Icon and title used in
  784.        * the other views...ie, make it point to the MINIRECORDCORE data
  785.        * and not point to the details data because we don't want to
  786.        * waste space.
  787.        */
  788.  
  789.       if (i <= 1)
  790.       {
  791.         pFI->offStruct = pCFI->offFieldData;
  792.       }
  793.       else
  794.       {
  795.         pFI->offStruct = offTotal;
  796.         offTotal += pCFI->ulLenFieldData;
  797.       }
  798.     }
  799.   }
  800.  
  801.   fieldInfoInsert.cb = sizeof(FIELDINFOINSERT);
  802.   fieldInfoInsert.pFieldInfoOrder = (PFIELDINFO) CMA_FIRST;
  803.   fieldInfoInsert.cFieldInfoInsert = cColumns;
  804.   fieldInfoInsert.fInvalidateFieldInfo = TRUE;
  805.   WinSendMsg(hwndCnr, CM_INSERTDETAILFIELDINFO,
  806.       MPFROMP(pFirstFI), MPFROMP(&fieldInfoInsert));
  807. #endif
  808.  
  809.   /*
  810.    * Size and position the address book view and force an
  811.    * update of the screen so the user gets the feeling something
  812.    * is happening.
  813.    *
  814.    * Note we're saving only one set of fonts, colors, size,
  815.    * etc with WinStoreWindowPos for all the indexed views
  816.    * of address books, so we need to call
  817.    * WinQueryTaskSizePos to get a new suggestion position.
  818.    * We could save a separate window position key for each
  819.    * instance (like WPS does) using wpQueryHandle to create
  820.    * a unique key, but that wastes space in the INI file
  821.    * (and this is just an example, afterall).
  822.    */
  823.  
  824.   WinQueryTaskSizePos(NULLHANDLE, 0, &swp);
  825.   swp.hwnd = hwndFrame;
  826.   swp.hwndInsertBehind = HWND_TOP;
  827.   swp.fl |= (SWP_ZORDER | SWP_ACTIVATE | SWP_SHOW);
  828.  
  829.   if (WinRestoreWindowPos(vszAppName,
  830.       vszIndexedViewWindowPosKeyName, hwndFrame))
  831.     swp.fl &= ~SWP_SIZE;
  832.  
  833.   WinSetMultWindowPos(NULLHANDLE, &swp, 1);
  834.   WinUpdateWindow(hwndFrame);
  835.  
  836.   /*
  837.    * It would be better if wpPopulate was called from another
  838.    * thread so we don't hold up PM while the address book (folder)
  839.    * is filled.  
  840.    *
  841.    * But all the person objects are WPAbstract subclasses
  842.    * which can be created very quickly, and we don't expect a lot
  843.    * of them (famous last words).
  844.    */
  845.  
  846.   WinSetPointer(HWND_DESKTOP,
  847.      WinQuerySysPointer(HWND_DESKTOP, SPTR_WAIT, FALSE));
  848.  
  849.   _wpPopulate(somSelf, NULLHANDLE, NULL, FALSE);
  850.  
  851.   WinSetPointer(HWND_DESKTOP,
  852.      WinQuerySysPointer(HWND_DESKTOP, SPTR_ARROW, FALSE));
  853.  
  854.   /*
  855.    * Fill the address book view container with the person objects.
  856.    * Request its semaphore to avoid objects being added/removed while 
  857.    * in the loop below.
  858.    */
  859.  
  860.   bHoldingSem = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT);
  861.  
  862.   if (bHoldingSem)
  863.   {
  864.     somObject = _wpQueryContent(somSelf, NULL, QC_First);
  865.     while (somObject)
  866.     {
  867.       ShrCnrInsertObject(somObject, hwndCnr);
  868.  
  869.       /*
  870.        * Each call to wpPopulate increments the object's
  871.        * lock count. The locking count is used to determine 
  872.        * when an object is ready for garbage collection.  
  873.        *                                                               
  874.        * If you're going to access an object from a second thread, 
  875.        * use wpRequestObjectMutexSem and wpReleaseObjectMutexSem.  
  876.        * The wpRequestObjectMutexSem method will will prevent 
  877.        * other threads from modifying your object's instance data 
  878.        * while you do (of course, this assumes new methods you add 
  879.        * that query/set instance data use wpRequestObjectMutexSem 
  880.        * and wpReleaseObjectMutexSem, too).  
  881.        *                                                               
  882.        * WPS is extremely multithreaded.  The 
  883.        * wpRequest/ReleaseObjectMutexSem methods are very 
  884.        * important.  It is wise to request/release the object 
  885.        * mutex semaphore in each method that queries/sets object
  886.        * instance data.  In addition, the method should include 
  887.        * exception protection should the method trap to make 
  888.        * certain it releases the object's semaphore.  
  889.        */
  890.  
  891.       _wpUnlockObject(somObject);
  892.   
  893.       somObject = _wpQueryContent(somSelf, somObject, QC_Next);
  894.     }
  895.  
  896.     bHoldingSem = _wpReleaseObjectMutexSem(somSelf);
  897.   }
  898.  
  899.   /*
  900.    * Now that the address book view container is filled, update the
  901.    * top page.  We put off setting the notebook owner till now to
  902.    * prevent the notebook from sending a page selection notification
  903.    * when we added the first page.
  904.    */
  905.  
  906.   WinSetOwner(hwndNotebook, hwndFrame);
  907.   ShrAddressBookUpdateTopPage(somSelf, hwndNotebook, hwndCnr);
  908.  
  909.   /*
  910.    * Add interest in the address book so the open view will be
  911.    * notified if something of interest occurs to it (eg, a person
  912.    * is added to the address book).
  913.    *
  914.    * Interest is removed when the frame is destroyed.
  915.    */
  916.  
  917.   _shrAddInterestedWindow(_shrQueryNotifier(somSelf), hwndFrame);
  918.  
  919.   ShrEndTryBlock;
  920.   return hwndFrame;
  921.  
  922.   /*
  923.    * Exception handling code for this function goes here.
  924.    */
  925.  
  926. OnException:
  927.   if (bHoldingSem)
  928.     bHoldingSem = _wpReleaseObjectMutexSem(somSelf);
  929.  
  930.   if (pViewData)
  931.     _wpFreeMem(somSelf, (PBYTE) pViewData);
  932.  
  933.   if (hwndFrame)
  934.   {
  935.     /*
  936.      * Set the view instance data pointer to NULL since
  937.      * it is tested in WM_DESTROY.
  938.      */
  939.  
  940.     setAddressBookViewDataFromFrame(hwndFrame, NULL);
  941.     WinDestroyWindow(hwndFrame);
  942.   }
  943.  
  944.   ShrEndTryBlock;
  945.   return NULLHANDLE;
  946. }
  947.