Jump to content

Creating a Workplace Shell "PileOf" Class

From EDM2
Revision as of 18:46, 4 September 2013 by Martini (talk | contribs) (Created page with "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 ...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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:

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³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.

File:PileOF-Fig-1-2.gif

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