home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
CNRSAMP.ZIP
/
CNRADV.ZIP
/
CNRADV.C
next >
Wrap
C/C++ Source or Header
|
1992-12-15
|
96KB
|
2,624 lines
/* ===================================================================*/
/* Advanced Container Sample program for ColoradOS/2 */
/* ------------------------------------------------------------------ */
/* */
/* Functional Description: */
/* */
/* This program is a follow on to the CNRBAS sample. It is the */
/* CNRBAS sample with many added features. The features added */
/* to this sample include: */
/* */
/* - direct editing - scroll to record */
/* - context menus - scroll to column */
/* - processing CN_ENTER - select/deselect all */
/* - IN_USE emphasis - sorting */
/* - drag/drop - removing records */
/* - record sharing - data validation */
/* */
/* This sample does not intend to cover all aspects of container */
/* programming, but shows how to do some of the more common and */
/* complex aspects prevalent in a container application. */
/* */
/* This sample was written and tested using OS/2 2.0 with the */
/* OS/2 Service Pack. Although most functions in this sample */
/* will run on GA, some do require at least the Service Pack. */
/* */
/* For: Programming the OS/2 Container Control - ColoradOS/2, */
/* January, 11-15 1993, Colorado Springs, Colorado. */
/* */
/* Compiled using: IBM C Set/2 1.0 */
/* */
/* By: Peter P. Brightbill */
/* Peter F. Haggar */
/* */
/* Copyright (C) 1992, IBM Corporation */
/* ===================================================================*/
#define INCL_WIN
#define INCL_DOS
#define INCL_WINWINDOWMGR
#define INCL_WINSYS
#define INCL_WINPOINTERS
#define INCL_WINSTDCNR
#define INCL_WINSTDDRAG
#define INCL_WINMENUS
#define INCL_WINMESSAGEMGR
#define INCL_DOSMODULEMGR
#define INCL_WININPUT
#define INCL_GPILCIDS
#include <os2.h>
#include <stdlib.h>
#include <cnradv.h>
void main(void)
{
HAB hab;
HMQ hmq;
QMSG qmsg;
HWND hwndFrame;
HWND hwndClient;
ULONG fcf = FCF_TITLEBAR | FCF_SIZEBORDER | FCF_SYSMENU |
FCF_ICON | FCF_MINMAX | FCF_SHELLPOSITION;
hab = WinInitialize (0);
hmq = WinCreateMsgQueue (hab, 0);
/* Register the Container sample window class. Also reserve 4 bytes
* of memory to store a pointer to our control block.
*/
WinRegisterClass (hab, "Container Sample",
CnrSampleWndProc, 0, 4);
/* Create a standard frame window. */
hwndFrame = WinCreateStdWindow (HWND_DESKTOP, /* Handle of desktop */
WS_VISIBLE, /* Window style */
&fcf, /* Creation flags */
"Container Sample", /* Client Class name */
"Container Sample", /* Title Bar text */
0, /* client wnd style */
NULLHANDLE, /* hwnd of resources */
ICON_RES_ID, /* ID of Resources */
&hwndClient); /* handle of Client */
while (WinGetMsg (hab, &qmsg, NULLHANDLE, 0, 0))
{
WinDispatchMsg (hab, &qmsg);
}
WinDestroyWindow (hwndFrame);
WinDestroyMsgQueue (hmq);
WinTerminate (hab);
}
/*----------------------------------------------------------------------
Function Name: CnrSampleWndProc
Description:
This is the window procedure for the client are of our window. It is
also the owner of the container control window.
Parameters:
(HWND) hwnd - The handle of the client window.
(ULONG) msg - The message to be processed.
(MRESULT) mp1 - The first message parameter for the message.
(MRESULT) mp2 - The second message parameter for the message.
----------------------------------------------------------------------*/
MRESULT EXPENTRY CnrSampleWndProc (HWND hwnd, ULONG msg,
MRESULT mp1, MRESULT mp2)
{
SWP swp;
HPS hps;
RECTL rect;
POINTL MousePt;
ULONG ulBorder;
CNRINFO CnrInfo;
PSAMPLEINFO pSampleInfo;
switch (msg)
{
case WM_CREATE:
/* Store the hwnd of this client in a global. We will use
* it if and when we create an additional view and share
* some records from this view.
*/
vhwndMainClient = hwnd;
/* Create the container window. If it creates successfully,
* return FALSE, otherwise return TRUE to indicate an error.
*/
if (CreateCnr (hwnd))
{
return ((MRESULT)FALSE);
}
else
{
return ((MRESULT)TRUE);
}
case WM_PAINT:
hps = WinBeginPaint (hwnd, NULLHANDLE, &rect);
WinFillRect (hps, &rect, CLR_BLUE);
WinEndPaint (hps);
break;
case WM_SIZE:
/* Add a border around the container by sizing the container
* to cover most of the client area. In the area left
* uncovered, fill it with some color. (We used BLUE)
* General note: When setting the position of only 1 window, for
* performance reasons use WinSetMultWindowPos instead of
* WinSetWindowPos. The reason for this is because when
* setting the position of 1 window with WinSetWindowPos,
* inside of PMWIN, WinSetWindowPos calls WinSetMultWindowPos
* with a value of 1. Therefore, coding as we do below shortens
* the code path as well as execution time. This will not
* make or break your application, but is a good tip...also
* good as a trivia question at your next office party.
*/
ulBorder = WinQuerySysValue (HWND_DESKTOP, SV_CXSIZEBORDER);
swp.fl = SWP_MOVE | SWP_SIZE;
swp.x = swp.y = ulBorder;
swp.cx = (SHORT1FROMMP(mp2) - (2 * ulBorder));
swp.cy = (SHORT2FROMMP(mp2) - (2 * ulBorder));
swp.hwndInsertBehind = NULLHANDLE;
swp.hwnd = WinWindowFromID (hwnd, CNR_SAMPLE_ID);
WinSetMultWindowPos (WinQueryAnchorBlock (hwnd), &swp, 1);
break;
case WM_DESTROY:
/* When the user double clicks on the system menu of our frame
* window, or chooses the QUIT option from our menu, call a
* function to free up the resources utilized by this application.
*/
CleanupCnr (hwnd);
break;
case WM_CONTROL:
switch (SHORT2FROMMP(mp1))
{
/* The use requested a context (popup) menu. Get the
* position of the mouse and put up the menu at that
* location.
*/
case CN_CONTEXTMENU:
WinQueryMsgPos (WinQueryAnchorBlock (hwnd), &MousePt);
ProcessContextMenu (hwnd, MousePt.x,
MousePt.y, (PPERSONRECORD)mp2);
break;
case CN_REALLOCPSZ:
return ((MRESULT)ProcessDirectEdit (hwnd, (PCNREDITDATA)mp2));
case CN_ENTER:
ProcessEnter (hwnd,(PNOTIFYRECORDENTER)mp2);
break;
case CN_INITDRAG:
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
ProcessInitDrag( hwnd, pSampleInfo->hwndCnr,
((PCNRDRAGINIT) (PVOIDFROMMP(mp2))) );
break;
case CN_DROP:
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
ProcessDrop( hwnd, pSampleInfo->hwndCnr,
(PCNRDRAGINFO)(PVOIDFROMMP(mp2)) );
break;
case CN_DRAGAFTER:
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
/* Set this flag to indicate that CN_DRAGAFTER was last
* received. This information is needed when using
* CA_MIXEDTARGETEMPHASIS and the CN_DROP notification
* is received. See ProcessDrop below.
*/
pSampleInfo->bDragAfter = TRUE;
return(MRFROM2SHORT(DOR_DROP, DO_MOVE));
case CN_DRAGOVER:
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
/* Set this flag to indicate that CN_DRAGOVER was last
* received. This information is needed when using
* CA_MIXEDTARGETEMPHASIS and the CN_DROP notification
* is received. See ProcessDrop below.
*/
pSampleInfo->bDragAfter = FALSE;
return(MRFROM2SHORT(DOR_DROP, DO_MOVE));
default:
return (WinDefWindowProc (hwnd, msg, mp1, mp2));
}
break;
case WM_COMMAND:
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
/* The following messages are received when the user chooses
* one of the menu options on our action bar.
*/
switch (SHORT1FROMMP(mp1))
{
case TEXTV_ID:
/* Switch the container to Text view. */
CnrInfo.flWindowAttr = CV_TEXT | CA_ORDEREDTARGETEMPH;
WinSendMsg (pSampleInfo->hwndCnr, CM_SETCNRINFO,
MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR));
break;
case TEXTV_FLOWED_ID:
/* Switch the container to Flowed Text view. */
CnrInfo.flWindowAttr = CV_TEXT | CV_FLOW |
CA_ORDEREDTARGETEMPH;
WinSendMsg (pSampleInfo->hwndCnr, CM_SETCNRINFO,
MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR));
break;
case NAMEV_ID:
/* Switch the container to Name view with a container
* title.
*/
CnrInfo.flWindowAttr = CV_NAME | CA_CONTAINERTITLE |
CA_TITLESEPARATOR | CA_TITLEREADONLY |
CA_MIXEDTARGETEMPH;
CnrInfo.pszCnrTitle = pSampleInfo->pszCnrTitle;
WinSendMsg (pSampleInfo->hwndCnr, CM_SETCNRINFO,
MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR |
CMA_CNRTITLE));
break;
case NAMEV_FLOWED_ID:
/* Switch the container to Flowed Name view with a
* container title.
*/
CnrInfo.flWindowAttr = CV_NAME | CV_FLOW | CA_MIXEDTARGETEMPH;
WinSendMsg (pSampleInfo->hwndCnr, CM_SETCNRINFO,
MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR));
break;
case ICONV_ID:
/* Set the container to Icon view with a container title,
* then arrange it so the records display orderly.
*/
CnrInfo.flWindowAttr = CV_ICON | CA_CONTAINERTITLE |
CA_TITLESEPARATOR | CA_TITLEREADONLY;
CnrInfo.pszCnrTitle = pSampleInfo->pszCnrTitle;
WinSendMsg (pSampleInfo->hwndCnr, CM_SETCNRINFO,
MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR |
CMA_CNRTITLE));
WinSendMsg (pSampleInfo->hwndCnr, CM_ARRANGE, NULL, NULL);
break;
case TREEV_ID:
/* Switch the container to Tree view. If this is the
* first time the user has requested Tree view, populate
* it first. Also, add the container title to Tree view.
*/
if (!pSampleInfo->bTreePopulated)
{
if (PopulateTree (hwnd))
{
pSampleInfo->bTreePopulated = TRUE;
}
else
{
return ((MRESULT)NULL);
}
}
CnrInfo.flWindowAttr = CV_TREE | CV_ICON | CA_TREELINE |
CA_CONTAINERTITLE | CA_TITLESEPARATOR |
CA_TITLEREADONLY;
CnrInfo.pszCnrTitle = pSampleInfo->pszCnrTitle;
WinSendMsg (pSampleInfo->hwndCnr, CM_SETCNRINFO,
MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR |
CMA_CNRTITLE));
break;
case DETAILSV_ID:
/* Switch the container to Details view. Set the
* splitbar in the middle of the container window.
*/
WinQueryWindowRect (pSampleInfo->hwndCnr, &rect);
CnrInfo.flWindowAttr = CV_DETAIL | CA_DETAILSVIEWTITLES |
CA_MIXEDTARGETEMPH;
CnrInfo.xVertSplitbar = rect.xRight / 2;
CnrInfo.pFieldInfoLast = pSampleInfo->pFieldInfoLast;
WinSendMsg (pSampleInfo->hwndCnr, CM_SETCNRINFO,
MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR |
CMA_XVERTSPLITBAR | CMA_PFIELDINFOLAST));
break;
case SORT_BY_NAME:
case SORT_BY_BIRTHDAY:
if (SHORT1FROMMP(mp1) == SORT_BY_NAME)
{
WinSendMsg (pSampleInfo->hwndCnr, CM_SORTRECORD,
MPFROMP(pfnCompareName), MPFROMP(NULL) );
}
else
{
WinSendMsg (pSampleInfo->hwndCnr, CM_SORTRECORD,
MPFROMP(pfnCompareBDay), MPFROMP(NULL) );
}
WinSendMsg (pSampleInfo->hwndCnr, CM_ARRANGE, NULL, NULL);
break;
case SCROLL_TO_CURSORED_REC:
{
PMINIRECORDCORE pCursoredRec;
pCursoredRec = (PMINIRECORDCORE)WinSendMsg(pSampleInfo->hwndCnr,
CM_QUERYRECORDEMPHASIS,
MPFROMP((PRECORDCORE)CMA_FIRST),
MPFROMLONG(CRA_CURSORED) );
ScrollToRecord(pSampleInfo->hwndCnr, (PRECORDCORE)pCursoredRec);
}
break;
case SCROLL_TO_TOB_COLUMN:
if (WinSendMsg (pSampleInfo->hwndCnr, CM_QUERYCNRINFO,
MPFROMP(&CnrInfo), MPFROMSHORT(sizeof(CNRINFO))))
{
if (CnrInfo.flWindowAttr & CV_DETAIL)
{
ScrollToColumn (pSampleInfo->hwndCnr,
pSampleInfo->pScrollColumn);
}
}
break;
/* This code will select all the records in the container by
* sending the key event of <CTRL>[/] (Control, forward slash)
* to the container. This method is much faster than
* querying all records and setting their selection emphasis
* via CM_SETRECORDEMPHASIS.
*/
case SELECT_ALL:
{
BYTE KeyStateTable[256];
WinSetKeyboardStateTable( HWND_DESKTOP,
(PBYTE)&KeyStateTable[0],
FALSE );
KeyStateTable[VK_CTRL] |= 0x80;
WinSetKeyboardStateTable( HWND_DESKTOP,
(PBYTE)(&KeyStateTable[0]),
TRUE );
WinSendMsg(pSampleInfo->hwndCnr,
WM_CHAR,
MPFROMP(NULL),
MPFROM2SHORT((SHORT)0X002f, (SHORT)0X0000) );
KeyStateTable[VK_CTRL] &= !(0x80);
WinSetKeyboardStateTable( HWND_DESKTOP,
(PBYTE)(&KeyStateTable[0]),
TRUE );
}
break;
/* This code will deselect all the records in the container by
* sending the key event of <CTRL>[\] (Control, backslash)
* to the container. This method is much faster than
* querying all records and removing their selection emphasis
* via CM_SETRECORDEMPHASIS.
*/
case DESELECT_ALL:
{
BYTE KeyStateTable[256];
WinSetKeyboardStateTable( HWND_DESKTOP,
(PBYTE)&KeyStateTable[0],
FALSE );
KeyStateTable[VK_CTRL] |= 0x80;
WinSetKeyboardStateTable( HWND_DESKTOP,
(PBYTE)(&KeyStateTable[0]),
TRUE );
WinSendMsg(pSampleInfo->hwndCnr,
WM_CHAR,
MPFROMP(NULL),
MPFROM2SHORT((SHORT)0X005C, (SHORT)0X0000) );
KeyStateTable[VK_CTRL] &= !(0x80);
WinSetKeyboardStateTable( HWND_DESKTOP,
(PBYTE)(&KeyStateTable[0]),
TRUE );
}
break;
case SAMPLE_MENU_QUIT:
/* The user has requested to quit the application. Post
* a WM_QUIT to ourselves and bail.
*/
WinPostMsg (hwnd, WM_QUIT, 0, 0);
break;
case REMOVE_RECORDS:
RemoveRecords (hwnd);
break;
default:
return (WinDefWindowProc (hwnd, msg, mp1, mp2));
}
break;
default:
return (WinDefWindowProc (hwnd, msg, mp1, mp2));
}
return (0);
}
/*----------------------------------------------------------------------
Function Name: CreateCnr
Description:
This function creates a container window, then populates the
container with 5 records.
Parameters:
(HWND) hwnd - The handle of the client window that we are creating
the container in.
----------------------------------------------------------------------*/
BOOL CreateCnr (HWND hwnd)
{
HWND hwndCnr;
PSAMPLEINFO pSampleInfo;
CNRINFO CnrInfo;
BOOL rc = TRUE;
/* Create the container window. */
hwndCnr = WinCreateWindow (hwnd, /* Parent */
WC_CONTAINER, /* Class */
NULL, /* Window text */
WS_VISIBLE | CCS_EXTENDSEL | /* Window style */
CCS_MINIRECORDCORE,
0,0, /* x,y */
0,0, /* cx, cy */
hwnd, /* Owner */
HWND_TOP, /* placement */
CNR_SAMPLE_ID, /* Window ID */
NULL, /* Control data */
NULL); /* presparams */
/* If the window is created successfully, allocate some memory
* for our control block and initialize it to 0. Then, set the
* information in it and store a pointer to this structure in the
* window words we reserved on the WinRegisterClass call.
*/
if (hwndCnr)
{
pSampleInfo = malloc (sizeof(SAMPLEINFO));
if (pSampleInfo)
{
memset (pSampleInfo, 0, sizeof(SAMPLEINFO));
pSampleInfo->hwndCnr = hwndCnr;
WinSetWindowPtr (hwnd, QWL_USER, pSampleInfo);
/* Give the container a title and a horizontal separator to
* separate the title from the viewport. Store a pointer to
* the container title in our control block since we will
* need it whenever the user switches to a view that we want
* to display the title in.
*/
pSampleInfo->pszCnrTitle = malloc(TEXT_SIZE);
if (pSampleInfo->pszCnrTitle)
{
strcpy (pSampleInfo->pszCnrTitle,
"Advanced Container\r\nSample Program");
CnrInfo.pszCnrTitle = pSampleInfo->pszCnrTitle;
CnrInfo.flWindowAttr = CV_ICON | CA_CONTAINERTITLE |
CA_TITLESEPARATOR | CA_TITLEREADONLY;
WinSendMsg (hwndCnr,
CM_SETCNRINFO,
MPFROMP(&CnrInfo),
MPFROMLONG(CMA_CNRTITLE | CMA_FLWINDOWATTR));
/* Populate the container with 5 records. This function will
* also set up the details view information for each record.
*/
if (!PopulateCnr (hwnd))
{
rc = FALSE;
}
}
else
{
rc = FALSE; /* Out of memory error */
}
}
else
{
rc = FALSE; /* Out of memory error */
}
}
else
{
rc = FALSE; /* Creation error */
}
if (!rc)
{
if (hwndCnr)
{
WinDestroyWindow (hwndCnr);
}
if (pSampleInfo)
{
if (pSampleInfo->pszCnrTitle)
{
free (pSampleInfo->pszCnrTitle);
}
free (pSampleInfo);
}
}
else
{
/* Give the container the focus and return. */
WinSetFocus (HWND_DESKTOP, hwndCnr);
}
return (rc);
}
/*----------------------------------------------------------------------
Function Name: PopulateCnr
Description:
This function allocates 5 container records, assigns the appropriate
information, including the information needed for details view and
finally inserts the 5 records into the container.
Parameters:
(HWND) hwnd - The handle of the client window.
----------------------------------------------------------------------*/
BOOL PopulateCnr (HWND hwnd)
{
PSAMPLEINFO pSampleInfo;
PPERSONRECORD pPersonRec;
PPERSONRECORD pPersonRecFirst;
HPOINTER hptrPersonIcon;
USHORT i;
RECORDINSERT RecordInsert;
BOOL rc = TRUE;
ULONG ulNumRecords = 5;
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
/* Load the person icon that will be used by the container in views
* which show icons. These icons are used multiple times, but
* be sure to only load them once and reuse the HPOINTER.
*/
pSampleInfo->hptrPersonIcon = WinLoadPointer (HWND_DESKTOP,
NULLHANDLE,
ID_PERSON_ICON);
/* Load the job icon that will be used for the child records
* in Tree View.
*/
pSampleInfo->hptrJobIcon = WinLoadPointer (HWND_DESKTOP,
NULLHANDLE,
ID_JOB_ICON);
/* Allocate and insert the fieldinfo structures. */
if (!SetupAndAddFieldInfos (hwnd))
{
return (FALSE);
}
/* Allocate a linked list of records. Note how MP1 is calculated.
* It is the number of ADDITIONAL bytes that you want the container
* to allocate per record. Since PERSONRECORD contains a
* MINIRECORDCORE, we subtract it out. Now if you ever change the
* size of PERSONRECORD by adding or subtracting any extra fields,
* this code will not have to change.
*/
pPersonRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
CM_ALLOCRECORD,
MPFROMLONG(sizeof(PERSONRECORD) -
sizeof(MINIRECORDCORE)),
MPFROMLONG(ulNumRecords));
/* Save a pointer to the front of the list of records returned. This
* will be used when we insert the records at the bottom of this
* function.
*/
pPersonRecFirst = pPersonRec;
/* Loop through all the records assigning the necessary
* information.
*/
i = 1;
while ((pPersonRec) && (rc))
{
pPersonRec->MiniRec.hptrIcon = pSampleInfo->hptrPersonIcon;
pPersonRec->MiniRec.pszIcon = malloc (TEXT_SIZE);
/* If we successfully allocated the memory, copy the text string
* to the pszIcon field and go to the next record in the linked
* list.
*/
if (pPersonRec->MiniRec.pszIcon)
{
/* Set up the record specific data for each record, including
* the details view data.
*/
switch (i)
{
case 1:
strcpy (pPersonRec->MiniRec.pszIcon, "Peter Haggar");
strcpy (pPersonRec->szMiddleInit, "F");
pPersonRec->pszMiddleInit = pPersonRec->szMiddleInit;
pPersonRec->DateOfBirth.month = 2;
pPersonRec->DateOfBirth.day = 3;
pPersonRec->DateOfBirth.year = 65;
pPersonRec->TimeOfBirth.hours = 3;
pPersonRec->TimeOfBirth.minutes = 30;
pPersonRec->TimeOfBirth.seconds = 0;
pPersonRec->CurrentAge = 27;
pPersonRec->usJob = JR_DEVELOPMENT;
break;
case 2:
strcpy (pPersonRec->MiniRec.pszIcon, "Peter Brightbill");
strcpy (pPersonRec->szMiddleInit, "P");
pPersonRec->pszMiddleInit = pPersonRec->szMiddleInit;
pPersonRec->DateOfBirth.month = 1;
pPersonRec->DateOfBirth.day = 23;
pPersonRec->DateOfBirth.year = 65;
pPersonRec->TimeOfBirth.hours = 17;
pPersonRec->TimeOfBirth.minutes = 10;
pPersonRec->TimeOfBirth.seconds = 47;
pPersonRec->CurrentAge = 27;
pPersonRec->usJob = JR_DEVELOPMENT;
break;
case 3:
strcpy (pPersonRec->MiniRec.pszIcon, "Tai Nam");
strcpy (pPersonRec->szMiddleInit, "W");
pPersonRec->pszMiddleInit = pPersonRec->szMiddleInit;
pPersonRec->DateOfBirth.month = 2;
pPersonRec->DateOfBirth.day = 3;
pPersonRec->DateOfBirth.year = 60;
pPersonRec->TimeOfBirth.hours = 10;
pPersonRec->TimeOfBirth.minutes = 56;
pPersonRec->TimeOfBirth.seconds = 19;
pPersonRec->CurrentAge = 32;
pPersonRec->usJob = JR_DEVELOPMENT;
break;
case 4:
strcpy (pPersonRec->MiniRec.pszIcon, "Joe Shmo");
strcpy (pPersonRec->szMiddleInit, "D");
pPersonRec->pszMiddleInit = pPersonRec->szMiddleInit;
pPersonRec->DateOfBirth.month = 10;
pPersonRec->DateOfBirth.day = 29;
pPersonRec->DateOfBirth.year = 70;
pPersonRec->TimeOfBirth.hours = 23;
pPersonRec->TimeOfBirth.minutes = 5;
pPersonRec->TimeOfBirth.seconds = 36;
pPersonRec->CurrentAge = 22;
pPersonRec->usJob = JR_SUPPORT;
break;
case 5:
strcpy (pPersonRec->MiniRec.pszIcon, "John Public");
strcpy (pPersonRec->szMiddleInit, "Q");
pPersonRec->pszMiddleInit = pPersonRec->szMiddleInit;
pPersonRec->DateOfBirth.month = 5;
pPersonRec->DateOfBirth.day = 17;
pPersonRec->DateOfBirth.year = 61;
pPersonRec->TimeOfBirth.hours = 19;
pPersonRec->TimeOfBirth.minutes = 3;
pPersonRec->TimeOfBirth.seconds = 9;
pPersonRec->CurrentAge = 31;
pPersonRec->usJob = JR_SUPPORT;
break;
}
pPersonRec = (PPERSONRECORD)pPersonRec->MiniRec.preccNextRecord;
i++;
}
else
{
rc = FALSE;
}
}
/* Now that we have the data assigned, insert the records in the
* container. First set up the RECORDINSERT structure which tells
* the container how to insert the records.
*/
RecordInsert.cb = sizeof(RECORDINSERT);
RecordInsert.pRecordOrder = (PRECORDCORE)CMA_FIRST;
RecordInsert.pRecordParent = NULL;
RecordInsert.zOrder = CMA_TOP;
RecordInsert.cRecordsInsert = ulNumRecords;
RecordInsert.fInvalidateRecord = TRUE;
/* Since the container will be coming up in Icon view, after inserting
* the records, send the CM_ARRANGE message to arrange the icons in
* the container viewport.
*/
if (WinSendMsg (pSampleInfo->hwndCnr,
CM_INSERTRECORD,
MPFROMP(pPersonRecFirst),
MPFROMP(&RecordInsert)))
{
WinSendMsg (pSampleInfo->hwndCnr, CM_ARRANGE, NULL, NULL);
}
else
{
rc = FALSE;
}
return (rc);
}
/*----------------------------------------------------------------------
Function Name: SetupAndAddFieldInfos
Description:
This function allocates, sets up, and inserts the 6 FieldInfo
structures that are used to describe each column for the Details
view.
Parameters:
(HWND) hwnd - The handle of the client window.
----------------------------------------------------------------------*/
BOOL SetupAndAddFieldInfos (HWND hwnd)
{
PSAMPLEINFO pSampleInfo;
PFIELDINFO pFieldInfo;
PFIELDINFO pFieldInfoFirst;
FIELDINFOINSERT FieldInfoInsert;
BOOL rc = TRUE;
USHORT usNumFieldInfo = 6;
USHORT i = 1;
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
/* Allocate the 6 FieldInfo structures used for the columns in
* Details view.
*/
pFieldInfo = WinSendMsg (pSampleInfo->hwndCnr,
CM_ALLOCDETAILFIELDINFO,
MPFROMSHORT(usNumFieldInfo),
NULL);
pFieldInfoFirst = pFieldInfo;
/* Loop through each FieldInfo and assign the data as necessary.
* Pay particular attention to the offStruct field. This tells
* the container at what offset from the beginning of the
* RECORDCORE/MINIRECORDCORE to find the data that is to be
* displayed in the column.
*/
while ((pFieldInfo) && (rc))
{
switch (i)
{
case 1:
pFieldInfo->flTitle = CFA_BITMAPORICON;
pFieldInfo->pTitleData = (PVOID)pSampleInfo->hptrPersonIcon;
pFieldInfo->flData = CFA_BITMAPORICON | CFA_HORZSEPARATOR |
CFA_SEPARATOR;
pFieldInfo->offStruct = FIELDOFFSET(PERSONRECORD,
MiniRec.hptrIcon);
break;
case 2:
pSampleInfo->pFieldInfoLast = pFieldInfo;
pFieldInfo->flTitle = CFA_STRING;
pFieldInfo->pTitleData = malloc(TEXT_SIZE);
if (pFieldInfo->pTitleData)
{
strcpy(pFieldInfo->pTitleData, "Name");
pFieldInfo->flData = CFA_STRING | CFA_HORZSEPARATOR;
pFieldInfo->offStruct = FIELDOFFSET(PERSONRECORD,
MiniRec.pszIcon);
}
else
{
rc = FALSE;
}
break;
case 3:
pFieldInfo->flTitle = CFA_STRING;
pFieldInfo->pTitleData = malloc(TEXT_SIZE);
if (pFieldInfo->pTitleData)
{
strcpy(pFieldInfo->pTitleData, "Middle Initial");
pFieldInfo->flData = CFA_STRING | CFA_CENTER |
CFA_HORZSEPARATOR;
pFieldInfo->offStruct = FIELDOFFSET(PERSONRECORD,
pszMiddleInit);
}
else
{
rc = FALSE;
}
break;
case 4:
pFieldInfo->flTitle = CFA_STRING;
pFieldInfo->pTitleData = malloc(TEXT_SIZE);
if (pFieldInfo->pTitleData)
{
strcpy(pFieldInfo->pTitleData, "Date of Birth");
pFieldInfo->flData = CFA_DATE | CFA_RIGHT | CFA_HORZSEPARATOR;
pFieldInfo->offStruct = FIELDOFFSET(PERSONRECORD,
DateOfBirth);
}
else
{
rc = FALSE;
}
break;
case 5:
pSampleInfo->pScrollColumn = pFieldInfo;
pFieldInfo->flTitle = CFA_STRING;
pFieldInfo->pTitleData = malloc(TEXT_SIZE);
if (pFieldInfo->pTitleData)
{
strcpy(pFieldInfo->pTitleData, "Time of Birth");
pFieldInfo->flData = CFA_TIME | CFA_RIGHT | CFA_HORZSEPARATOR;
pFieldInfo->offStruct = FIELDOFFSET(PERSONRECORD,
TimeOfBirth);
}
else
{
rc = FALSE;
}
break;
case 6:
pFieldInfo->flTitle = CFA_STRING | CFA_CENTER;
pFieldInfo->pTitleData = malloc(TEXT_SIZE);
if (pFieldInfo->pTitleData)
{
strcpy(pFieldInfo->pTitleData, "Current\r\nAge");
pFieldInfo->flData = CFA_ULONG | CFA_RIGHT |
CFA_HORZSEPARATOR;
pFieldInfo->offStruct = FIELDOFFSET(PERSONRECORD,
CurrentAge);
}
else
{
rc = FALSE;
}
break;
}
pFieldInfo = pFieldInfo->pNextFieldInfo;
i++;
}
if (rc)
{
FieldInfoInsert.cb = sizeof(FIELDINFOINSERT);
FieldInfoInsert.pFieldInfoOrder = (PFIELDINFO)CMA_FIRST;
FieldInfoInsert.cFieldInfoInsert = usNumFieldInfo;
FieldInfoInsert.fInvalidateFieldInfo = FALSE;
WinSendMsg (pSampleInfo->hwndCnr, CM_INSERTDETAILFIELDINFO,
MPFROMP(pFieldInfoFirst), MPFROMP(&FieldInfoInsert));
}
return (rc);
}
/*----------------------------------------------------------------------
Function Name: PopulateTree
Description:
This functions adds 3 child records to each of the records that are
currently in the container.
Parameters:
(HWND) hwnd - The handle of the client window.
----------------------------------------------------------------------*/
BOOL PopulateTree (HWND hwnd)
{
PSAMPLEINFO pSampleInfo;
PPERSONRECORD pPersonRec;
BOOL rc = TRUE;
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
/* Get the first root item in the tree. Add its children, then
* continue adding the children of the next root level item.
*/
pPersonRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
CM_QUERYRECORD,
NULL,
MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
while ((pPersonRec) && (rc))
{
if (AddChildren (hwnd, pPersonRec))
{
pPersonRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
CM_QUERYRECORD,
MPFROMP(pPersonRec),
MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
}
else
{
rc = FALSE;
}
}
return (rc);
}
/*----------------------------------------------------------------------
Function Name: AddChildren
Description:
This function allocates the record memory for each child record and
inserts them as children of the parent record passed in as the
second parameter.
Parameters:
(HWND) hwnd - The handle of the client window.
(PPERSONRECORD) pParentRec - The parent record for the children
records we are creating.
----------------------------------------------------------------------*/
BOOL AddChildren (HWND hwnd, PPERSONRECORD pParentRec)
{
PSAMPLEINFO pSampleInfo;
PPERSONRECORD pChildRec;
PPERSONRECORD pChildRecFirst;
RECORDINSERT RecordInsert;
BOOL rc = TRUE;
ULONG ulNumChildren = 3;
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
/* Allocate the 3 records which will be used as children. */
pChildRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
CM_ALLOCRECORD,
MPFROMLONG(sizeof(PERSONRECORD) -
sizeof(MINIRECORDCORE)),
MPFROMLONG(ulNumChildren));
pChildRecFirst = pChildRec;
/* Go through each record and assign the data as necessary. */
pChildRec->MiniRec.hptrIcon = pSampleInfo->hptrJobIcon;
pChildRec->MiniRec.pszIcon = malloc(TEXT_SIZE);
if (pChildRec->MiniRec.pszIcon)
{
if (pParentRec->usJob == JR_DEVELOPMENT)
{
strcpy (pChildRec->MiniRec.pszIcon, "Design");
}
else
{
strcpy (pChildRec->MiniRec.pszIcon, "Planning");
}
}
else
{
rc = FALSE;
}
if (rc)
{
pChildRec = (PPERSONRECORD)pChildRec->MiniRec.preccNextRecord;
pChildRec->MiniRec.hptrIcon = pSampleInfo->hptrJobIcon;
pChildRec->MiniRec.pszIcon = malloc(TEXT_SIZE);
if (pChildRec->MiniRec.pszIcon)
{
if (pParentRec->usJob == JR_DEVELOPMENT)
{
strcpy (pChildRec->MiniRec.pszIcon, "Coding");
}
else
{
strcpy (pChildRec->MiniRec.pszIcon, "Function\r\nTest");
}
}
else
{
rc = FALSE;
}
}
if (rc)
{
pChildRec = (PPERSONRECORD)pChildRec->MiniRec.preccNextRecord;
pChildRec->MiniRec.hptrIcon = pSampleInfo->hptrJobIcon;
pChildRec->MiniRec.pszIcon = malloc(TEXT_SIZE);
if (pChildRec->MiniRec.pszIcon)
{
/* \r\n or \n will work to create a multi line entry. */
if (pParentRec->usJob == JR_DEVELOPMENT)
{
strcpy (pChildRec->MiniRec.pszIcon, "Unit\nTest");
}
else
{
strcpy (pChildRec->MiniRec.pszIcon, "Documentation");
}
}
else
{
rc = FALSE;
}
}
/* If no errors, set up the RECORDINSERT structure and insert the
* child records. Notice that we set the pRecordParent field of
* the RECORDINSERT structure to the parent record passed in.
*/
if (rc)
{
RecordInsert.cb = sizeof(RECORDINSERT);
RecordInsert.pRecordOrder = (PRECORDCORE)CMA_FIRST;
RecordInsert.pRecordParent = (PRECORDCORE)pParentRec;
RecordInsert.zOrder = CMA_TOP;
RecordInsert.cRecordsInsert = ulNumChildren;
RecordInsert.fInvalidateRecord = TRUE;
WinSendMsg (pSampleInfo->hwndCnr,
CM_INSERTRECORD,
MPFROMP(pChildRecFirst),
MPFROMP(&RecordInsert));
}
return (rc);
}
/*----------------------------------------------------------------------
Function Name: CleanupCnr
Description:
This function is called when this application is closed and will
free all the memory and resources used by this application.
Parameters:
(HWND) hwnd - The handle of the client window.
Notes:
It is not necessary to remove and free all of the container records
when the application is closed. The container, as part of its
WM_DESTROY processing, will do this for you.
----------------------------------------------------------------------*/
VOID CleanupCnr (HWND hwnd)
{
PSAMPLEINFO pSampleInfo;
PPERSONRECORD pPersonRec;
PFIELDINFO pFieldInfo;
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
/* Make sure it is still valid */
if (pSampleInfo)
{
/* Zip through all the records and free the memory we previously
* allocated for the text string.
*/
pPersonRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
CM_QUERYRECORD,
NULL,
MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
while (pPersonRec)
{
if (pPersonRec->MiniRec.pszIcon)
{
free (pPersonRec->MiniRec.pszIcon);
}
/* For each record, we need to also clean up the memory
* used for its child records.
*/
CleanupChildren (hwnd, pPersonRec);
/* Get the next record and continue. */
pPersonRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
CM_QUERYRECORD,
MPFROMP(pPersonRec),
MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
}
/* Zip through all the fieldinfo's and free the memory we previously
* allocated for the title text strings.
*/
pFieldInfo = WinSendMsg (pSampleInfo->hwndCnr,
CM_QUERYDETAILFIELDINFO,
NULL,
MPFROMSHORT(CMA_FIRST));
while (pFieldInfo)
{
/* If the title data for this column is a string, free the
* string memory.
*/
if (!(pFieldInfo->flTitle & CFA_BITMAPORICON))
{
if (pFieldInfo->pTitleData)
{
free (pFieldInfo->pTitleData);
}
}
/* Go to the next fieldinfo in the container. */
pFieldInfo = WinSendMsg (pSampleInfo->hwndCnr,
CM_QUERYDETAILFIELDINFO,
MPFROMP(pFieldInfo),
MPFROMSHORT(CMA_NEXT));
}
/* Free the icons we used and menus we used. */
WinDestroyPointer (pSampleInfo->hptrPersonIcon);
WinDestroyPointer (pSampleInfo->hptrJobIcon);
WinDestroyWindow (pSampleInfo->hwndWindowMenu);
WinDestroyWindow (pSampleInfo->hwndRecordMenu);
/* Free the container title text string memory we stored in our
* control block.
*/
if (pSampleInfo->pszCnrTitle)
{
free (pSampleInfo->pszCnrTitle);
}
/* Finally, free the SAMPLEINFO control block. */
free (pSampleInfo);
}
return;
}
/*----------------------------------------------------------------------
Function Name: CleanupChildren
Description:
This function will free the resources of the child records used in
the Tree view.
Parameters:
(HWND) hwnd - The handle of the client window.
(PPERSONRECORD) pParentRec - Pointer to the parent record whose
child records we will be processing.
----------------------------------------------------------------------*/
VOID CleanupChildren (HWND hwnd, PPERSONRECORD pParentRec)
{
PSAMPLEINFO pSampleInfo;
PPERSONRECORD pChildRec;
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
/* Get the first child record for the parent record passed in. */
pChildRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
CM_QUERYRECORD,
MPFROMP(pParentRec),
MPFROM2SHORT(CMA_FIRSTCHILD, CMA_ITEMORDER));
/* Go through each child record, freeing the memory for the text
* string in each.
*/
while (pChildRec)
{
if (pChildRec->MiniRec.pszIcon)
{
free (pChildRec->MiniRec.pszIcon);
}
pChildRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
CM_QUERYRECORD,
MPFROMP(pChildRec),
MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
}
return;
}
/*----------------------------------------------------------------------
Function Name: ProcessContextMenu
Description:
This will do the necessary processing to bring up a context
menu in the container window. If the record clicked on is selected,
a list is built of all the currently selected records in the
container window. This is done because when a context menu is
requested on a selected record, the action chosen applies to all
selected records.
Parameters:
(HWND) hwnd - The handle of the client window.
(LONG) xPos - x position of the mouse.
(LONG) yPos - y position of the mouse.
(PPERSONRECORD pPersonRec - pointer to the record the user clicked
on. If they clicked on white space,
this value is NULL.
----------------------------------------------------------------------*/
VOID ProcessContextMenu (HWND hwnd, LONG xPos, LONG yPos,
PPERSONRECORD pPersonRec)
{
PSAMPLEINFO pSampleInfo;
PVOID pOffset;
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
/* If the user clicked on a record, check to see if it is a
* selected record. If so, call GetSelectedRecords to build
* a linked list of all selected records. We will then be
* able to use this list later if and when the user selects
* an action on the menu.
* If the record is not selected, store it as a list of 1.
*/
if (pPersonRec)
{
if (pPersonRec->MiniRec.flRecordAttr & CRA_SELECTED)
{
pSampleInfo->pMenuRecord = GetSelectedRecords (hwnd);
}
else
{
pPersonRec->MiniRec.preccNextRecord = NULL;
pSampleInfo->pMenuRecord = pPersonRec;
}
}
else
{
/* This user clicked on white space....set our field to NULL. */
pSampleInfo->pMenuRecord = NULL;
}
/* If the user requested a popup menu whilst over a record, load
* the record context menu. Otherwise, they clicked on white
* space so load the window menu.
*/
if (pPersonRec)
{
if (!pSampleInfo->hwndRecordMenu)
{
DosGetResource (NULLHANDLE, RT_MENU, RECORD_POPUP_MENU, &pOffset);
pSampleInfo->hwndRecordMenu = WinCreateMenu (HWND_DESKTOP,
pOffset);
DosFreeResource (pOffset);
}
}
else if (!pSampleInfo->hwndWindowMenu)
{
DosGetResource (NULLHANDLE, RT_MENU, WIN_POPUP_MENU, &pOffset);
pSampleInfo->hwndWindowMenu = WinCreateMenu (HWND_DESKTOP, pOffset);
DosFreeResource (pOffset);
}
WinPopupMenu (HWND_DESKTOP, hwnd,
pPersonRec ? pSampleInfo->hwndRecordMenu :
pSampleInfo->hwndWindowMenu,
xPos, yPos, 0, PU_HCONSTRAIN | PU_VCONSTRAIN |
PU_KEYBOARD | PU_MOUSEBUTTON1 | PU_MOUSEBUTTON2 |
PU_MOUSEBUTTON3);
return;
}
/*----------------------------------------------------------------------
Function Name: ProcessDirectEdit
Description:
This function handles the direct editing processing resulting
from the CN_REALLOCPSZ notification. This function will also
perform data validation if the user is directly editing the
Middle Initial column in details view.
Parameters:
(HWND) hwnd - The handle of the client window.
(PCNREDITDATA) pCnrEditData - Pointer to the CNREDITDATA structure
which was passed back from the
container on the CN_REALLOCPSZ message.
----------------------------------------------------------------------*/
BOOL ProcessDirectEdit (HWND hwnd, PCNREDITDATA pCnrEditData)
{
CHAR szNewText[2];
/* Check to see if the user is directly editing the Middle Initial
* column of the details view. If so, we will need to verify
* that the data that is entered is valid.
*/
if (pCnrEditData->pFieldInfo)
{
if (pCnrEditData->pFieldInfo->offStruct ==
FIELDOFFSET (PERSONRECORD, pszMiddleInit))
{
/* Since we are processing the middle initial field, first
* check to be sure it is only 2 characters. The 2
* characters represent the letter and the NULL terminator.
* NOTE: This 2 character business will not cut it with
* double-byte languages.
*/
if (pCnrEditData->cbText == 2)
{
/* Get the new text the user entered out of the MLE. */
WinQueryWindowText (WinWindowFromID (pCnrEditData->hwndCnr,
CID_MLE),
pCnrEditData->cbText, (PSZ)szNewText);
/* Since we now have the new text, verify that it is valid.
* We will only accept a string that is 2 characters long
* and contains alphabetic characters. If it is ok, return
* TRUE to inform the container to accept the direct edit
* operation and display the new data the user entered.
*/
if (isalpha(szNewText[0]))
{
return (TRUE);
}
else
{
/* Data entered was not alphabetic. Return FALSE to inform
* the container to cancel the direct edit operation.
*/
return (FALSE);
}
}
else
{
/* Data is not 2 characters in length. Return FALSE to
* inform the container to cancel the direct edit operation.
*/
return (FALSE);
}
}
}
/* If we get to here, the data being edited was not from a details
* view column, or it was not the Middle initial column. Process
* the new text string by freeing memory occupying the old one and
* allocating a new memory block for the new text string. If we
* successfully allocate the memory, return TRUE to inform the
* container to update itself to display the new text string.
*/
free (*(pCnrEditData->ppszText));
*(pCnrEditData->ppszText) = malloc(pCnrEditData->cbText);
if (*(pCnrEditData->ppszText))
{
return (TRUE);
}
else
{
return (FALSE);
}
}
/*----------------------------------------------------------------------
Function Name: ProcessEnter
Description:
This function handles the case when a record is double-clicked
on with the mouse, or if the enter key is pressed when a record
is selected. If this happens when the container is in Tree View,
and the record selected is a parent record, we will open an
additional container view and insert the child records of the
record clicked on. This will demonstrate the record sharing
feature of the container.
Parameters:
(HWND) hwnd - The handle of the client window.
(PNOTIFYRECORDENTER) pRecordEnter - Structure passed back from the
container.
----------------------------------------------------------------------*/
VOID ProcessEnter (HWND hwnd, PNOTIFYRECORDENTER pRecordEnter)
{
PSAMPLEINFO pSampleInfo;
CNRINFO CnrInfo;
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
/* Query the CNRINFO structure used by the container to check to
* see what view the container is currently in. We will only
* process this if we are in the TREE-ICON view.
*/
if (WinSendMsg (pRecordEnter->hwndCnr, CM_QUERYCNRINFO,
MPFROMP(&CnrInfo), MPFROMSHORT(sizeof(CNRINFO))))
{
if ((CnrInfo.flWindowAttr & (CV_TREE)) &&
(CnrInfo.flWindowAttr & (CV_ICON)))
{
/* Check to see if the record that was clicked on is a root
* level record.
*/
if (!WinSendMsg (pRecordEnter->hwndCnr, CM_QUERYRECORD,
MPFROMP(pRecordEnter->pRecord),
MPFROM2SHORT(CMA_PARENT, CMA_ITEMORDER)))
{
/* Save the record that was clicked on in our control block. */
pSampleInfo->pRecordOpen = (PPERSONRECORD)pRecordEnter->pRecord;
/* Increment the use count for this record. */
pSampleInfo->pRecordOpen->usUseCount++;
/* Give the record that was clicked on IN-USE emphasis. */
WinSendMsg (pRecordEnter->hwndCnr, CM_SETRECORDEMPHASIS,
MPFROMP(pRecordEnter->pRecord),
MPFROM2SHORT(TRUE, CRA_INUSE));
if (!CreateAdditionalView (hwnd))
{
; /* report error */
}
}
}
}
return;
}
/*----------------------------------------------------------------------
Function Name: CreateAdditionalView
Description:
This function creates an additional frame and client window that
will be used to hold the records that are being shared from the
main container window.
Parameters:
(HWND) hwnd - The handle of the client window.
----------------------------------------------------------------------*/
BOOL CreateAdditionalView (HWND hwnd)
{
HWND hwndFrame;
HWND hwndClient;
BOOL rc = TRUE;
SWP swp;
ULONG fcf = FCF_TITLEBAR | FCF_SIZEBORDER | FCF_SYSMENU |
FCF_ICON | FCF_MINMAX;
/* Register the Additional Container window class. */
WinRegisterClass (WinQueryAnchorBlock(hwnd), "Additional View",
AdditionalCnrWndProc, 0, 0);
hwndFrame = WinCreateStdWindow (HWND_DESKTOP, /* Handle of desktop */
WS_VISIBLE, /* Window style */
&fcf, /* Creation flags */
"Additional View", /* Client Class name */
"Additional View", /* Title Bar text */
0, /* client wnd style */
NULLHANDLE, /* hwnd of resources */
ICON_RES_ID, /* ID of Resources */
&hwndClient); /* handle of Client */
if (hwndFrame)
{
swp.fl = SWP_MOVE | SWP_SIZE;
swp.x = (WinQuerySysValue (HWND_DESKTOP, SV_CXSCREEN) / 2);
swp.y = (WinQuerySysValue (HWND_DESKTOP, SV_CYSCREEN) / 2);
swp.cx = (swp.x / 2);
swp.cy = (swp.y / 2);
swp.hwndInsertBehind = NULLHANDLE;
swp.hwnd = hwndFrame;
WinSetMultWindowPos (WinQueryAnchorBlock(hwnd), &swp, 1);
}
else
{
rc = FALSE;
}
return (rc);
}
/*----------------------------------------------------------------------
Function Name: AdditionalCnrWndProc
Description:
This is the window procedure for the additional container window
that is used to hold the records that are shared from the
main container window.
Parameters:
(HWND) hwnd - The handle of the additional client window.
(ULONG) msg - The message to be processed.
(MRESULT) mp1 - The first message parameter for the message.
(MRESULT) mp2 - The second message parameter for the message.
----------------------------------------------------------------------*/
MRESULT EXPENTRY AdditionalCnrWndProc (HWND hwnd, ULONG msg,
MRESULT mp1, MRESULT mp2)
{
RECTL rect;
HPS hps;
SWP swp;
HWND hwndMainCnr;
HWND hwndAdditionalCnr;
PPERSONRECORD pInUseRec;
switch (msg)
{
case WM_CREATE:
if (SetupAdditionalCnr (hwnd))
{
return ((MRESULT)FALSE);
}
else
{
return ((MRESULT)TRUE);
}
case WM_PAINT:
hps = WinBeginPaint (hwnd, NULLHANDLE, &rect);
WinFillRect (hps, &rect, SYSCLR_BACKGROUND);
WinEndPaint (hps);
break;
case WM_SIZE:
swp.fl = SWP_MOVE | SWP_SIZE;
swp.x = swp.y = 0;
swp.cx = SHORT1FROMMP(mp2);
swp.cy = SHORT2FROMMP(mp2);
swp.hwndInsertBehind = NULLHANDLE;
swp.hwnd = WinWindowFromID (hwnd, CNR_ADDITIONAL_ID);
WinSetMultWindowPos (WinQueryAnchorBlock (hwnd), &swp, 1);
break;
case WM_CLOSE:
/* Remove IN_USE emphasis from the source record in the main
* container window.
*/
hwndMainCnr = WinWindowFromID (vhwndMainClient, CNR_SAMPLE_ID);
hwndAdditionalCnr = WinWindowFromID (hwnd, CNR_ADDITIONAL_ID);
pInUseRec = FindInUseRecord (hwndAdditionalCnr, hwndMainCnr);
/* Decrement the use count of this record since we have closed
* one of its views. If the use count is 0, meaning all of
* its views are closed, remove IN_USE emphasis.
*/
pInUseRec->usUseCount--;
if (!pInUseRec->usUseCount)
{
WinSendMsg (hwndMainCnr, CM_SETRECORDEMPHASIS,
MPFROMP(pInUseRec), MPFROM2SHORT(FALSE, CRA_INUSE));
}
/* Destroy this additional frame window without taking down
* our entire application.
*/
WinDestroyWindow (WinQueryWindow (hwnd, QW_PARENT));
return ((MRESULT)FALSE);
default:
return (WinDefWindowProc (hwnd, msg, mp1, mp2));
}
return (0);
}
/*----------------------------------------------------------------------
Function Name: SetupAdditionalCnr
Description:
This function is called as a result of receiving a WM_CREATE
message to our additional client window. This function will
create a second container window and share the child records
of the record that was selected in our main container window.
Parameters:
(HWND) hwnd - The handle of the additional client window.
----------------------------------------------------------------------*/
BOOL SetupAdditionalCnr (HWND hwnd)
{
PSAMPLEINFO pSampleInfo;
PPERSONRECORD pChildRec;
RECORDINSERT RecordInsert;
HWND hwndAdditionalCnr;
CNRINFO CnrInfo;
BOOL rc = TRUE;
PPERSONRECORD pChildRecFirst = NULL;
PPERSONRECORD pChildRecCur = NULL;
ULONG ulNumRecords = 0;
hwndAdditionalCnr = WinCreateWindow (hwnd, /* Parent */
WC_CONTAINER, /* Class */
NULL, /* Window text */
WS_VISIBLE | CCS_EXTENDSEL | /* Window style */
CCS_MINIRECORDCORE | CCS_READONLY,
0,0, /* x,y */
0,0, /* cx, cy */
hwnd, /* Owner */
HWND_TOP, /* placement */
CNR_ADDITIONAL_ID, /* Window ID */
NULL, /* Control data */
NULL); /* presparams */
if (hwndAdditionalCnr)
{
/* Get a pointer to the main client window's control block where
* the pointer to its container window is stored.
*/
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (vhwndMainClient,
QWL_USER);
/* Set up the container title for the additional view. We will
* use the icon name.
*/
CnrInfo.pszCnrTitle = pSampleInfo->pRecordOpen->MiniRec.pszIcon;
CnrInfo.flWindowAttr = CV_ICON | CA_CONTAINERTITLE |
CA_TITLESEPARATOR | CA_TITLELEFT;
WinSendMsg (hwndAdditionalCnr, CM_SETCNRINFO,
MPFROMP(&CnrInfo), MPFROMLONG(CMA_FLWINDOWATTR |
CMA_CNRTITLE));
/* Query the first child from the main container window. Then
* loop through and build a linked list of all the children for
* this specific parent record.
*/
pChildRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
CM_QUERYRECORD,
MPFROMP(pSampleInfo->pRecordOpen),
MPFROM2SHORT(CMA_FIRSTCHILD, CMA_ITEMORDER));
while (pChildRec)
{
ulNumRecords++;
if (pChildRecFirst)
{
pChildRecCur->MiniRec.preccNextRecord=(PMINIRECORDCORE)pChildRec;
}
else
{
pChildRecFirst = pChildRec;
}
pChildRecCur = pChildRec;
pChildRecCur->MiniRec.preccNextRecord = NULL;
pChildRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
CM_QUERYRECORD,
MPFROMP(pChildRec),
MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
}
/* Now insert the linked list we just built into the secondary
* container window we just created. Notice we are not reallocating
* the record memory, but reusing the existing records in another
* container window...ie record sharing.
*/
RecordInsert.cb = sizeof(RECORDINSERT);
RecordInsert.pRecordOrder = (PRECORDCORE)CMA_FIRST;
RecordInsert.pRecordParent = NULL;
RecordInsert.zOrder = CMA_TOP;
RecordInsert.cRecordsInsert = ulNumRecords;
RecordInsert.fInvalidateRecord = TRUE;
if (WinSendMsg (hwndAdditionalCnr, CM_INSERTRECORD,
MPFROMP(pChildRecFirst), MPFROMP(&RecordInsert)))
{
WinSendMsg (hwndAdditionalCnr, CM_ARRANGE, NULL, NULL);
}
else
{
rc = FALSE;
WinDestroyWindow (hwndAdditionalCnr);
}
}
else
{
rc = FALSE;
}
/* Give the new container the focus. */
WinSetFocus (HWND_DESKTOP, hwndAdditionalCnr);
return (rc);
}
/*----------------------------------------------------------------------
Function Name: pfnCompareName
Description:
This is the comparison function called by the container when a sort
by name is requested. This function simply compares the text
strings of the two records given.
Parameters:
(PRECORDCORE)preccFirst - The first of two records to compare.
(PRECORDCORE)preccSecond - The second of two records to compare.
(PVOID)pStorage - Application defined pointer(NULL).
----------------------------------------------------------------------*/
SHORT EXPENTRY pfnCompareName (PRECORDCORE preccFirst,
PRECORDCORE preccSecond, PVOID pStorage )
{
return (strcmp(preccFirst->pszIcon, preccSecond->pszIcon));
}
/*----------------------------------------------------------------------
Function Name: pfnCompareBDay
Description:
This is the comparison function called by the container when a sort
by birthday is requested. This function simply compares the
DateOfBirth fields of each PERSONRECORD to determine which is older.
It first looks at the year of birth, then the month and day if
necessary.
Parameters:
(PRECORDCORE)preccFirst - The first of two records to compare.
(PRECORDCORE)preccSecond - The second of two records to compare.
(PVOID)pStorage - Application defined pointer(NULL).
----------------------------------------------------------------------*/
SHORT EXPENTRY pfnCompareBDay (PRECORDCORE preccFirst,
PRECORDCORE preccSecond, PVOID pStorage)
{
if ( ((PPERSONRECORD)preccFirst)->DateOfBirth.year !=
((PPERSONRECORD)preccSecond)->DateOfBirth.year )
return( ((PPERSONRECORD)preccFirst)->DateOfBirth.year -
((PPERSONRECORD)preccSecond)->DateOfBirth.year );
else if ( ((PPERSONRECORD)preccFirst)->DateOfBirth.month !=
((PPERSONRECORD)preccSecond)->DateOfBirth.month )
return( ((PPERSONRECORD)preccFirst)->DateOfBirth.month -
((PPERSONRECORD)preccSecond)->DateOfBirth.month );
else if ( ((PPERSONRECORD)preccFirst)->DateOfBirth.day !=
((PPERSONRECORD)preccSecond)->DateOfBirth.day )
return( ((PPERSONRECORD)preccFirst)->DateOfBirth.day -
((PPERSONRECORD)preccSecond)->DateOfBirth.day );
else
return ( 0 );
}
/*----------------------------------------------------------------------
Function Name: ScrollToRecord
Description:
This function is used to bring a particular record in the container
to the top-left corner of the current viewport. It will always
scroll the record into view, however it may be impossible to scroll
it to the top-left corner. This may happen when a record is
positioned at the extreme right edge of the container's workspace.
Parameters:
(HWND)hwndCnr - The container window handle.
(PRECORDCORE)pRecord - The record to be scrolled to.
----------------------------------------------------------------------*/
VOID ScrollToRecord(HWND hwndCnr, PRECORDCORE pRecord)
{
RECTL rclViewport;
RECTL rclItem;
QUERYRECORDRECT QueryRecordRect;
LONG lMargin = 4L;
CNRINFO CnrInfo;
WinSendMsg (hwndCnr,
CM_QUERYCNRINFO,
MPFROMP(&CnrInfo),
MPFROMSHORT(sizeof(CNRINFO)) );
/* Query the container for the position of the target record relative
* to the viewport. We want the rectangle containing both the icon
* and the text of the record.
*/
QueryRecordRect.cb = sizeof(QUERYRECORDRECT);
QueryRecordRect.pRecord = pRecord;
QueryRecordRect.fRightSplitWindow = FALSE;
if (CnrInfo.flWindowAttr & CV_TEXT)
QueryRecordRect.fsExtent = CMA_TEXT;
else
QueryRecordRect.fsExtent = CMA_ICON | CMA_TEXT;
WinSendMsg (hwndCnr,
CM_QUERYRECORDRECT,
MPFROMP(&rclItem),
MPFROMP(&QueryRecordRect) );
/* Query the container for the size of the current viewport.
* This is necessary because the position of the record
* is relative to the bottom left corner of the viewport.
*/
WinSendMsg (hwndCnr,
CM_QUERYVIEWPORTRECT,
MPFROMP(&rclViewport),
MPFROM2SHORT(CMA_WINDOW, FALSE) );
/* If in details view, the viewport must be adjusted for the
* horizontal scroll bar.
* NOTE: This if statement is only needed when running OS/2 2.0
* GA or the Service Pack. This if statement should be
* removed for future versions of OS/2.
*/
if (CnrInfo.flWindowAttr & CV_DETAIL)
{
LONG lcyScrollBar;
lcyScrollBar = WinQuerySysValue(HWND_DESKTOP, SV_CYHSCROLL);
rclViewport.yBottom += lcyScrollBar;
rclViewport.yTop += lcyScrollBar;
}
/* Scroll the container vertically to bring the record to a position
* just below the top of the viewport.
*/
WinSendMsg (hwndCnr,
CM_SCROLLWINDOW,
MPFROMSHORT(CMA_VERTICAL),
MPFROMLONG(rclViewport.yTop - rclItem.yTop - lMargin) );
/* Scroll the container horizontally to bring the record to a
* position just to the right of the left edge of the viewport.
*/
WinSendMsg (hwndCnr,
CM_SCROLLWINDOW,
MPFROMSHORT(CMA_HORIZONTAL),
MPFROMLONG(rclItem.xLeft - lMargin) );
return;
}
/*----------------------------------------------------------------------
Function Name: ScrollToColumn
Description:
This function is used to bring a particular column in the container
details view to the left edge of the current viewport. It will
always scroll the column into view, however it may be impossible
to scroll it all the way to the left edge.
NOTE: This routine requires the Service Pack version.
There are workarounds in this function for the Service
Pack version of OS/2. For future releases the suggested
alternative should be used.
Parameters:
(HWND)hwndCnr - The container window handle.
(PFIELDINFO)pColumn - The column to be scrolled to.
----------------------------------------------------------------------*/
VOID ScrollToColumn(HWND hwndCnr, PFIELDINFO pColumn)
{
POINTL ColPos;
RECTL rclViewport;
BOOL LeftDVWnd;
BOOL SplitWnd;
LONG WindowOffset;
/* Query the Column position in workspace coordinates and also get
* information like if the window is split and which split window
* the column is in.
*/
QueryColumnPos(hwndCnr, pColumn, &ColPos, &LeftDVWnd, &SplitWnd);
/* Query the container for the viewports current workspace position
*/
if (LeftDVWnd)
WinSendMsg (hwndCnr,
CM_QUERYVIEWPORTRECT,
MPFROMP(&rclViewport),
MPFROM2SHORT(CMA_WORKSPACE, FALSE) );
else
{
/* For the service pack, the right window's workspace position must
* be adjusted by the offset from the container window origin.
* NOTE: For future releases of OS/2, this WindowOffset adjustment
* should be removed.
*/
WinSendMsg (hwndCnr,
CM_QUERYVIEWPORTRECT,
MPFROMP(&rclViewport),
MPFROM2SHORT(CMA_WINDOW, TRUE) );
WindowOffset = rclViewport.xLeft;
WinSendMsg (hwndCnr,
CM_QUERYVIEWPORTRECT,
MPFROMP(&rclViewport),
MPFROM2SHORT(CMA_WORKSPACE, TRUE) );
rclViewport.xLeft -= WindowOffset;
}
/* If the details view is split, then CM_HORZSCROLLSPLITWINDOW
* should be used, otherwise CM_SCROLLWINDOW should be used.
* We need to scroll the difference between the columns left
* side and the left side of the viewport.
* NOTE: For the service pack, use addition not subtraction.
* For future releases of OS/2 the commented lines
* should be used instead of addition.
*/
if (SplitWnd)
if (LeftDVWnd)
WinSendMsg (hwndCnr,
CM_HORZSCROLLSPLITWINDOW,
MPFROMSHORT(CMA_LEFT),
/* MPFROMLONG(ColPos.x - rclViewport.xLeft) ); */
MPFROMLONG(ColPos.x + rclViewport.xLeft) );
else
WinSendMsg (hwndCnr,
CM_HORZSCROLLSPLITWINDOW,
MPFROMSHORT(CMA_RIGHT),
/* MPFROMLONG(ColPos.x - rclViewport.xLeft) ); */
MPFROMLONG(ColPos.x + rclViewport.xLeft) );
else
WinSendMsg (hwndCnr,
CM_SCROLLWINDOW,
MPFROMSHORT(CMA_HORIZONTAL),
/* MPFROMLONG(ColPos.x - rclViewport.xLeft) ); */
MPFROMLONG(ColPos.x + rclViewport.xLeft) );
return;
}
/*----------------------------------------------------------------------
Function Name: QueryColumnPos
Description:
NOTE: This function utilizes the CMA_DATAWIDTH feature described
in the OS/2 service pack readme file. This function requires
the user to be running at least the service pack level of OS/2.
This function is used to calculate the left and right position
of the given column in workspace coordinates. Note that the
POINTL.x is used for the left side and that POINTL.y is used
for the columns right side.
Parameters:
(HWND)hwndCnr - The container window handle.
(PFIELDINFO)pColumn - The column to calculate position for.
The following parameters describe the return value from this function.
(PPOINTL)pPointL - pPointL.x will be the left side of
the column in workspace coord.
pPointL.y will be the right side of
the column in workspace coord.
(PBOOL)pLeftDVW - TRUE -> the column is in the left window
or the container is unsplit.
FALSE -> the column is in the right window
of a split container.
(PBOOL)pbSplit - TRUE-> the container details view is split
FALSE-> the container details view is
not split.
----------------------------------------------------------------------*/
void QueryColumnPos(HWND hwndCnr, PFIELDINFO pColumn,
PPOINTL pPointL, PBOOL pLeftDVW, PBOOL pbSplit)
{
CNRINFO CnrInfo;
FONTMETRICS FontMetrics;
HPS hps;
PFIELDINFO pFI, pLastVisibleFI;
LONG LeftSide=0, RightSide=0, LeftMargin, RightMargin;
WinSendMsg (hwndCnr,
CM_QUERYCNRINFO,
MPFROMP(&CnrInfo),
MPFROMSHORT(sizeof(CNRINFO)) );
/* Set default values for the LeftDVW and bSplit booleans and
* determine if it is actually different.
*/
*pLeftDVW = TRUE;
*pbSplit = FALSE;
if (CnrInfo.pFieldInfoLast)
{
*pbSplit = TRUE;
pFI = CnrInfo.pFieldInfoLast;
while ( pFI )
{
pFI = WinSendMsg (hwndCnr,
CM_QUERYDETAILFIELDINFO,
MPFROMP(pFI),
MPFROMSHORT(CMA_NEXT) );
if ( pFI == pColumn )
{
*pLeftDVW = FALSE;
pFI = NULL;
}
}
}
/* If the column is invisible, stop now.
*/
if (pColumn->flData & CFA_INVISIBLE)
return;
/* Query the font metrics so we can calculate the margins as
* described in the OS/2 service pack readme file.
*/
hps = WinGetPS( hwndCnr );
GpiQueryFontMetrics( hps, sizeof(FONTMETRICS), &FontMetrics);
WinReleasePS( hps );
LeftMargin = (3 * FontMetrics.lAveCharWidth) / 2;
RightMargin = (3 * FontMetrics.lAveCharWidth) - LeftMargin;
/* Determine the stopping point for our algorithm below.
*/
if ( *pLeftDVW )
pFI = QueryNextVisibleCol( hwndCnr, (PFIELDINFO)CMA_FIRST );
else
pFI = QueryNextVisibleCol( hwndCnr, CnrInfo.pFieldInfoLast );
if ( *pLeftDVW && CnrInfo.pFieldInfoLast)
if (CnrInfo.pFieldInfoLast->flData & CFA_INVISIBLE)
pLastVisibleFI = QueryPrevVisibleCol(hwndCnr,
CnrInfo.pFieldInfoLast );
else
pLastVisibleFI = CnrInfo.pFieldInfoLast;
else
pLastVisibleFI = QueryPrevVisibleCol(hwndCnr,
(PFIELDINFO)CMA_LAST );
/* Calculate the first column.
*/
RightSide = ( (LONG)(WinSendMsg (hwndCnr,
CM_QUERYDETAILFIELDINFO,
MPFROMP(pFI),
MPFROMSHORT(CMA_DATAWIDTH) )) );
RightSide += FontMetrics.lAveCharWidth + RightMargin;
/* Continue looping and calculating each column's position until
* we reach the desired column.
*/
while ( pFI && (pFI != pColumn) )
{
USHORT Width;
LeftSide = RightSide;
pFI = QueryNextVisibleCol( hwndCnr, pFI );
Width = (LONG)(WinSendMsg (hwndCnr,
CM_QUERYDETAILFIELDINFO,
MPFROMP(pFI),
MPFROMSHORT(CMA_DATAWIDTH) ));
RightSide += Width;
RightSide += RightMargin + LeftMargin;
}
/* If the column is the last visible column in its viewport then
* the right margin must be adjusted accordingly.
*/
if ( pFI == pLastVisibleFI )
RightSide += FontMetrics.lAveCharWidth - RightMargin;
/* Return to sender.
*/
pPointL->x = LeftSide;
pPointL->y = RightSide;
return;
}
/*----------------------------------------------------------------------
Function Name: QueryNextVisibleCol
Description:
This function is used to retrieve the next visible column in the
container from the given one. It is possible to have invisible
columns in the container so this function skips over these. It
also allows CMA_FIRST to be passed in so that it will return the
first visible column in this case.
Parameters:
(HWND)hwndCnr - The container window handle.
(PFIELDINFO)pColumn - The column from which to start from.
----------------------------------------------------------------------*/
PFIELDINFO QueryNextVisibleCol( HWND hwndCnr, PFIELDINFO pFI)
{
/* If CMA_FIRST, get the first visible column in the container.
*/
if ( pFI == (PFIELDINFO)CMA_FIRST )
{
pFI = WinSendMsg (hwndCnr,
CM_QUERYDETAILFIELDINFO,
NULL,
MPFROMSHORT(CMA_FIRST) );
if (!(pFI->flData & CFA_INVISIBLE) || !pFI )
return( pFI );
}
/* Continue querying the next column until a visible column is found
* or we reach the end.
*/
pFI = WinSendMsg (hwndCnr,
CM_QUERYDETAILFIELDINFO,
MPFROMP(pFI),
MPFROMSHORT(CMA_NEXT) );
while ( pFI && (pFI->flData & CFA_INVISIBLE) )
{
pFI = WinSendMsg (hwndCnr,
CM_QUERYDETAILFIELDINFO,
MPFROMP(pFI),
MPFROMSHORT(CMA_NEXT) );
}
return( pFI );
}
/*----------------------------------------------------------------------
Function Name: QueryPrevVisibleCol
Description:
This function is used to retrieve the previous visible column in the
container from the given one. It is possible to have invisible
columns in the container so this function skips over these. It
also allows CMA_LAST to be passed in so that it will return the
last visible column in this case.
Parameters:
(HWND)hwndCnr - The container window handle.
(PFIELDINFO)pColumn - The column from which to start from.
----------------------------------------------------------------------*/
PFIELDINFO QueryPrevVisibleCol( HWND hwndCnr, PFIELDINFO pFI)
{
/* If CMA_LAST, get the last visible column in the container.
*/
if ( pFI == (PFIELDINFO)CMA_LAST )
{
pFI = WinSendMsg (hwndCnr,
CM_QUERYDETAILFIELDINFO,
NULL,
MPFROMSHORT(CMA_LAST) );
if (!(pFI->flData & CFA_INVISIBLE) || !pFI )
return( pFI );
}
/* Continue querying the previous column until a visible column is found
* or we reach the beginning.
*/
pFI = WinSendMsg (hwndCnr,
CM_QUERYDETAILFIELDINFO,
MPFROMP(pFI),
MPFROMSHORT(CMA_PREV) );
while ( pFI && (pFI->flData & CFA_INVISIBLE) )
{
pFI = WinSendMsg (hwndCnr,
CM_QUERYDETAILFIELDINFO,
MPFROMP(pFI),
MPFROMSHORT(CMA_PREV) );
}
return( pFI );
}
/*----------------------------------------------------------------------
Function Name: QueryColumnFromRect
Description:
This function is not called by this application but is provided
as additional information.
This function is similar to the container's CM_QUERYRECORDFROMRECT.
It takes a rectangle in window coordinates and starting from a
given column will search for the next column overlapping this
rectangle. Note that this is like CMA_PARTIAL in the
CM_QUERYRECORDFROMRECT message.
Due to time constraints, this function was only written to handle
the CMA_PARTIAL and an unsplit details view. However, it is
a little tedious but not difficult to add the remaining functions.
Notice that this function ignores the top and bottom values of the
rectangle as only the right and left values are pertinent.
Parameters:
(HWND)hwndCnr - The container window handle.
(PFIELDINFO)pStartFI - The column from which to start from.
(PRECTL)pRect - The rectangle used to query with
(In container window coordinates)
----------------------------------------------------------------------*/
PFIELDINFO QueryColumnFromRect( HWND hwndCnr, PFIELDINFO pStartFI,
PRECTL pRect )
{
PFIELDINFO pFI;
POINTL ColPos;
BOOL bleft, bSplit;
RECTL rclViewport;
RECTL SearchRect;
/* Adjust the given rectangle in window coordinates to workspace
* coordinates.
*/
WinSendMsg (hwndCnr,
CM_QUERYVIEWPORTRECT,
MPFROMP(&rclViewport),
MPFROM2SHORT(CMA_WORKSPACE, FALSE) );
pRect->xLeft += rclViewport.xLeft;
pRect->xRight += rclViewport.xLeft;
pFI = QueryNextVisibleCol( hwndCnr, pStartFI );
while (pFI)
{
/* Query the columns position.
*/
QueryColumnPos( hwndCnr, pFI, &ColPos, &bleft, &bSplit);
/* This if statement basically checks to see if there is any
* overlap between the column and the given rectangle. We
* are providing the CMA_PARTIAL case only.
*/
if ( ((ColPos.x <= pRect->xLeft) && (pRect->xLeft <= ColPos.y)) ||
((ColPos.x <= pRect->xRight) && (pRect->xRight <= ColPos.y)) ||
((pRect->xLeft <= ColPos.x) && (ColPos.y <= pRect->xRight)) )
return( pFI );
pFI = QueryNextVisibleCol( hwndCnr, pFI );
}
return( NULL );
}
/*----------------------------------------------------------------------
Function Name: ProcessInitDrag
Description:
This function is called when CN_INITDRAG is received from the
container. It sets up the DRAGINFO, DRAGITEM, and DRAGIMAGE
structures and initiates drag by calling DrgDrag.
Note: This drag example only provides dragging of one item at a
time. CUA '91 suggests that if a selected item is dragged
then all the selected items should be dragged.
Parameters:
(HWND)hwnd - The container's owner window handle.
(HWND)hwndCnr - The container window handle.
(PCNRDRAGINT)pCnrDragInit - The rectangle used to query with
----------------------------------------------------------------------*/
VOID ProcessInitDrag( HWND hwnd, HWND hwndCnr,
PCNRDRAGINIT pCnrDragInit )
{
PDRAGINFO pDragInfo;
DRAGITEM DragItem;
HWND hwndDest;
DRAGIMAGE DragImage;
/* Only start drag if there is a record under pointer. */
if (pCnrDragInit->pRecord)
{
/* Allocate Drag Info structure. Set the drag source window hwnd.
* Set the DragItem and the DragImage.
*/
pDragInfo = DrgAllocDraginfo ( 1 );
pDragInfo->hwndSource = hwndCnr;
SetDragItem( hwndCnr, pDragInfo, pCnrDragInit->pRecord);
SetDragImage(hwndCnr, (PDRAGIMAGE) &DragImage,
pCnrDragInit, pCnrDragInit->pRecord);
/* Call drag to start dragging. */
hwndDest = DrgDrag (hwndCnr,
pDragInfo,
(PDRAGIMAGE) &DragImage,
1,
VK_BUTTON2,
(PVOID) 0x80000000);
}
}
/*----------------------------------------------------------------------
Function Name: SetDragItem
Description:
This function is called to setup the DRAGITEM structure and then
call DrgSetDragitem.
Parameters:
(HWND)hwndCnr - The container window handle.
(PDRAGINFO)pDragInfo - The DragInfo structure.
(PRECORDCORE)pRecord - The record being dragged.
----------------------------------------------------------------------*/
VOID SetDragItem(HWND hwndCnr, PDRAGINFO pDragInfo, PRECORDCORE pRecord)
{
DRAGITEM dragItem;
/* Setup the DragItem structure. */
dragItem.hwndItem = hwndCnr;
dragItem.ulItemID = (ULONG) pRecord;
dragItem.fsControl = 0;
dragItem.fsSupportedOps = 0;
/* Set the rendering mechanism and formats for the item. */
dragItem.hstrRMF = DrgAddStrHandle ("<DRM_PRINT, DRF_TEXT>");
/* Setup icon string for item being dragged. */
dragItem.hstrSourceName = DrgAddStrHandle((PSZ) pRecord->pszIcon);
/* Call drag to set the item into the drag info structure. */
DrgSetDragitem (pDragInfo, &dragItem, sizeof(DRAGITEM), 0);
}
/*----------------------------------------------------------------------
Function Name: SetDragImage
Description:
This function is called to setup the DRAGIMAGE structure.
Parameters:
(HWND)hwndCnr - The container window handle.
(PDRAGIMAGE)pDragImage - The DragImage structure.
(PCNRDRAGINIT)pCnrDragInit - The container's DragInit structure.
(PRECORDCORE)pRecord - The record being dragged.
----------------------------------------------------------------------*/
VOID SetDragImage(HWND hwndCnr, PDRAGIMAGE pDragImage,
PCNRDRAGINIT pCnrDragInit, PRECORDCORE pRecord)
{
CNRINFO CnrInfo;
WinSendMsg (hwndCnr, CM_QUERYCNRINFO,
MPFROMP(&CnrInfo), MPFROMSHORT(sizeof(CNRINFO)));
/* Initialize DragImage structure with information about image
* being dragged.
*/
pDragImage->cb = sizeof( DRAGIMAGE );
pDragImage->cptl = 0;
pDragImage->fl = DRG_ICON;
pDragImage->cxOffset = HIUSHORT(pCnrDragInit->cx);
pDragImage->cyOffset = HIUSHORT(pCnrDragInit->cy);
if ((pCnrDragInit->pRecord->hptrIcon) &&
!(CnrInfo.flWindowAttr & CV_MINI))
{
/* Drag regular icon. */
pDragImage->hImage = pRecord->hptrIcon;
}
else
if ((pCnrDragInit->pRecord->hptrMiniIcon) &&
(CnrInfo.flWindowAttr & CV_MINI))
{
/* Drag regular mini icon. */
pDragImage->hImage = pRecord->hptrMiniIcon;
}
else
{
/* Drag system pointer. */
pDragImage->hImage = WinQuerySysPointer(HWND_DESKTOP,
SPTR_APPICON,FALSE);
}
/* Setup image offsets from the pointer hotspot. */
pDragImage->cxOffset = (SHORT)pCnrDragInit->cx;
pDragImage->cyOffset = (SHORT)pCnrDragInit->cy;
}
/*----------------------------------------------------------------------
Function Name: ProcessDrop
Description:
This function is called when the container sends the CN_DROP
notification. This function moves the items in icon view.
It will do the same for other non-tree views when the record
is not being dropped on another record. This sample program
keeps track whether CN_DRAGOVER or CN_DRAGAFTER was last
received so that we can act appropriately when CA_MIXEDTARGETEMPH
is being used.
Parameters:
(HWND)hwnd - The container's owner window handle.
(HWND)hwndCnr - The container window handle.
(PCNRDRAGINFO)pCnrDragInfo - The container's DragInfo structure.
----------------------------------------------------------------------*/
VOID ProcessDrop( HWND hwnd, HWND hwndCnr, PCNRDRAGINFO pCnrDragInfo )
{
PDRAGINFO pDragInfo;
CNRINFO CnrInfo;
RECORDINSERT RecordInsert;
PRECORDCORE pRecord;
PDRAGITEM pDragItem;
pDragInfo = pCnrDragInfo->pDragInfo;
DrgAccessDraginfo(pDragInfo);
/* Check if dragging within same container window. */
if (hwndCnr == pDragInfo->hwndSource)
{
WinSendMsg (hwndCnr, CM_QUERYCNRINFO,
MPFROMP(&CnrInfo), MPFROMSHORT(sizeof(CNRINFO)));
/* Query the drag item pointer for the dropped item. */
pDragItem = DrgQueryDragitemPtr (pDragInfo, 0);
/* Get the record pointer from the drag item structure. */
pRecord = (PRECORDCORE)pDragItem->ulItemID;
/* Check if the current view is icon. */
if (CnrInfo.flWindowAttr & CV_ICON)
{
/* Erase the record from previous location. */
WinSendMsg (hwndCnr, CM_ERASERECORD, MPFROMP(pRecord), NULL);
/* Update icon position with the new drop position. Add a
* variance such that records are not dropped on top of each
* other.
*/
pRecord->ptlIcon.x = pDragInfo->xDrop;
pRecord->ptlIcon.y = pDragInfo->yDrop;
/* Map the drop coordinates to the Container window. */
WinMapWindowPoints (HWND_DESKTOP, hwndCnr, &(pRecord->ptlIcon),
(SHORT)1);
pRecord->ptlIcon.x += CnrInfo.ptlOrigin.x;
pRecord->ptlIcon.y += CnrInfo.ptlOrigin.y;
/* Paint the record in its new position within the Container. */
WinSendMsg (hwndCnr, CM_INVALIDATERECORD, MPFROMP(&pRecord),
MPFROMSHORT(1));
}
else
{
PSAMPLEINFO pSampleInfo;
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
/* Process the item as Name or Text view. The insertion will
* only occur if we are not dropping the record on itself
* and dragafter was the last drag message received.
*/
if ((pRecord != pCnrDragInfo->pRecord) &&
(pSampleInfo->bDragAfter) && (pCnrDragInfo->pRecord))
{
WinSendMsg (hwndCnr, CM_REMOVERECORD, MPFROMP(&pRecord),
MPFROM2SHORT(1,FALSE));
/* Setup information for CM_INSERTRECORD. */
RecordInsert.cb = (ULONG)sizeof(RECORDINSERT);
RecordInsert.pRecordOrder = pCnrDragInfo->pRecord;
RecordInsert.zOrder = (USHORT)CMA_TOP;
RecordInsert.cRecordsInsert = (USHORT)1;
RecordInsert.fInvalidateRecord = FALSE;
RecordInsert.pRecordParent = NULL;
WinSendMsg (hwndCnr, CM_INSERTRECORD, MPFROMP(pRecord),
MPFROMP(&RecordInsert));
/* Paint the Container window. */
WinSendMsg (hwndCnr, CM_INVALIDATERECORD,
NULL, MPFROM2SHORT(0, CMA_REPOSITION));
}
else if (pRecord != pCnrDragInfo->pRecord)
{
/* Process for dragover last received...The dragged record
* was dropped on the target. Left as an exercise for the
* interested application developer.
*/
}
}
}
DrgFreeDraginfo (pDragInfo);
}
/*----------------------------------------------------------------------
Function Name: RemoveRecords
Description:
This function will remove the records specified in the pMenuRecord
linked list. The pMenuRecord linked list was created to represent
all the records that a particular context menu operation applied to.
Parameters:
(HWND) hwnd - The handle of the client window.
----------------------------------------------------------------------*/
VOID RemoveRecords (HWND hwnd)
{
PSAMPLEINFO pSampleInfo;
PPERSONRECORD pPersonRecNext;
PPERSONRECORD pPersonRec;
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
/* This value is a pointer to a linked list of records the
* context menu applies to. Remove each of the records in
* this linked list.
*/
if (pSampleInfo->pMenuRecord)
{
pPersonRec = pSampleInfo->pMenuRecord;
while (pPersonRec)
{
pPersonRecNext=(PPERSONRECORD)pPersonRec->MiniRec.preccNextRecord;
WinSendMsg (pSampleInfo->hwndCnr, CM_REMOVERECORD,
MPFROMP(&pPersonRec), MPFROM2SHORT(1, CMA_FREE));
pPersonRec = pPersonRecNext;
}
/* Before invalidating the records, we must query the latest and
* most up-to-date information for them. Remember that the
* CM_INVALIDATERECORD message will copy the current contents
* of flRecordAttr and ptlIcon to the internal storage area
* maintained for each record. Since we are using record
* sharing, this call is necessary. Since these records may
* have been shared, it is possible that the external values
* for flRecordAttr and ptlIcon are not valid. We need to
* query the valid information from the container first, so we
* don't cause any problems.
*/
WinSendMsg (pSampleInfo->hwndCnr, CM_QUERYRECORDINFO,
NULL, MPFROMSHORT(0));
/* Invaidate all the records. */
WinSendMsg (pSampleInfo->hwndCnr, CM_INVALIDATERECORD,
NULL, MPFROM2SHORT(0, CMA_REPOSITION));
}
return;
}
/*----------------------------------------------------------------------
Function Name: GetSelectedRecords
Description:
This function builds a linked list of currently selected records
that exist in the container and returns a pointer to the head of
the list. This function is called when the user requests a
context menu on a record that is selected. When this happens, the
menu should apply to all currently selected records.
Parameters:
(HWND) hwnd - The handle of the client window.
----------------------------------------------------------------------*/
PPERSONRECORD GetSelectedRecords (HWND hwnd)
{
PSAMPLEINFO pSampleInfo;
PPERSONRECORD pPersonRec;
PPERSONRECORD pPersonRecFirst = NULL;
PPERSONRECORD pPersonRecCur = NULL;
/* Get the pointer to our control block. */
pSampleInfo = (PSAMPLEINFO)WinQueryWindowPtr (hwnd, QWL_USER);
/* Get the first selected record in the container. */
pPersonRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
CM_QUERYRECORDEMPHASIS,
(MPARAM)CMA_FIRST,
MPFROMSHORT(CRA_SELECTED));
/* Build a linked list of selected records. When the list is
* complete, return a pointer to the head of the list.
*/
while (pPersonRec)
{
if (pPersonRecFirst)
{
pPersonRecCur->MiniRec.preccNextRecord =
(PMINIRECORDCORE)pPersonRec;
}
else
{
pPersonRecFirst = pPersonRec;
}
pPersonRecCur = pPersonRec;
pPersonRecCur->MiniRec.preccNextRecord = NULL;
/* Get the next selected record in the container. */
pPersonRec = (PPERSONRECORD)WinSendMsg (pSampleInfo->hwndCnr,
CM_QUERYRECORDEMPHASIS,
MPFROMP(pPersonRec),
MPFROMSHORT(CRA_SELECTED));
}
return (pPersonRecFirst);
}
/*----------------------------------------------------------------------
Function Name: FindInUseRecord
Description:
This function is called whenever an additional view is closed. Since
we added IN_USE emphasis to the record that was clicked on in the
main container window, we need to find the record that IN_USE
emphasis was applied to. We do so by looking at the records in the
main container window that have IN_USE emphasis already applied.
If their icon string matches the title of the additional view,
we have found the record which matches the view being closed.
Parameters:
(HWND) hwnd - The handle of the client window.
----------------------------------------------------------------------*/
PPERSONRECORD FindInUseRecord (HWND hwndAdditionalCnr, HWND hwndMainCnr)
{
CNRINFO CnrInfo;
PPERSONRECORD pInUseRec;
WinSendMsg (hwndAdditionalCnr, CM_QUERYCNRINFO,
MPFROMP(&CnrInfo), MPFROMSHORT(sizeof(CNRINFO)));
pInUseRec = WinSendMsg (hwndMainCnr, CM_QUERYRECORDEMPHASIS,
(MPARAM)CMA_FIRST, MPFROMSHORT(CRA_INUSE));
while ((pInUseRec) &&
(strcmp (CnrInfo.pszCnrTitle, pInUseRec->MiniRec.pszIcon)))
{
pInUseRec = WinSendMsg (hwndMainCnr, CM_QUERYRECORDEMPHASIS,
MPFROMP(pInUseRec), MPFROMSHORT(CRA_INUSE));
}
return (pInUseRec);
}