home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / listings / v_02_08 / 2n08007b < prev    next >
Text File  |  1991-06-19  |  20KB  |  567 lines

  1. /*
  2.     A CUA compliant window class for data entry.
  3.     Uses dialog templates created with the resource compiler (rc),
  4.     or the OS/2 SDK screen painter (dlgbox).
  5.  
  6.     NB: Unlike a dialog box, a form has a true client window which
  7.     owns the data entry control windows.
  8. */
  9.  
  10. #include <stdlib.h> /* for calloc() and free() */
  11.  
  12. #define INCL_DOSRESOURCES /* For DosGetResource() */
  13. #define INCL_WIN
  14. #include <os2.h>
  15.  
  16. #include "form.h"
  17.  
  18. extern HAB hab; /* Under OS/2 1.2 you could use
  19.                    WinQueryAnchorBlock( HWND ) */
  20.  
  21. static MRESULT EXPENTRY wpFormFrame( HWND, USHORT, MPARAM, MPARAM );
  22.  
  23. HWND EXPENTRY FormLoad( HWND hwndParent, HWND hwndOwner, PFNWP pfnwp,
  24.                         HMODULE hmod, USHORT id, PVOID p )
  25. {
  26.     SEL sel;
  27.     HWND hwnd = NULL;
  28.  
  29.     if (!DosGetResource( hmod, RT_DIALOG, id, &sel ))
  30.     {
  31.         hwnd = FormCreate( hwndParent, hwndOwner, pfnwp,
  32.                            MAKEP( sel, 0 ), hmod, id, p );
  33.         DosFreeSeg( sel );
  34.     }
  35.     return hwnd;
  36. }
  37.  
  38. #define LINE_SIZE 8   /* Number of pels to move on scroll by line  */
  39. #define BORDER_SIZE 4 /* Whitespace to be left round edges of form */
  40. #define QWS_CXFORM  (2*sizeof(PVOID))
  41. #define QWS_CYFORM  (2*sizeof(PVOID)+sizeof(USHORT))
  42.  
  43. static void set_scrollbars( HWND hwnd,
  44.                             SHORT cxClient, SHORT cyClient );
  45.  
  46. HWND EXPENTRY FormCreate( HWND hwndParent, HWND hwndOwner,
  47.                           PFNWP pfnwp, PDLGTEMPLATE pDlgTemplate,
  48.                           HMODULE hmod, USHORT id, PVOID p )
  49. {
  50.     static BOOL fInitialized;
  51.     USHORT i;
  52.     HWND hwndFrame, hwnd;
  53.     HWND hctl=NULL; /* default control to start with focus */
  54.     FRAMECDATA fcdata; /* holds parameters used
  55.                           to create a frame window */
  56.     PSZ pszTitle;
  57.     HWND *pahwnd; /* pointer to array of control window handles */
  58.     POINTL aptl[2];
  59.     DLGTITEM *pDlgItem = &pDlgTemplate->adlgti[ 0 ];
  60.     USHORT cxClient, cyClient;
  61.     RECTL rclForm;
  62.  
  63.     if (!fInitialized)
  64.     {
  65.         fInitialized = TRUE;
  66.         #define CLASS_NAME "FORM" /* any unique name will do */
  67.         WinRegisterClass( hab, CLASS_NAME, wpForm, 0,
  68.                           2*sizeof(PVOID)+2*sizeof(USHORT) );
  69.     }
  70.     fcdata.cb = sizeof fcdata; /* initialization required by PM */
  71.     fcdata.flCreateFlags = 
  72.         *(PULONG)((PBYTE)pDlgTemplate +
  73.                          pDlgTemplate->adlgti[0].offCtlData)|
  74.         FCF_TITLEBAR | FCF_SYSMENU | FCF_SIZEBORDER |
  75.         FCF_HORZSCROLL | FCF_VERTSCROLL;
  76.     fcdata.hmodResources = hmod;
  77.     fcdata.idResources = id;
  78.  
  79.     pszTitle = (PSZ)pDlgTemplate + pDlgTemplate->adlgti[ 0 ].offText;
  80.     if (!*pszTitle)              /* if no text for title bar */
  81.         pszTitle = "(Untitled)"; /* provide something        */
  82.  
  83.     aptl[0].x = pDlgItem->x;  /* read size and position of form */
  84.     aptl[0].y = pDlgItem->y;  /* from first DLGTITEM structure  */
  85.     aptl[1].x = pDlgItem->cx;
  86.     aptl[1].y = pDlgItem->cy;
  87.  
  88.     /* convert from dialog units to pels */
  89.     WinMapDlgPoints( hwndParent, aptl, 2, TRUE );
  90.     cxClient = (USHORT)aptl[1].x;
  91.     cyClient = (USHORT)aptl[1].y;
  92.     
  93.     /* inflate the frame window to accomodate controls around the form */
  94.     aptl[0].x -= WinQuerySysValue( HWND_DESKTOP, SV_CXSIZEBORDER );
  95.     aptl[0].y -= WinQuerySysValue( HWND_DESKTOP, SV_CYSIZEBORDER );
  96.     aptl[0].y -= WinQuerySysValue( HWND_DESKTOP, SV_CYHSCROLL );
  97.  
  98.     aptl[1].x += 2 * WinQuerySysValue( HWND_DESKTOP, SV_CXSIZEBORDER );
  99.     aptl[1].x += WinQuerySysValue( HWND_DESKTOP, SV_CXVSCROLL );
  100.  
  101.     aptl[1].y += 2 * WinQuerySysValue( HWND_DESKTOP, SV_CYSIZEBORDER );
  102.     aptl[1].y += WinQuerySysValue( HWND_DESKTOP, SV_CYTITLEBAR );
  103.     aptl[1].y += WinQuerySysValue( HWND_DESKTOP, SV_CYHSCROLL );
  104.  
  105.     if (fcdata.flCreateFlags & FCF_MENU)
  106.         aptl[1].y += WinQuerySysValue( HWND_DESKTOP, SV_CYMENU );
  107.     
  108.     hwndFrame = WinCreateWindow( hwndParent, WC_FRAME, pszTitle,
  109.                     0, 0, 0, 0, 0, hwndOwner, HWND_TOP, id,
  110.                     &fcdata, 0 );
  111.     if (!hwndFrame)
  112.     {
  113.         /* display PM error message */
  114.         ErrorInfoMessageBox( hab, hwndParent, "Form creation error" );
  115.         return NULL;
  116.     }
  117.     hwnd = WinCreateWindow( hwndFrame, CLASS_NAME, 0, 0, 0, 0, 0, 0, 0,
  118.                             HWND_TOP, FID_CLIENT, 0, 0 );
  119.  
  120.     WinSetWindowPos( hwndFrame, HWND_TOP, 
  121.                      (SHORT)aptl[0].x, (SHORT)aptl[0].y, 
  122.                      (SHORT)aptl[1].x, (SHORT)aptl[1].y,
  123.                      SWP_SIZE | SWP_MOVE );
  124.  
  125.     pahwnd = (HWND*)calloc( pDlgTemplate->adlgti[ 0 ].cChildren+1, 
  126.                             sizeof(HWND) );
  127.     WinSetWindowPtr( hwnd, QWL_USER+sizeof(PVOID), pahwnd );
  128.  
  129.     rclForm.xLeft = rclForm.xRight =
  130.         rclForm.yBottom = rclForm.yTop = 0;
  131.  
  132.     /* create the child control windows */
  133.     for (i = 1; i <= pDlgTemplate->adlgti[ 0 ].cChildren; i++)
  134.     {
  135.         pDlgItem = &pDlgTemplate->adlgti[ i ];
  136.  
  137.         aptl[0].x = pDlgItem->x;
  138.         aptl[0].y = pDlgItem->y;
  139.         aptl[1].x = pDlgItem->cx;
  140.         aptl[1].y = pDlgItem->cy;
  141.  
  142.         WinMapDlgPoints( hwnd, aptl, 2, TRUE/*Convert du to pels*/ );
  143.  
  144.         pahwnd[ i-1 ] = WinCreateWindow( hwnd, 
  145.             pDlgItem->offClassName < 10 ?
  146.                 /* Stock class or user defined? */
  147.                 (PSZ)(0xFFFF0000 | (LONG)pDlgItem->offClassName):
  148.                 (PSZ)pDlgTemplate + pDlgItem->offClassName,
  149.             (PSZ)pDlgTemplate + pDlgItem->offText,
  150.             pDlgItem->flStyle,
  151.             (SHORT)aptl[0].x, (SHORT)aptl[0].y, 
  152.             (SHORT)aptl[1].x, (SHORT)aptl[1].y, hwnd, HWND_TOP,
  153.             pDlgItem->id, 0, 0 );
  154.  
  155.         /* record first tabstop item - will receive default focus */
  156.         if (!hctl && pDlgItem->flStyle & WS_TABSTOP)
  157.             hctl = pahwnd[ i-1 ];
  158.  
  159.         rclForm.xLeft =   min( rclForm.xLeft,   aptl[0].x );
  160.         rclForm.yBottom = min( rclForm.yBottom, aptl[0].y );
  161.         rclForm.xRight =  max( rclForm.xRight,  aptl[0].x+aptl[1].x );
  162.         rclForm.yTop =    max( rclForm.yTop,    aptl[0].y+aptl[1].y );
  163.     }
  164.     WinSetWindowUShort( hwnd, QWS_CXFORM,
  165.                         (USHORT)(rclForm.xRight-rclForm.xLeft+
  166.                                                      2*BORDER_SIZE) );
  167.     WinSetWindowUShort( hwnd, QWS_CYFORM,
  168.                         (USHORT)(rclForm.yTop-rclForm.yBottom+
  169.                                                      2*BORDER_SIZE) );
  170.     WinSubclassWindow( hwndFrame, wpFormFrame );/* for scrollbars */
  171.     WinSubclassWindow( hwnd, pfnwp/*user defined window proc*/ );
  172.  
  173.     set_scrollbars( hwnd, cxClient, cyClient );
  174.  
  175.     if (!WinSendMsg( hwnd, WM_INITDLG, hctl, p ))
  176.         WinSetFocus( HWND_DESKTOP, hctl );
  177.  
  178.     if (pDlgItem->flStyle & WS_VISIBLE)
  179.         WinShowWindow( hwndFrame, TRUE );
  180.  
  181.     return hwnd;
  182. }
  183.  
  184. static MRESULT EXPENTRY wpFormFrame( HWND hwnd, USHORT msg,
  185.                                      MPARAM mp1, MPARAM mp2 )
  186. {
  187.     USHORT usPos, usMin, usMax;
  188.     SHORT sMove;
  189.     HWND hctl;
  190.     MRESULT mr;
  191.     RECTL rclUpdate;
  192.  
  193.     switch( msg )
  194.     {
  195.     case WM_HSCROLL:
  196.     case WM_VSCROLL:
  197.         hctl = WinWindowFromID( hwnd, SHORT1FROMMP(mp1) );
  198.         mr = WinSendMsg( hctl, SBM_QUERYRANGE, 0L, 0L );
  199.         usMin = SHORT1FROMMR( mr );
  200.         usMax = SHORT2FROMMR( mr );
  201.         usPos = SHORT1FROMMR( WinSendMsg( hctl, SBM_QUERYPOS, 0L, 0L ) );
  202.  
  203. #if !defined(min)
  204. #define max(a,b)        (((a) > (b)) ? (a) : (b))
  205. #define min(a,b)        (((a) < (b)) ? (a) : (b))
  206. #endif
  207.         switch (SHORT2FROMMP(mp2))
  208.         {
  209.         case SB_LINELEFT: /* #defined same as SB_LINEUP */
  210.             sMove = -min( LINE_SIZE, usPos-usMin );
  211.             break;
  212.         case SB_PAGELEFT: /* #defined same as SB_PAGEUP */
  213.             sMove = -min( 8*LINE_SIZE, usPos-usMin );
  214.             break;
  215.         case SB_LINERIGHT:/* #defined same as SB_LINEDOWN */
  216.             sMove = +min( LINE_SIZE, usMax-usPos );
  217.             break;
  218.         case SB_PAGERIGHT:/* #defined same as SB_PAGEDOWN */
  219.             sMove = +min( 8*LINE_SIZE, usMax-usPos );
  220.             break;
  221.         case SB_SLIDERPOSITION:
  222.             sMove = SHORT1FROMMP(mp2) - usPos;
  223.             break;
  224.         default:
  225.             return 0L; /* Spurious commands occur under CodeView */
  226.         }
  227.         /* scroll the controls in the opposite direction to the scrollbar
  228.         */
  229.         if (SHORT1FROMMP(mp1)==FID_HORZSCROLL)
  230.             WinScrollWindow( WinWindowFromID( hwnd, FID_CLIENT ),
  231.                              -sMove, 0, NULL, NULL, NULL, &rclUpdate,
  232.                              SW_SCROLLCHILDREN | SW_INVALIDATERGN );
  233.         else
  234.             WinScrollWindow( WinWindowFromID( hwnd, FID_CLIENT ),
  235.                              0, +sMove, NULL, NULL, NULL, &rclUpdate,
  236.                              SW_SCROLLCHILDREN | SW_INVALIDATERGN );
  237.  
  238.         WinSendMsg( hctl, SBM_SETPOS, MPFROMSHORT( usPos+sMove ), 0L );
  239.         return 0L;
  240.     }
  241.     return WinDefDlgProc( hwnd, msg, mp1, mp2 );
  242. }
  243.  
  244. static void set_scrollbars( HWND hwnd,
  245.                             SHORT cxClient, SHORT cyClient );
  246.  
  247. /* internal routine to interpret tabs and cursor keys in the form */
  248. static void FormNavigate( HWND hwnd, USHORT usVKey );
  249.  
  250. /* Default window procedure for a form window */
  251. MRESULT EXPENTRY wpForm( HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2 )
  252. {
  253.     HPS hps;
  254.     RECTL rcl;
  255.  
  256.     switch( msg )
  257.     {
  258.     /* provides default action: no focus set */
  259.     case WM_INITDLG: return FALSE;
  260.  
  261.     case WM_CALCVALIDRECTS:
  262.         /* keep child control windows in the top left of the form
  263.             if and when it is resized */
  264.         return MRFROMSHORT( CVR_ALIGNTOP | CVR_ALIGNLEFT );
  265.  
  266.     case WM_CHAR:
  267.         {
  268.         USHORT usFlags = SHORT1FROMMP(mp1);
  269.  
  270.         if ( usFlags & KC_VIRTUALKEY && !(usFlags & KC_KEYUP) )
  271.             FormNavigate( hwnd, SHORT2FROMMP(mp2) );
  272.         break;
  273.         }
  274.  
  275.     case WM_COMMAND:
  276.         return 0L; /* unlike WinDefDlgProc() does not dismiss window */
  277.  
  278.     case WM_ERASEBACKGROUND:
  279.         return MRFROMSHORT(TRUE);
  280.  
  281.     case WM_FOCUSCHANGE:
  282.         /* control within form about to be given focus? */
  283.         if (!SHORT1FROMMP(mp2)
  284.             && WinQueryWindow( mp1, QW_PARENT, FALSE )==hwnd)
  285.         {
  286.             SHORT x=0, y=0;
  287.             SWP swp, swpCtl;
  288.             RECTL rclUpdate;
  289.             USHORT usPos;
  290.             HWND hctl;
  291.             HWND hwndFrame = WinQueryWindow( hwnd, QW_PARENT, FALSE );
  292.  
  293.             WinQueryWindowPos( mp1, &swpCtl );
  294.             WinQueryWindowPos( hwnd, &swp );
  295.  
  296.             if ( swpCtl.x < BORDER_SIZE )
  297.                 x = BORDER_SIZE - swpCtl.x;
  298.             else
  299.             if ( swpCtl.x + swpCtl.cx > swp.cx - BORDER_SIZE )
  300.                 x = swp.cx - BORDER_SIZE - swpCtl.x - swpCtl.cx;
  301.  
  302.             if ( swpCtl.y < BORDER_SIZE )
  303.                 y = BORDER_SIZE - swpCtl.y;
  304.             else
  305.             if ( swpCtl.y + swpCtl.cy > swp.cy - BORDER_SIZE )
  306.                 y = swp.cy - BORDER_SIZE - swpCtl.y - swpCtl.cy;
  307.  
  308.             if (x || y)
  309.                 WinScrollWindow( hwnd, x, y, NULL, NULL, NULL,
  310.                     &rclUpdate, SW_SCROLLCHILDREN | SW_INVALIDATERGN );
  311.             if (x)
  312.             {
  313.                 hctl = WinWindowFromID( hwndFrame, FID_HORZSCROLL );
  314.                 usPos = SHORT1FROMMR(
  315.                             WinSendMsg( hctl, SBM_QUERYPOS, 0L, 0L ) );
  316.                 WinSendMsg( hctl, SBM_SETPOS,
  317.                             MPFROMSHORT( usPos-x ), 0L );
  318.             }
  319.             if (y)
  320.             {
  321.                 hctl = WinWindowFromID( hwndFrame, FID_VERTSCROLL );
  322.                 usPos = SHORT1FROMMR(
  323.                             WinSendMsg( hctl, SBM_QUERYPOS, 0L, 0L ) );
  324.                 WinSendMsg( hctl, SBM_SETPOS,
  325.                             MPFROMSHORT( usPos+y ), 0L );
  326.             }
  327.  
  328.             /* Check the moved control fits in the form window.
  329.                If not, expand the form window.
  330.             */
  331.             WinQueryWindowPos( mp1, &swpCtl ); /* Find new location */
  332.  
  333.             x = max( 0, swpCtl.x + swpCtl.cx - swp.cx + BORDER_SIZE );
  334.             y = max( 0, swpCtl.y + swpCtl.cy - swp.cy + BORDER_SIZE );
  335.  
  336.             if (x || y)
  337.             {
  338.                 SWP swp;
  339.                 WinQueryWindowPos( hwndFrame, &swp );
  340.                 swp.cx += x;
  341.                 swp.cy += y;
  342.                 WinSetWindowPos( hwndFrame, NULL, 0, 0,
  343.                                  swp.cx, swp.cy, SWP_SIZE );
  344.             }
  345.         }
  346.         break; /* leave for WinDefDlgProc() */
  347.  
  348.     case WM_PAINT:
  349.         hps = WinBeginPaint( hwnd, NULL, NULL );
  350.         WinQueryWindowRect( hwnd, &rcl );
  351.         WinFillRect( hps, &rcl, SYSCLR_WINDOW );
  352.         WinEndPaint( hps );
  353.         return 0;
  354.  
  355.     case WM_SIZE:
  356.         if (WinQueryWindowPtr( hwnd, QWL_USER+sizeof(PVOID) ))
  357.             set_scrollbars( hwnd, SHORT1FROMMP(mp2), SHORT2FROMMP(mp2) );
  358.         return 0;
  359.  
  360.     case WM_DESTROY:
  361.         free( WinQueryWindowPtr( hwnd, QWL_USER+sizeof(PVOID) ) );
  362.         break;
  363.     }
  364.     return WinDefWindowProc( hwnd, msg, mp1, mp2 );
  365. }
  366.  
  367. /* Update the scroll bars ranges so user will be able to scroll
  368.    to parts of the form that will be outside the client area.
  369.  
  370.    The scroll bar range has two components:
  371.    the number of pels between the left of the form and the
  372.    left of the window, and the number of pels between the right
  373.    of the window and the right of the form.
  374. */
  375. static void set_scrollbars( HWND hwnd, SHORT cxClient, SHORT cyClient )
  376. {
  377.     SHORT cxForm, cyForm;
  378.     SHORT sPos, sPosNew, sRange;
  379.     RECTL rclUpdate;
  380.     HWND hwndFrame = WinQueryWindow( hwnd, QW_PARENT, FALSE );
  381.     HWND hctl = WinWindowFromID( hwndFrame, FID_HORZSCROLL );
  382.  
  383.     cxForm = WinQueryWindowUShort( hwnd, QWS_CXFORM );
  384.     cyForm = WinQueryWindowUShort( hwnd, QWS_CYFORM );
  385.  
  386.     sPos = SHORT1FROMMR( WinSendMsg( hctl, SBM_QUERYPOS, 0L, 0L ) );
  387.  
  388.     /* If the right of the window is beyond the right of the form
  389.        scroll to the left if possible.
  390.     */
  391.     if (sPos && sPos + cxClient > cxForm)
  392.     {
  393.         sPosNew = max( 0, cxForm - cxClient );
  394.         WinScrollWindow( hwnd, sPos-sPosNew, 0,
  395.                          NULL, NULL, NULL, &rclUpdate,
  396.                          SW_SCROLLCHILDREN );
  397.         WinInvalidateRegion( hwnd, NULL, TRUE );
  398.         sPos = sPosNew;
  399.         WinSendMsg( hctl, SBM_SETPOS, MPFROMSHORT( sPos ), 0L );
  400.     }
  401.     sRange = sPos + max( cxForm - cxClient - sPos, 0 );
  402.  
  403.     if (sRange)
  404.     {
  405.         WinEnableWindow( hctl, TRUE );
  406.         WinSendMsg( hctl, SBM_SETSCROLLBAR, MPFROMSHORT( sPos ),
  407.                     MPFROM2SHORT( 0, sRange ) );
  408.     }
  409.     else
  410.         WinEnableWindow( hctl, FALSE );
  411.  
  412.     hctl = WinWindowFromID( hwndFrame, FID_VERTSCROLL );
  413.     sPos = SHORT1FROMMR( WinSendMsg( hctl, SBM_QUERYPOS, 0L, 0L ) );
  414.     if (sPos && sPos + cyClient > cyForm)
  415.     {
  416.         sPosNew = max( 0, cyForm - cyClient );
  417.         WinScrollWindow( hwnd, 0, sPosNew-sPos,
  418.                          NULL, NULL, NULL, &rclUpdate,
  419.                          SW_SCROLLCHILDREN );
  420.         WinInvalidateRegion( hwnd, NULL, TRUE );
  421.         sPos = sPosNew;
  422.         WinSendMsg( hctl, SBM_SETPOS, MPFROMSHORT( sPos ), 0L );
  423.     }
  424.     sRange = sPos + max( cyForm - cyClient - sPos, 0 );
  425.     if (sRange)
  426.     {
  427.         WinEnableWindow( hctl, TRUE );
  428.         WinSendMsg( hctl, SBM_SETSCROLLBAR, MPFROMSHORT( sPos ),
  429.                     MPFROM2SHORT( 0, sRange ) );
  430.     }
  431.     else
  432.         WinEnableWindow( hctl, FALSE );
  433.     return;
  434. }
  435.  
  436. #define STYLE( hwnd ) WinQueryWindowULong( hwnd, QWL_STYLE )
  437.  
  438. static void FormNavigate( HWND hwnd, USHORT usVKey )
  439. {
  440.     HWND hctl = WinQueryFocus( HWND_DESKTOP, FALSE );
  441.  
  442.     switch( usVKey )
  443.     {
  444.     case VK_TAB:
  445.         do
  446.             hctl = FormEnumItem( hwnd, hctl, EDI_NEXTTABITEM, FALSE );
  447.         while (STYLE( hctl )& WS_DISABLED);
  448.         break;
  449.  
  450.     case VK_BACKTAB:
  451.         do
  452.             hctl = FormEnumItem( hwnd, hctl, EDI_PREVTABITEM, FALSE );
  453.         while (STYLE( hctl )& WS_DISABLED);
  454.         break;
  455.  
  456.     case VK_RIGHT:  case VK_DOWN:
  457.         do
  458.             hctl = FormEnumItem( hwnd, hctl, EDI_NEXTGROUPITEM, FALSE );
  459.         while (SHORT1FROMMR(WinSendMsg( hctl, WM_QUERYDLGCODE, 0, 0 ))
  460.                                                             & DLGC_STATIC
  461.                || STYLE( hctl )& WS_DISABLED);
  462.         break;
  463.  
  464.     case VK_LEFT:   case VK_UP:
  465.         do
  466.             hctl = FormEnumItem( hwnd, hctl, EDI_PREVGROUPITEM, FALSE );
  467.         while (SHORT1FROMMR(WinSendMsg( hctl, WM_QUERYDLGCODE, 0, 0 ))
  468.                                                             & DLGC_STATIC
  469.                || STYLE( hctl )& WS_DISABLED);
  470.         break;
  471.  
  472.     default: return;
  473.     }
  474.     WinSetFocus( HWND_DESKTOP, hctl );
  475.     return;
  476. }
  477.  
  478. HWND EXPENTRY FormEnumItem( HWND hwndForm, HWND hwnd,
  479.                             USHORT code, BOOL fLock )
  480. {
  481.     HWND *pahwnd = (HWND*)WinQueryWindowPtr( hwndForm,
  482.                                              QWL_USER+sizeof(PVOID) );
  483.     HWND *phwnd;
  484.  
  485.     if (code != EDI_FIRSTTABITEM && code != EDI_LASTTABITEM)
  486.     {
  487.         for (phwnd = pahwnd; *phwnd && *phwnd != hwnd; phwnd++){}
  488.         if (!*phwnd)
  489.             return NULL;
  490.     }
  491.  
  492.     switch( code )
  493.     {
  494.     case EDI_FIRSTTABITEM:
  495.         for (phwnd=pahwnd;
  496.             *phwnd && !(STYLE( *phwnd )& WS_TABSTOP);
  497.              phwnd++){}
  498.         break;
  499.  
  500.     case EDI_LASTTABITEM:
  501.         for (phwnd=pahwnd; *phwnd; phwnd++){}
  502.         while (--phwnd != pahwnd && !(STYLE( *phwnd )& WS_TABSTOP))
  503.         { /* do nothing */ }
  504.         break;
  505.  
  506.     case EDI_FIRSTGROUPITEM:
  507.         while (!(STYLE( *phwnd )& WS_GROUP) && --phwnd != pahwnd){}
  508.         break;
  509.  
  510.     case EDI_LASTGROUPITEM:
  511.         while (*++phwnd && !(STYLE( *phwnd )& WS_GROUP)){}
  512.         phwnd--;
  513.         break;
  514.  
  515.     case EDI_NEXTTABITEM:
  516.         do if (!*++phwnd)
  517.             phwnd = pahwnd;
  518.         while (!(STYLE( *phwnd )& WS_TABSTOP));
  519.         break;
  520.  
  521.     case EDI_PREVTABITEM:
  522.         do if (phwnd==pahwnd)
  523.             while (*++phwnd){}
  524.         while (!(STYLE( *--phwnd )& WS_TABSTOP));
  525.         break;
  526.  
  527.     case EDI_NEXTGROUPITEM:
  528.         if (!*++phwnd || STYLE( *phwnd )& WS_GROUP)
  529.             return FormEnumItem( hwndForm, *--phwnd,
  530.                                  EDI_FIRSTGROUPITEM, fLock );
  531.         break;
  532.  
  533.     case EDI_PREVGROUPITEM:
  534.         if (phwnd==pahwnd || STYLE( *phwnd )& WS_GROUP)
  535.             return FormEnumItem( hwndForm, *phwnd,
  536.                                  EDI_LASTGROUPITEM, fLock );
  537.         phwnd--;
  538.         break;
  539.     }
  540.     if (fLock)
  541.         WinLockWindow( *phwnd, fLock );
  542.     return *phwnd;
  543. }
  544.  
  545. /* Utility routine to fetch the latest PM error and
  546.    display it in a message box
  547. */
  548. void EXPENTRY ErrorInfoMessageBox( HAB hab, HWND hwnd, PSZ pszTitle )
  549. {
  550.     PERRINFO perrinfo = WinGetErrorInfo( hab );
  551.     if (perrinfo)
  552.     {
  553.         SEL sel = SELECTOROF( perrinfo );
  554.         PUSHORT aoffszMsg = MAKEP( sel, perrinfo->offaoffszMsg );
  555.  
  556.         WinAlarm( HWND_DESKTOP, WA_ERROR );
  557.         WinMessageBox( HWND_DESKTOP, hwnd, 
  558.                        MAKEP( sel, aoffszMsg[ 0 ] ),
  559.                        pszTitle, 0,
  560.                        MB_OK | MB_ICONEXCLAMATION | MB_MOVEABLE );
  561.         WinFreeErrorInfo( perrinfo );
  562.     }
  563.     return;
  564. }
  565. /*END*/
  566.  
  567.