Creating a Workplace Shell "PileOf" Class

by Bernie Arruza

Let's be creative and think about things in a new way. For instance, you know how objects in the world around you can be manipulated. Your fingers are like a form of user interface. You can pick things up and drop them. From experience, you have an idea of how much those things weigh (you adjust your muscles accordingly) or if they will break (from their composition).

Computers will someday be that smart, friendly, powerful, and three-dimensional. But we are not there yet.

With today's technology, OS/2 desktop objects represent a two-dimensional world fairly efficiently. Look at the objects you see on the OS/2 desktop; study their behavior. Can you think of new classes of objects? Can you think of new behaviors for those objects? Would these new behaviors enhance your desktop?

I receive a lot of magazines from different interest groups. What do I do with them? Well, some of them I read and throw away, but for the most part, when I read them I put them away in neatly arranged piles.

Unlike a folder, a pile "distinctively implies the laying of one thing...on top of another in a more or less orderly formation;...an assembly of like things or things of approximately the same size or shape" (from Webster's New Dictionary of Synonyms, Springfield, MA: GMerriam Co., 1973). The term pile is more adequate than heap, stack, mass, or bank to convey the idea behind this kind of behavior when we apply it to the OS/2 desktop.

This article discusses the Workplace Shell PileOf class, and provides sample code that demonstrates how you can quickly prototype the desired behavior of a Workplace Shell object.

Because the closest thing to a PileOf object is an OS/2 folder object, let's begin by looking at the differences between the two kinds of objects: As you can see, the PileOf object class reuses a lot of the code from the WPFolder class. However, it looks and behaves differently.

Unlike a regular folder, the PileOf object:
 * Contains a settings page so the user can specify the type of pile and its behavior.
 * Validates drag/drop to ensure the object being dragged/dropped is the right type.
 * Contains extra items in its popup menu for the manipulation of its contents.

PileOf Settings Page
In the _wpAddSettingsPages method, call the parent first (WPFolder class) and then call the PileOf method: /* Add PileOf settings page */ ulPageId = _AddPileOfPage(somSelf,hwndNotebook); The _AddPileOfPage method fills up the PAGEINFO structure for the PileOf page and invokes the wpInsertSettingsPage method. PAGEINFO pi;                              /* Variable declaration */

pi.cb                 = sizeof(PAGEINFO); pi.hwndPage           = NULLHANDLE; pi.usPageStyleFlags   = BKA_MAJOR; pi.usPageInsertFlags  = BKA_FIRST; pi.pfnwp              = MyDialogProc;     /* Pileof settings controls    */ /* messages go here           */ pi.resid              = hmod;             /* from DosQueryModuleHandle of*/ /* PILEOF.DLL                 */ pi.dlgid              = DLG_TITLE; pi.pszName            = "PileOf";         /* PileOf tab text             */ pi.pCreateParams      = somSelf;          /* dialog create parms         */ pi.idDefaultHelpPanel = 0; pi.pszHelpLibraryName = NULL;

ulPageID = _wpInsertSettingsPage ( somSelf, hwndNbk, &pi );  Note: You can/should override some of the wpAddFolderXXXXX (XXXXX=page type) methods and have them return SETTINGS_PAGE_REMOVED. It's up to you to decide what settings pages just plain don't belong for your PileOf object. The open view in my implementation (and the only view) is a modified Details view.





''Figure 1. PileOf settings page and PileOf settings page with selected items''

As you can see the settings page contains several controls:
 * A list box to display and choose from all the OS/2 system objects (you can choose one or more). If you choose to have a pile of bitmaps and icons, you select the Bitmap and Icon entries in the list box. Those are the only two classes of objects that you can drop into this PileOf object.
 * A set of check boxes for options:
 * One Class only - When checked, only one item can be selected from the list box (single selection). When unchecked, multiple items can be selected. After multiple items are selected, this button becomes disabled. To reactivate it, reduce the number of selected items in the list box to zero or one.
 * Spread contents - When checked, the popup menu for this PileOf object contains the Spread Contents menu item. When clicked, a folder/icon view of the contents of the pile is displayed.
 * Delete if empty - If selected, and if the PileOf object remains empty before shutdown, the PileOf object will be deleted. After all, an empty pile is not a pile. But then, a pile of one object is not a pile either. You decide!


 * A set of radio buttons for the way objects will be piled:
 * Left to Right (this would be like a bookcase)
 * Top to Bottom (more like a true pile)

Other PileOf settings pages/controls can be added as desired.

To extract a list of all OS/2 public class objects, do the following:  /* used in MyDialogProc */ typedef struct   _WINDATA {            SOMObject   *somSelf; SOMObject  *somClassObj; } WINDATA, *PWINDATA;
 * In the PILEOF.IDL file pass-through section:

/* structure to keep track of PileOf class object list */ /* there are several other ways of doing this !!! */   typedef  struct   _CLSINSERT1 {          PSZ            pszClass; SOMClass      *Class; SOMClass      *ClassParent; BOOL          flClassSelected; } CLSINSERT1, *PCLSINSERT1;

/* PileOf instance data. Other info could/should be added */ typedef struct _DATAPILEOF {         ULONG           cClassIns; PCLSINSERT1 pClsInsert; } DATAPILEOF, *PDATAPILEOF;   /* Enumerate the object classes registered with workplace */
 * In the source code file (PILEOF.C):

WinEnumObjectClasses(NULL,&ulSize);

/* Initialize the structures */ if ( ( pDtPileOf = (PDATAPILEOF)_wpAllocMem(somSelf, sizeof(DATAPILEOF),NULL) ) &&         ( pObjClass = (POBJCLASS)_wpAllocMem(somSelf, ulSize,NULL) ) &&             ( pClassIns = (PCLSINSERT1)_wpAllocMem(somSelf, ulSize,NULL) )          ) {         memset((PBYTE)pClassIns,0,(USHORT)ulSize); pclsi = pClassIns; if ( WinEnumObjectClasses(pObjClass,&ulSize) ) {            poc = pObjClass; while (poc) {               /* Find the NLS name for this class */ ClassId  = SOM_IdFromString(poc->pszClassName); somPrintf( "The class name is: '%s'\n",poc->pszClassName); if ((Class =_somClassFromId(SOMClassMgrObject,ClassId)) &&                   !(_wpclsQueryStyle(Class) & CLSSTYLE_PRIVATE)) {                   pszClass = _wpclsQueryTitle(Class); if (!pszClass) pszClass = poc->pszClassName; somPrintf( "The private class name is: '%s'\n", pszClass);

/* Save the class in an entry in the CLSINSERT1 table that */ /* we construct, along with its parent class              */ pclsi->pszClass   = pszClass; pclsi->Class      = Class; pclsi->ClassParent = _somGetParent(Class); pclsi->flClassSelected = FALSE; pclsi    += 1; cClassIns += 1; }               SOMFree(ClassId); poc = poc->pNext; }         }       } 

Validating Drag/Drop
Again, the behavior of the PileOf class demands that we restrict the user's ability to drop items into it. Only the objects from one of the classes selected in the PileOf settings page will be permitted into the pile.

To accomplish this, use the PileOf method ValidateDragAndDrop, which will know where the pile's instance data is, as follows:  /* Set pointer to PileOf data */ pDtPileOf = (PDATAPILEOF)_QueryPileOfInstData( somSelf );

/* Points to class list structure */ pClassIns = pDtPileOf->pClsInsert;

/* Get the number of objects being dragged */ ulNumberOfObjects = DrgQueryDragitemCount( pdrgInfo);

/* Test each object */ for( ulCurrentObject = 0; ulCurrentObjecthstrRMF, sizeof( pszBuffer), pszBuffer); somPrintf( "The rendering mechanism is: '%s'\n", pszBuffer);

/* Make sure that the rendering mechanism is DRM_OBJECT */ /* Only objects with a rendering mechanism of DRM_OBJECT */ /* can be considered. */   if(DrgVerifyRMF( pDragItem, "DRM_OBJECT", NULL)) {     /* Get type of object */ Object = OBJECT_FROM_DRAGITEM(pDragItem);

/* Get class from object */ somClassObj = _somGetClass ( Object );

/* Get class title for this class */ pszClass = _wpclsQueryTitle(somClassObj);

/* See if this class title is in the list of classes in object's */ /* instance data. */     for (j = 0; j < pDtPileOf->cClassIns; j++) {       pclsi = pClassIns + j;

if (pclsi->flClassSelected)

/* See if class was selected for PileOf object */ if (!strcmp(pclsi->pszClass, pszClass)) break;

/* At least one of the objects being dragged is not in         */ /* the list of allowed objects. DROP should fail ! */       /* DESIGN: if one object in the DRAGINFO structure can          */ /*        not be dropped in this PileOf object, can we somehow */ /*        drop the rest? For now the DROP will fail for all. */       if ((j+1) == pDtPileOf->cClassIns) return (FALSE);

}

}     else return ( FALSE ); }

Next, override wpDragOver and wpDrop and call ValidateDragAndDrop, as follows:  /* Call the parent's method first  */ /* If the parent said it is okay to drop, then we will check it also. */

if( DOR_NEVERDROP != SHORT1FROMMR( mResult)){

/* If the object is acceptable, return the mResult from the parent */ if( FALSE == _ValidateDragAndDrop( somSelf, pdrgInfo)) mResult = MRFROM2SHORT( DOR_NEVERDROP, DO_UNKNOWN); } 

PileOf Popup Menu Items
To limit the open view to one view (Details view), override wpFilterPopupMenu. Call the parent first and then do the following: /* Remove the tree/icon view menu option. Make sure delete is available */ return ( ( ulFlags | CTXT_DELETE ) & ~CTXT_TREE ~CTXT_ICON); Details view is the default when you open a PileOf object. You can change to a new view or let the user specify a view by adding some extra code. Override wpModifyPopupMenu to add a few new options for the PileOf class, as follows:  /* Get the module handle for the dll */ rc = DosQueryModuleHandle( "PILEOF.DLL", &hmod );

/* call the parent first */ fFlagsOut =  parent_wpModifyPopupMenu(somSelf,hwndMenu,hwndCnr,iPosition);

/* IDM_PRIMARYMENU is in my PILEOF.RC file. The items it represents */ /* will be inserted at the end on WPMENUID_PRIMARY                  */ fSuccess = _wpInsertPopupMenuItems( somSelf                                      ,hwndMenu                                       ,iPosition                                       ,hmod                                       ,IDM_PRIMARYMENU                                       ,WPMENUID_PRIMARY);  To control what happens when one of the items being added is selected, override wpMenuItemSelected.

Testing PILEOF.DLL
The makefile for this Workplace Shell class was designed to run on Intel machines. It uses the C Set compiler and the Developer's Toolkit for OS/2 Warp.

I created two files, INSTALL.EXE and UNINST.EXE, to test the PILEOF.DLL. INSTALL.EXE calls the following:  hab = WinInitialize (0); /* register the PileOf class */ fSuccess = WinRegisterObjectClass ( "PileOf", "C:\\OS2\\DLL\\PILEOF.DLL" );

if ( fSuccess == FALSE ) {       printf ( "Unable to register object class PileOf, error: " ); printf ( "%x\n", ERRORIDERROR ( WinGetLastError ( hab ) ) ); } /* create an instance of the PileOf class */ hobjPileOf = WinCreateObject ( "PileOf"                             , "PileOf..."                               , "OBJECTID="                               , ""                               , CO_REPLACEIFEXISTS ); if ( hobjPileOf == 0 ) {        printf ( "Unable to create PileOf... object, error: " ); printf ( "%x\n", ERRORIDERROR ( WinGetLastError ( hab ) ) ); }

WinTerminate ( hab );

UNINST.EXE calls the following:

hab = WinInitialize ( 0 );

fSuccess = WinDeregisterObjectClass ( "PileOf" );

if ( fSuccess == FALSE ) {    printf ( "Unable to de-register object class PileOf, error: " ); printf ( "%d\n", ERRORIDERROR ( WinGetLastError ( hab ) ) ); }

WinTerminate (hab); 

Conclusion
When developing your user interface, look closely at the world around you for ideas. Can you take advantage of OS/2's object technology to bring the behavior of your user interface closer to the way real objects behave? It may take some doing to verify that if an object looks like a hammer, it actually behaves like a hammer. But think how much easier you are making it for your customer. That is the final reward, and that is what really matters.