Creating a Workplace Shell "PileOf" Class
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:
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³User's action A "regular" A "regular" ³ ³ folder object PileOf object ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Drag and drop a Adds the new If the object ³ ³folder from the folder to its is a "pile" of ³ ³desktop into a list of items. data files, or ³ ³folder or another non- ³ ³PileOf object. If the folder folder class, ³ ³ object is the drop will ³ ³ closed, the not be allowed.³ ³ folder is added ³ ³ at the end or If the object ³ ³ according to a is a "pile" of ³ ³ sorting folders and the³ ³ criteria. PileOf object ³ ³ is closed, the ³ ³ If the folder folder will go ³ ³ holds other on top of the ³ ³ folders and it other folders. ³ ³ is opened, the No other ³ ³ new folder can choices will be³ ³ be inserted allowed. ³ ³ between folders ³ ³ or placed If the object ³ ³ inside another is a "pile" of ³ ³ folder (its folders and the³ ³ position can be PileOf object ³ ³ modified by is opened, the ³ ³ sorting folder can be ³ ³ criteria). inserted in ³ ³ between folders³ ³ or inside ³ ³ another folder ³ ³ (no sorting ³ ³ criteria is ³ ³ enforced for ³ ³ the folders in ³ ³ the pile). ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Drag and drop a Adds the new If the object ³ ³non-folder object to its is a "pile" of ³ ³object from the list of items. another object ³ ³desktop into a class or the ³ ³folder or If the folder object class ³ ³PileOf object. object is being ³ ³ closed, the introduced is ³ ³ object is added not in the set ³ ³ at the end or of permissible ³ ³ according to classes, the ³ ³ sorting drop will fail.³ ³ criteria. ³ ³ If the object ³ ³ If the folder is a closed " ³ ³ holds other pile" of ³ ³ objects and it objects of the ³ ³ is opened, the same class as ³ ³ new object can the object ³ ³ be inserted being ³ ³ between objects introduced, or ³ ³ or placed the object ³ ³ inside another introduced is ³ ³ folder (its in the set of ³ ³ position can be permissible ³ ³ modified by classes, the ³ ³ sorting object will go ³ ³ criteria). on top of the ³ ³ pile. No other ³ ³ choices will be³ ³ allowed. ³ ³ ³ ³ If the object ³ ³ is an opened " ³ ³ pile" of ³ ³ objects of the ³ ³ same class as ³ ³ the object ³ ³ being ³ ³ introduced, or ³ ³ the object ³ ³ introduced is ³ ³ in the set of ³ ³ permissible ³ ³ classes, the ³ ³ object can be ³ ³ inserted ³ ³ between objects³ ³ (no sorting ³ ³ criteria is ³ ³ enforced for ³ ³ the objects in ³ ³ the pile). ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Drag two or All objects A new pile with³ ³more objects appear on the those items is ³ ³out of a folder desktop. created on the ³ ³or PileOf desktop. Their ³ ³object and drop order is ³ ³them on the maintained. The³ ³desktop. settings ³ ³ notebook should³ ³ appear so you ³ ³ can name the ³ ³ pile. ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Select sorting Does not apply. Sorts the piles³ ³criteria for but not the ³ ³the PileOf contents of any³ ³piles object. of the piles. ³ ³ Each pile ³ ³ maintains its ³ ³ own sort order.³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Select sorting Destructive-- Non-destructive³ ³criteria. All items in --All items in ³ ³ the folder are the pile can be³ ³ arranged arranged ³ ³ according to according to ³ ³ sorting sorting ³ ³ criteria. When criteria, but ³ ³ the folder is when you close ³ ³ closed or the pile and ³ ³ reopened, items reopen it, the ³ ³ will reflect original order ³ ³ sorting is preserved. ³ ³ criteria. You can drag ³ ³ and drop ³ ³ objects within ³ ³ the pile to ³ ³ arrange them ³ ³ the way you ³ ³ want. Also, a ³ ³ sort and save ³ ³ option can be ³ ³ included for ³ ³ larger piles. ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Insert a PileOf Does not apply. If the PileOf ³ ³object into a piles object is³ ³PileOf piles closed, the new³ ³object. pile goes on ³ ³ top. A dialog ³ ³ offering to ³ ³ change the name³ ³ of the pile ³ ³ should appear. ³ ³ ³ ³ If the PileOf ³ ³ piles object is³ ³ opened, the new³ ³ pile can be ³ ³ inserted ³ ³ between piles ³ ³ but not inside ³ ³ another pile. ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Desktop area Defaults to 2" Because objects³ ³taken up by an x 7" (even if appear as a ³ ³opened folder the folder list, which you³ ³object or contains only can scroll down³ ³PileOf object. one object). , the list can ³ ³ be made very ³ ³ small (for ³ ³ example, show ³ ³ only two ³ ³ objects). ³ ³ Defaults to 1" ³ ³ x 3". ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Resize the Some objects No problem--the³ ³window so that might become " hidden objects ³ ³it shows only hidden" because can only belong³ ³two of the they are out of to the classes ³ ³objects in the view, and the supported by ³ ³contained user can forget the PileOf ³ ³object. that the object. You ³ ³ objects exist. implicitly know³ ³ what's there. ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
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:
- In the PILEOF.IDL file pass-through section:
/* used in MyDialogProc */ typedef struct _WINDATA { SOMObject *somSelf; SOMObject *somClassObj; } WINDATA, *PWINDATA; /* 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;
- In the source code file (PILEOF.C):
/* Enumerate the object classes registered with workplace */ 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; ulCurrentObject<ulNumberOfObjects; ulCurrentObject++) { pDragItem = DrgQueryDragitemPtr( pdrgInfo, ulCurrentObject); DrgQueryStrName( pDragItem->hstrRMF, 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=<OBJECT_PILEOF>" , "<WP_DESKTOP>" , 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.
Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation