![]() |
Programming the Container Control - Part 3/3Written by Larry Salomon, Jr. |
Back at the BatcaveLast month we continued our disection of the container control and how to use it. The tree view was added to our list of conquests, and we started developing a sample application which we will continue to use. This month, we will add more meat to the bones of our skeleton by learning about the detail view and direct editing, among other things. Detail ViewBack in the first installment of this series, the detail view was described in the following manner. Each object is represented as a detailed description of the object. The information displayed is defined by the application. While I realize that did not say much, it served to illustrate that the detail view is the most flexible of the views, in terms of what can be displayed. It should be logical then to assume that this means yet more setup on the part of the application. What is the Detail View?To be precise, the detail view is a matrix view of the contents of a container, where each row in the matrix is a separate object and each column is an attribute (called a field) of every object to be displayed. Since the objects are already added using the CM_ALLOCRECORD/CM_INSERTRECORD messages, the columns must be added; this is done using the CM_ALLOCDETAILFIELDINFO/CM_INSERTDETAILFIELDINFO messages. As with its record-oriented counterpart, the CM_ALLOCDETAILFIELDINFO accepts the number of fields to allocate memory for and returns a pointer to the first FIELDINFO structure in the linked-list. typedef struct _FIELDINFO { ULONG cb; ULONG flData; ULONG flTitle; PVOID pTitleData; ULONG offStruct; PVOID pUserData; struct _FIELDINFO *pNextFieldInfo; ULONG cxWidth; } FIELDINFO, *PFIELDINFO;Figure 1: The FIELDINFO structure.
typedef struct _MYCNRREC { MINIRECORDCORE mrcCore; CHAR achText[256]; ULONG ulNumSold; float fGrossIncome; float fNetIncome; float fTotalCost; float fNetProfit; CHAR achProdName[256]; PCHAR pchProdName; } MYCNRREC, *PMYCNRREC;we would specify FIELDOFFSET(MYCNRREC,pchProdName) instead of FIELDOFFSET(MYCNRREC,achProdName). The reason for this will be clear when we discuss direct editing.
Data type
Alignment
Miscellaneous
Miscellaneous
What's Next?Once you have initialized all of the FIELDINFO structures, you can "insert" them into the container using the CM_INSERTDETAILFIELDINFO message. Again using the parallel of the CM_INSERTRECORD message, it expects a pointer to the first FIELDINFO structure as well as a pointer to a FIELDINFOINSERT structure. typedef struct _FIELDINFOINSERT { ULONG cb; PFIELDINFO pFieldInfoOrder; ULONG fInvalidateFieldInfo; ULONG cFieldInfoInsert; } FIELDINFOINSERT, *PFIELDINFOINSERT;Figure 2: The FIELDINFOINSERT structure.
CNRINFO ciInfo; ciInfo.flWindowAttr=CV_DETAIL; WinSendMsg(hwndCnr, CM_SETCNRINFO, MPFROMP(&ciInfo), MPFROMLONG(CMA_FLWINDOWATTR));Figure 3: Changing to the detail view. Note that, even if you initialize the pTitleData field of the FIELDINFO structure to point to the column heading, the titles are not displayed unless you specify CA_DETAILSVIEWTITLES in the flWindowAttr field. Direct EditingDirect editing is accomplished by pressing the proper combination of keys and/or mouse buttons as defined in the "Mappings" page of the "Mouse" settings (in the "OS/2 System"/"System Setup" folder) while the mouse is over a directly-editable region. When this is done, a multi-line edit control appears and is initialized with the current text, in which you can make your changes; the enter key acts as a newline, while the pad enter key completes the editing operation and (normally) applies the changes. From a programming perspective, three notifications are sent to the application whenever direct-editing is requested by the user when over a non-read-only field ("field" is used here to mean any text string and not as it was defined in the discussion of the detail view) - CN_BEGINEDIT, CN_REALLOCPSZ, and CN_ENDEDIT (in that order). For all three, mpParm2 points to a CNREDITDATA structure which describes the state of the record being edited. The purpose of CN_BEGINEDIT and CN_ENDEDIT is to notify the user that editing is about to begin/end. However, only the CN_REALLOCPSZ is important, since the former two can be ignored while the latter cannot. typedef struct _CNREDITDATA { ULONG cb; HWND hwndCnr; PRECORDCORE pRecord; PFIELDINFO pFieldInfo; PSZ *ppszText; ULONG cbText; ULONG id; } CNREDITDATA;Figure 4: The CNREDITDATA structure.
Altered StatesAs defined by CUA '91 (I think), an object in general can be in one or more of five states (or none at all) - source, target, in-use, cursored, and selected. A container record stores information on its current state in the flRecordAttr (in both the RECORDCORE and MINIRECORDCORE structures) in the form of CRA_* constants. Setting the state, however, is not a simple matter of setting this field, since the container will have no way of knowing that you've changed the field. Instead, you send the container a CM_SETRECORDEMPHASIS message which updates this field in the record and updates the display of that record on the screen. Those who are "on the ball" will notice that there is no CRA_SOURCE constant defined in the 2.0 Toolkit. This was inadvertently left out and should be defined to be 0x00004000L in pmstddlg.h. So what do all of these states mean?
Popup MenusIf you take a close look at the Workplace Shell, you will see all of these states used in one way or another. A more interesting use is in conjunction with popup menus; if the record underneath the mouse is not selected, it alone is given source emphasis. If it is selected, all records that are selected are given source emphasis. If no record is underneath the mouse, the container itself is given source emphasis. After the appropriate record states have been changed, WinPopupMenu() is called. Finally, the WM_MENUEND message is intercepted to "un-source" the records that were changed. Broken down into pseudo-code, this becomes:
Virtual CoordinatesOkay, okay, everyone has probably heard of and is vaguely familiar with virtual coordinates, or else you would not be in PM programming to begin with. The container's notion of the origin in its coordinate system is somewhat awry, unfortunately, and this confuses things; the origin is defined to be the screen coordinate of the lower left corner of the container at the time the last CM_ARRANGE message was sent. So, you either have to keep track of when you send the container a CM_ARRANGE message and perform all sorts of hocus pocus to remember where the origin is supposed to be, or you can finish reading this sentence and discover that the documentation for CM_QUERYRECORDFROMRECT is flat-out wrong. The rectangle specified in this message is in window coordinates. Whew! That greatly simplifies things, except that when in detail view the record returned is the one above the one the mouse is over. Oh boy. Fortunately, we can calculate the height of a record using the CM_QUERYRECORDRECT message, which we use to adjust the mouse position before calling CM_QUERYRECORDFROMRECT. Now that we have the record underneath the mouse, we can check its selection state by examining the flRecordAttr field. If the record is selected, it is probably more efficient to use the CM_QUERYRECORDEMPHASIS message to get all selected records, but we already have this exquisite recursive search function, so I used that instead. Another example of poor documentation is in CM_SETRECORDEMPHASIS where it does not tell you that you can set the container's source emphasis by specifying NULL for the record. Finally, we call WinPopupMenu() and undo the source emphasis and - voila! - we're done. CNR3 - A Sample Application RevisitedCNR3 builds upon CNR2 by adding detail view support, direct editing support, and "proper" popup menu support. As with part II, landmarks have been added to CNR3.C which are to point out things of interest. These landmarks are described below. Landmark 1This is to point out the additions of the last four fields to the MYCNREDIT structure and the addition of the pmcrMenu field to the CLIENTDATA structure. Landmark 2This points out the allocation, initialization, and insertion of the FIELDINFO structures. Landmark 3This points out the new procedure for handling the context menu. Landmark 4This points out the correction for the bug in the CM_QUERYRECORDFROMRECT message when in details view as described above. Landmark 5This points out the processing of the CN_REALLOCPSZ notification. Landmark 6This points out the addition of the detail view menu item. SummaryThis month we learned a lot of things, namely how to setup the details view, how direct editing is performed and what the container expects from the application with regards to this, and how selection states are set, queried, and used. We also saw how inadequate the documentation is when it contains so many examples of incorrect or incomplete information. Now you have enough information to use the container well. However, we're not done yet; next month, I will try to figure out some of the more advanced capabilities of the container such as record sharing and deltas. Stay tuned, same Bat-time, same Bat-channel! |