home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1995 November / PCWK1195.iso / inne / podstawy / os2 / nakladki / pc2v190.exe / SOURCE.ZIP / Source / PC2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-01  |  167.1 KB  |  2,951 lines

  1. /***********************************************************************\
  2.  *                                PC2.c                                *
  3.  *           Copyright (C) by Stangl Roman, 1993, 1994, 1995           *
  4.  * This Code may be freely distributed, provided the Copyright isn't   *
  5.  * removed, under the conditions indicated in the documentation.       *
  6.  *                                                                     *
  7.  * PC/2 - Program Commander/2 is a configurable program starter for    *
  8.  * OS/2 2.x PM. If the user clicks button 1 on the DESKTOP, a user     *
  9.  * modifyable popup menu is displayed. The user then selects a program *
  10.  * to be started, or configuration of PC/2 or dismisses it.            *
  11.  * You can define Hotkeys to switch to a program, or start it if it is *
  12.  * not already running.                                                *
  13.  * PC/2 is an alternative method of starting programs compared to      *
  14.  * icons and uses no space on DESKTOP, and no folder must be opended   *
  15.  * to start a program. For frequently used programs, this reduces the  *
  16.  * time to start an application.                                       *
  17.  * PC/2 also implements an optional virtual Desktop and sliding focus. *
  18.  *                                                                     *
  19. \***********************************************************************/
  20.  
  21. static char RCSID[]="@(#) $Header: PC2.c/PC2.h Version 1.90 05,1995 $ (LBL)";
  22.  
  23. #define         _FILE_  "PC/2 - PC2.c V1.90"
  24.  
  25. #include        "PC2.h"                 /* User include files */
  26. #include        "Error.h"
  27.  
  28. HEV             hevPc2;                 /* Handle of PC/2 semaphore */
  29. UCHAR           *pucFilenameProfile;    /* The buffer holding the filename of the profile */
  30. UCHAR           *pucFilenameINI;        /* Path and filename of PC2.INI */
  31. UCHAR           *pucFilenameHLP;        /* The buffer holding the filename of the HLP file */
  32. UCHAR           *pucPathDLL;            /* Path to directory PC/2 was started from and DLLs reside */
  33. TID             tidThread=0;            /* Working thread ID */
  34. HMODULE         hDLLPc2Hook=NULLHANDLE; /* PC2HOOK.DLL module handle */
  35.                                         /* PC2SPOOL.DLL module handle */
  36. HMODULE         hDLLPc2Spooler=NULLHANDLE;
  37. PFNWP           pfnMenuWindowProc;      /* PC/2 smarticons menu window procedure saved for subclassing */
  38. PFNWP           pfnListboxWindowProc;   /* PC/2's setup dialog CDLB_MENUPROGRAM listbox window procedure
  39.                                            saved for subclassing */
  40. HDC             hdcMemory;              /* In memory Overview window DC */
  41.  
  42. /*                                                                                      *\
  43.  * Function pointer of functions loaded dynamically.                                    *
  44. \*                                                                                      */
  45. HOOKPARAMETERS  *pHP=NULL;              /* Pointer to PC/2 control structure loaded from PC2HOOK.DLL */
  46. PFFUNCPTR1      *pSetParameters;        /* Initialize PC2HOOK.DLL */
  47. PFFUNCPTR2      *pInputHook;            /* Input hook procedure */
  48. PFFUNCPTR3      *pWinSendMsgHook;       /* WinSendMsg() hook procedure */
  49. PFFUNCPTR5      *pSpoolerInitialize;    /* Initialize PC2SPOOL.DLL */
  50.  
  51. /*--------------------------------------------------------------------------------------*\
  52.  * The main procedure.                                                                  *
  53.  * Req:                                                                                 *
  54.  * Returns:                                                                             *
  55.  *      int ........... Exitcode (0, or errorlevel)                                     *
  56. \*--------------------------------------------------------------------------------------*/
  57. int main(void)
  58. {
  59. HAB     habPc2;                         /* Anchor block handle */
  60. HMQ     hmqPc2;                         /* Message queue handle */
  61. HWND    hwndFrame;                      /* Frame window handle, used until HOOKPARAMETERS got
  62.                                            loaded from PC2HOOK.DLL */
  63. HWND    hwndClient;                     /* Client window handle, same as hwndFrame */
  64. HWND    hwndHelp;                       /* Help window handle */
  65. ULONG   ulOverviewFCF;                  /* Frame creation flags */
  66. QMSG    qmsg;                           /* Message queue */
  67. UCHAR   ucModuleName[CCHMAXPATH];       /* PC/2's full qualified path name, PC/2 was started
  68.                                            from, obtained from PC/2's PIB */
  69. TIB     *ptib;                          /* PC/2's thread information block */
  70. PIB     *ppib;                          /* PC/2's process information block */
  71. APIRET  Rc;                             /* Dos_* calls return code */
  72.  
  73. /*                                                                                      *\
  74.  * Bring up an anchor block as soon as possible, to be able to display message boxes.   *
  75. \*                                                                                      */
  76. do
  77. {
  78.                                         /* Initialize anchor block and message queue */
  79.     if(WinStartUp(&habPc2, &hmqPc2)==FALSE)
  80.         break;
  81.     if(!WinRegisterClass(               /* Register window class */
  82.         habPc2,                         /* Handle of anchor block */
  83.         (PSZ)PC2_CLASSNAME,             /* Window class name */
  84.         (PFNWP)PC2_MainWindowProc,      /* Address of window procedure */
  85.                                         /* Class style */
  86.         CS_SIZEREDRAW | CS_SAVEBITS | CS_MOVENOTIFY,
  87.         0))                             /* Extra window words */
  88.         {
  89.         PM_ERR(habPc2, HWND_DESKTOP, HELP_CREATEWINDOW, MB_ERROR|MB_OK|MB_MOVEABLE|MB_DEFBUTTON1,
  90.             "Initializing the PM environment failed, PC/2 can't continue. You may have run out "\
  91.             "of resources, close some windows or applications and retry. Exiting...");
  92.         break;
  93.         }
  94. /*                                                                                      *\
  95.  * Start frame window, which creates window(s) and WM_CREATE message. Then associate    *
  96.  * a help instance with this window, to be able to display online help for message      *
  97.  * boxes.                                                                               *
  98. \*                                                                                      */
  99.     ulOverviewFCF=FCF_OVERVIEWWINDOW;
  100.     hwndFrame=WinCreateStdWindow(
  101.         HWND_DESKTOP,                   /* DESKTOP is parent */
  102.         0,                              /* Standard window styles */
  103.         &ulOverviewFCF,                 /* Frame control flags */
  104.         (PSZ)PC2_CLASSNAME,             /* Client window class name */
  105.         "",                             /* No window text */
  106.         0,                              /* No special class style */
  107.         (HMODULE)0,                     /* Ressource is in .EXE file */
  108.         ID_PC2MAINWINDOW,               /* Frame window identifier */
  109.         &hwndClient);                   /* Client window handle */
  110.     if(hwndFrame==NULLHANDLE)
  111.         {
  112.         PM_ERR(habPc2, HWND_DESKTOP, HELP_CREATEWINDOW, MB_ERROR|MB_OK|MB_MOVEABLE|MB_DEFBUTTON1,
  113.             "Creation of PC/2's Overview Window, PC/2 can't continue. You may have run out "\
  114.             "of resources, close some windows or applications and retry. Exiting...");
  115.         break;
  116.         }
  117. /*                                                                                      *\
  118.  * The only portable way to access the path PC/2 was invoked from, to access the        *
  119.  * command-line parameters and the environment is to query the process information      *
  120.  * block. Portable means that this is expected to work for OS/2 for PowerPC too.        *
  121. \*                                                                                      */
  122.     DosGetInfoBlocks(&ptib, &ppib);
  123.     DosQueryModuleName(ppib->pib_hmte, sizeof(ucModuleName), ucModuleName);
  124. /*                                                                                      *\
  125.  * Get the full path and filename of the running copy of PC/2 and change the extension  *
  126.  * .EXE into .cfg to open the configuration file under this name. If the user supplies  *
  127.  * [-,/Profile filename.ext] then use this filename as the Profile. Also change .EXE    *
  128.  * into .HLP, .INI and PC/2 directory as the current directory to access .DLL. All path *
  129.  * names contain the drive letter + directory PC/2 was started from.                    *
  130. \*                                                                                      */
  131.                                         /* Long enough to hold user Profile name */
  132.     pucFilenameProfile=calloc(CCHMAXPATH+1, sizeof(BYTE));
  133.     pucFilenameINI=calloc(CCHMAXPATH+1, sizeof(BYTE));
  134.     pucFilenameHLP=calloc(CCHMAXPATH+1, sizeof(BYTE));
  135.     pucPathDLL=calloc(CCHMAXPATH+1, sizeof(BYTE));
  136.                                         /* If no drive letter is available, e.g. using
  137.                                            SET RUNWORKPLACE=\PMAPPS\PC2.EXE, add current
  138.                                            drive, which is the boot drive */
  139.     if(ucModuleName[0]=='\\')
  140.         {
  141.         ULONG   ulDriveNumber;          /* Current logical drive */
  142.         ULONG   ulLogicalDriveMap;      /* Logical drive bitmapped flag */
  143.         UCHAR   ucCurrentDrive[3]="C:"; /* Current drive */
  144.  
  145.                                         /* Get current logical drive 1...A, 2...B, 3...C, ... */
  146.         if(!DosQueryCurrentDisk(&ulDriveNumber, &ulLogicalDriveMap))
  147.                                         /* On no error use current drive, otherwise assume C: */
  148.             ucCurrentDrive[0]=(CHAR)(--ulDriveNumber)+'A';
  149.                                         /* Add drive letter */
  150.         strcpy(pucFilenameProfile, ucCurrentDrive);
  151.         strcpy(pucFilenameINI, ucCurrentDrive);
  152.         strcpy(pucFilenameHLP, ucCurrentDrive);
  153.         strcpy(pucPathDLL, ucCurrentDrive);
  154.         }
  155.                                         /* Add the full qualified path PC/2 was started from,
  156.                                            which we queried from the process information block */
  157.     strcat(pucFilenameProfile, ucModuleName);
  158.     strcat(pucFilenameINI, ucModuleName);
  159.     strcat(pucFilenameHLP, ucModuleName);
  160.     strcat(pucPathDLL, ucModuleName);
  161.     strcpy(strchr(pucFilenameProfile, '.'), ".cfg");
  162.     strcpy(strchr(pucFilenameINI, '.'), ".ini");
  163.     strcpy(strchr(pucFilenameHLP, '.'), ".hlp");
  164.     strcpy(strrchr(pucPathDLL, '\\'), "");
  165. /*                                                                                      *\
  166.  * Now initilize Help, if it can't be initialized the we get no help but that's no      *
  167.  * reason to terminate.                                                                 *
  168. \*                                                                                      */
  169.     if(WinStartHelp(habPc2, pucFilenameHLP, &hwndHelp, hwndFrame)==FALSE)
  170.         PM_ERR(habPc2, hwndFrame, HELP_PC2HELP, MB_INFORMATION|MB_OK|MB_MOVEABLE|MB_DEFBUTTON1,
  171.             "Can't find or load from PC2.HLP, please check that PC2.HLP is in the directory you "\
  172.             "started PC/2 from. All help help requests will be ignored - continuing...");
  173. /*                                                                                      *\
  174.  * Check if we are allready loaded before by querying a semaphore that is defined the   *
  175.  * first time PC/2 runs.                                                                *
  176. \*                                                                                      */
  177.     if(DosCreateEventSem(               /* Create a semaphore */
  178.         PC2_SEM,                        /* Name */
  179.         &hevPc2,                        /* Handle */
  180.         (ULONG)0,                       /* Named semaphores are allways shared */
  181.         (BOOL32)FALSE))                 /* Initially set */
  182.         {                               /* If an error occurs, either we can't create
  183.                                            the semaphore or it allready exists. We assume
  184.                                            that it exists, meaning PC/2 allready loaded.
  185.                                            Parameters NULL force an immediate exit(1). */
  186.         USR_ERR(hwndFrame, HELP_PC2LOADED, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  187.             "One instance of PC/2 is already loaded - exiting...");
  188.         break;
  189.         }
  190. /*                                                                                      *\
  191.  * Load the Pc2Hook DLL either from the current directory or a LIBPATH path and         *
  192.  * obtain the addresses of the entrypoints. There seems to be a little bug?, when the   *
  193.  * library name contains a .DLL extension - the DLL is loaded sucessfully but not       *
  194.  * always correct initialized? and calling functions in the DLL tend to fail. Extension *
  195.  * .DLL therefore not appended to library name.                                         *
  196. \*                                                                                      */
  197.     {
  198.     UCHAR       ucBuffer[80];           /* Buffer for possible return codes from DLL loading */
  199.     UCHAR       ucDrive;
  200.     UCHAR       *pucRunWorkPlace;       /* Scan environment for active WPS process */
  201.     HMODULE     hDLLDOSCALL1;           /* Handle of DOSCALL1.DLL to get APIs for dynamically
  202.                                            changing the LIBPATH for WARP+ */
  203.     KEYDATA     *pKD;                   /* Used to clear KEYDATA table in PC2HOOL.DLL */
  204.     ULONG       ulIndex;                /* KEYDATA table index */
  205.  
  206.                                         /* Get drive of PC/2's startup directory
  207.                                            1=A, 2=B, 3=C,.... and direcotry itself */
  208.     ucDrive=tolower(pucPathDLL[0]);
  209.     DosSetDefaultDisk(++ucDrive-'a');
  210.     DosSetCurrentDir(pucPathDLL+2);
  211.     Rc=DosLoadModule(                   /* Load the DLL of PC/2 */
  212.         ucBuffer,                       /* Save failure there */
  213.         sizeof(ucBuffer)-1,             /* Length of save area */
  214.         "PC2Hook",                      /* Fully qualified Library name */
  215.         &hDLLPc2Hook);                   /* DLL module handle */
  216.     if(Rc!=NO_ERROR)
  217.         {                               /* DLL couldn't be found in the current PC/2
  218.                                            directory or via the LIBPATH path */
  219.         DOS_ERR(Rc, hwndFrame, HELP_DLLLOADED, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  220.             "PC/2 can't find PC2HOOK.DLL, please check that the DLL resides in the directory "\
  221.             "PC/2 was started from - exiting");
  222.         break;
  223.         }
  224.     Rc=DosQueryProcAddr(                /* Now get the address of the functions within the DLL */
  225.         hDLLPc2Hook,                    /* DLL module handle */
  226.         4,                              /* Ordinal number of procedure whose address is desired */
  227.         "HookParameters",               /* Procedure name being referenced */
  228.                                         /* Procedure address returned */
  229.         (PFN *)(&pHP));
  230.     if(Rc!=NO_ERROR)
  231.         {                               /* An error occured */
  232.         DosFreeModule(hDLLPc2Hook);     /* Free DLL reference */
  233.         DOS_ERR(Rc, hwndFrame, HELP_DLLLOADED, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  234.             "PC/2 can't load from PC2HOOK.DLL, maybe you are using an outdated version - exiting");
  235.         break;
  236.         }
  237.     memset(pHP, 0, sizeof(HOOKPARAMETERS));
  238.     pHP->hDLLPc2Hook=hDLLPc2Hook;
  239.     Rc=DosQueryProcAddr(hDLLPc2Hook, 1, "PC2DLL_SetParameters", (PFN *)(&pSetParameters));
  240.     if(Rc!=NO_ERROR)
  241.         {                               /* An error occured */
  242.         DosFreeModule(hDLLPc2Hook);     /* Free DLL reference */
  243.         DOS_ERR(Rc, hwndFrame, HELP_DLLLOADED, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  244.             "PC/2 can't load from PC2HOOK.DLL, maybe you are using an outdated version - exiting");
  245.         break;
  246.         }
  247.     Rc=DosQueryProcAddr(hDLLPc2Hook, 2, "PC2DLL_InputHook", (PFN *)(&pInputHook));
  248.     if(Rc!=NO_ERROR)
  249.         {                               /* An error occured */
  250.         DosFreeModule(hDLLPc2Hook);     /* Free DLL reference */
  251.         DOS_ERR(Rc, hwndFrame, HELP_DLLLOADED, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  252.             "PC/2 can't load from PC2HOOK.DLL, maybe you are using an outdated version - exiting");
  253.         break;
  254.         }
  255.     Rc=DosQueryProcAddr(hDLLPc2Hook, 3, "PC2DLL_WinSendMsgHook", (PFN *)(&pWinSendMsgHook));
  256.     if(Rc!=NO_ERROR)
  257.         {
  258.         DosFreeModule(hDLLPc2Hook);
  259.         DOS_ERR(Rc, hwndFrame, HELP_DLLLOADED, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  260.             "PC/2 can't load from PC2HOOK.DLL, maybe you are using an outdated version - exiting");
  261.         break;
  262.         }
  263.     Rc=DosQueryProcAddr(hDLLPc2Hook, 5, "KeyData", (PFN *)(&pHP->pKeyData));
  264.     if(Rc!=NO_ERROR)
  265.         {
  266.         DosFreeModule(hDLLPc2Hook);
  267.         DOS_ERR(Rc, hwndFrame, HELP_DLLLOADED, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  268.             "PC/2 can't load from PC2HOOK.DLL, maybe you are using an outdated version - exiting");
  269.         break;
  270.         }
  271. /*                                                                                      *\
  272.  * Initialize HookParameters structure.                                                 *
  273. \*                                                                                      */
  274.                                         /* Because PC2HOOK.DLL may already be loaded in the system,
  275.                                            we have to clear the Hotkeys already used from the previous
  276.                                            invokation of PC/2. E.g. IBM CM/2 does lock PC2HOOK.DLL,
  277.                                            for unknown reasons, even if PC/2 gets terminated, so
  278.                                            the next invokation may get uninitialized Hotkey data */
  279.     for(pKD=pHP->pKeyData, ulIndex=0; ulIndex<KEYDATACOUNT; ulIndex++, pKD++)
  280.         pKD->bUsed=FALSE;
  281.     pHP->pTib=ptib;                     /* Save thread and process information block of PC/2 */
  282.     pHP->pPib=ppib;
  283.     pHP->ProcessId=ppib->pib_ulpid;
  284.     pHP->PriorityClass=(USHORT)((UCHAR)(ptib->tib_ptib2->tib2_ulpri & (~0xFF)) >> 8);
  285.     pHP->PriorityDelta=(SHORT)((CHAR)(ptib->tib_ptib2->tib2_ulpri & 0xFF));
  286.     pHP->MenuDataId=ID_POPUPMENU;
  287.     pHP->habPc2=habPc2;
  288.     pHP->hmqPc2=hmqPc2;
  289.     pHP->hwndFrame=hwndFrame;
  290.     pHP->hwndClient=hwndClient;
  291.     pHP->hwndHelp=hwndHelp;
  292.     pHP->ulOverviewFCF=FCF_OVERVIEWWINDOW;
  293.     pHP->ulSpoolerFCF=FCF_SPOOLERWINDOW;
  294.     strcpy(pHP->ucPathDLL, pucPathDLL);
  295.     free(pucPathDLL);
  296.     strcpy(pHP->ucFilenameINI, pucFilenameINI);
  297.     free(pucFilenameINI);
  298.     pHP->DosSetExtLIBPATH=NULL;         /* First assume this new APIs are not available */
  299.     pHP->DosQueryExtLIBPATH=NULL;
  300.     pHP->hbmMemory=NULLHANDLE;          /* Clear in memory bitmp and presentation space handle */
  301.     pHP->hpsMemory=NULLHANDLE;
  302. /*                                                                                      *\
  303.  * Load DOSCALL1.998 DOSSETEXTLIBPATH and DOSCALL1.999 DOSQUERYEXTLIBPATH OS/2 APIs,    *
  304.  * which were newly introduced by OS/2 3.00 WARP. These APIs are use to set and query   *
  305.  * the EXTENDED LIBPATH, allowing to change the search path for dynamically loading     *
  306.  * DLLs. These settings are bound to the process's information block. Applications      *
  307.  * started by PC/2 may specify values for these variables, which are inherited from     *
  308.  * PC/2 after setting them before starting the application.                             *
  309. \*                                                                                      */
  310.     if(!DosLoadModule(ucBuffer, sizeof(ucBuffer)-1, "DOSCALL1", &hDLLDOSCALL1)!=NO_ERROR)
  311.         {
  312.         DosQueryProcAddr(hDLLDOSCALL1, 998, "DOSSETEXTLIBPATH", (PFN *)(&pHP->DosSetExtLIBPATH));
  313.         DosQueryProcAddr(hDLLDOSCALL1, 999, "DOSQUERYEXTLIBPATH", (PFN *)(&pHP->DosQueryExtLIBPATH));
  314.         }
  315.     DosFreeModule(hDLLDOSCALL1);
  316.                                         /* Clear extended LIBPATH to prevent inheritance
  317.                                            of unwanted data from a previous launch that set
  318.                                            the extended LIBPATH */
  319.     if(pHP->DosSetExtLIBPATH)
  320.         pHP->DosSetExtLIBPATH("", BEGINLIBPATH);
  321.     if(pHP->DosSetExtLIBPATH)
  322.         pHP->DosSetExtLIBPATH("", ENDLIBPATH);
  323. /*                                                                                      *\
  324.  * Load from PC2.INI and test for PC/2 running as a WPS replacement.                    *
  325. \*                                                                                      */
  326.                                         /* Get data from PC2.INI into HookParameters. Commandline
  327.                                            parameters may overload the settings in PC2.INI. */
  328.     if(!(INIAccess(pHP->ucFilenameINI, TRUE)))
  329.         USR_ERR(hwndFrame, HELP_PC2INI, MB_INFORMATION|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  330.             "Can't load configuration data from PC2.INI. "\
  331.             "Either you have started PC/2 for the first time, "\
  332.             "or you upgraded from a previous version of PC/2, or PC2.INI "\
  333.             "is defective. For unreadable data default values are assumed - continuing...");
  334.     pucRunWorkPlace=NULL;
  335.                                         /* Query environment for RUNWORKPLACE=[d:]path\PC2.EXE */
  336.     DosScanEnv("RUNWORKPLACE", (char **)&pucRunWorkPlace);
  337.                                         /* Find P of PC2.EXE (or PMSHELL.EXE) */
  338.     for( ; *pucRunWorkPlace; pucRunWorkPlace++)
  339.         {
  340.         if((*pucRunWorkPlace!='P') && (*pucRunWorkPlace!='p')) continue;
  341.                                         /* We found a P or p now look for PC2.EXE */
  342.         if(!strnicmp(pucRunWorkPlace, "PC2.EXE", 8))
  343.             {
  344.                                         /* If PC/2 is the WPS process save fact */
  345.             pHP->ulStatusFlag|=PC2RUNNINGASWPS;
  346.                                         /* PC/2 as WPS process should appear in Window List,
  347.                                            to enable the user to unhide PC/2 after hiding,
  348.                                            but can't be closed from Window List, Exit smart-
  349.                                            icon or Exit PC/2 menuentry. The door using the
  350.                                            accelerator F3 will stay open, but undokumented. */
  351.             }
  352.         }
  353. /*                                                                                      *\
  354.  * If PC/2 is running as the WPS process, get PC2SPOOL.DLL module handle.               *
  355. \*                                                                                      */
  356.     if((pHP->ulStatusFlag & PC2RUNNINGASWPS) || (pHP->ulStatusFlag & SHOWSPOOLERWINDOW))
  357.         {
  358.                                         /* Fully qualified path of PC2SPOOL.DLL */
  359.         Rc=DosLoadModule(ucBuffer, sizeof(ucBuffer)-1, "PC2Spool", &hDLLPc2Spooler);
  360.         if(Rc!=NO_ERROR)
  361.             {                           /* DLL couldn't be found in the current PC/2
  362.                                            directory or via the LIBPATH path */
  363.             DOS_ERR(Rc, hwndFrame, HELP_PC2SPOOLDLL, MB_INFORMATION|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  364.                 "PC/2 can't find PC2SPOOL.DLL, please check that the DLL resides in the directory "\
  365.                 "PC/2 was started from. Spooler window services will not be available - continuing...");
  366.                 pHP->ulStatusFlag&=(~PC2RUNNINGASWPS);
  367.             }
  368.         else
  369.             {
  370.             if(DosQueryProcAddr(hDLLPc2Spooler, 1, "SpoolerInitialize", (PFN *)(&pSpoolerInitialize))!=NO_ERROR)
  371.                 DosFreeModule(hDLLPc2Spooler);
  372.                 DOS_ERR(Rc, hwndFrame, HELP_PC2SPOOLDLL, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  373.                     "PC/2 can't load from PC2SPOOL.DLL, maybe you are using an outdated version - continuing...");
  374.             }
  375.         }
  376.     pHP->hDLLPc2Spooler=hDLLPc2Spooler;
  377.     }
  378. /*                                                                                      *\
  379.  * Now check possible commandline parameters, but read PC2.INI first to overwrite       *
  380.  * values read by commandline parameters. Again, the portable way to access them is via *
  381.  * the process information block.                                                       *
  382. \*                                                                                      */
  383.     {
  384.     UCHAR   *pucCommandline;            /* Pointer to the commandline passed during invokation */
  385.     UCHAR   *pucNextParameter;          /* Pointer to the current commandline parameter */
  386.                                         /* Default the Popup-Menu is displayed after a
  387.                                            WM_BUTTON1DBLCLK on the Desktop, but users may
  388.                                            prefer WM_BUTTON1CLICK to popup the menu, which
  389.                                            is set by default during reading PC2.INI, but
  390.                                            we never know if there is a chance to miss it,
  391.                                            so we explicitely test this */
  392.     if(pHP->ulClickFlag==0) pHP->ulClickFlag=WM_BUTTON1DBLCLK;
  393.                                         /* The commandline immediately follows the full qualified
  394.                                            path PC/2 was invoked  from */
  395.     pucCommandline=pHP->pPib->pib_pchcmd+strlen(pHP->pPib->pib_pchcmd)+1;
  396.     strupr(pucCommandline);             /* Uppercase commandline parameters, which are separated
  397.                                            by (one or more) spaces */
  398.                                         /* Loop as long commandline parameters are available */
  399.     for(pucNextParameter=pucCommandline;
  400.         (pucNextParameter && *pucNextParameter);
  401.         pucNextParameter=strchr(++pucNextParameter, ' '))
  402.         {
  403.                                         /* Skip spaces */
  404.         while(*pucNextParameter==' ') pucNextParameter++;
  405.                                         /* Test for /PROFILE or -PROFILE to get a
  406.                                            profile name */
  407.         if((strncmp(pucNextParameter, "/PROFILE", sizeof("/PROFILE")-1)==0) ||
  408.             (strncmp(pucNextParameter, "-PROFILE", sizeof("-PROFILE")-1)==0))
  409.             {
  410.             UCHAR   *pucProfile;
  411.             ULONG   ulCounter;
  412.  
  413.                                         /* Find next parameter */
  414.             pucNextParameter=strchr(pucNextParameter, ' ');
  415.             while(*pucNextParameter==' ')
  416.                 pucNextParameter++;
  417.             ulCounter=0;
  418.                                         /* For HPFS. the filename may be enclosed in quotes (") */
  419.             if(*pucNextParameter=='"')
  420.                 {
  421.                 pucNextParameter++;
  422.                 while((*(pucNextParameter+ulCounter)!='"') && (*(pucNextParameter+ulCounter)!='\0'))
  423.                     ulCounter++;
  424.                 }
  425.             else
  426.                 while((*(pucNextParameter+ulCounter)!=' ') && (*(pucNextParameter+ulCounter)!='\0'))
  427.                     ulCounter++;
  428.             pucProfile=pucFilenameProfile+strlen(pucFilenameProfile)-7;
  429.                                         /* Get the filename */
  430.             strncpy(pucProfile, pucNextParameter, ulCounter);
  431.             *(pucProfile+ulCounter)='\0';
  432.             pucNextParameter+=ulCounter;
  433.             continue;
  434.             }
  435.                                         /* Test for /INSTALL or -INSTALL to start the help
  436.                                            panels during initialization */
  437.         if((strncmp(pucNextParameter, "/INSTALL", sizeof("/INSTALL")-1)==0) ||
  438.             (strncmp(pucNextParameter, "-INSTALL", sizeof("-INSTALL")-1)==0))
  439.             pHP->ulStatusFlag |= INSTALLATIONHELP;
  440.                                         /* Test for /NOAUTOSTART or -NOAUTOSTART to start the help
  441.                                            panels during initialization */
  442.         if((strncmp(pucNextParameter, "/NOAUTOSTART", sizeof("/NOAUTOSTART")-1)==0) ||
  443.             (strncmp(pucNextParameter, "-NOAUTOSTART", sizeof("-NOAUTOSTART")-1)==0))
  444.             pHP->ulStatusFlag |= IGNOREAUTOSTART;
  445.                                         /* Test for /DOUBLECLICK or -DOUBLECLICK to display
  446.                                            the Popup-Menu after a double-click instead of
  447.                                            a single click */
  448.         if((strncmp(pucNextParameter, "/DOUBLECLICK", sizeof("/DOUBLECLICK")-1)==0) ||
  449.             (strncmp(pucNextParameter, "-DOUBLECLICK", sizeof("-DOUBLECLICK")-1)==0))
  450.             pHP->ulClickFlag=WM_BUTTON1DBLCLK;
  451.                                         /* Test for /SINGLECLICK or -SINGLECLICK to display
  452.                                            the Popup-Menu after a double-click instead of
  453.                                            a single click */
  454.         if((strncmp(pucNextParameter, "/SINGLECLICK", sizeof("/SINGLECLICK")-1)==0) ||
  455.             (strncmp(pucNextParameter, "-SINGLECLICK", sizeof("-SINGLECLICK")-1)==0))
  456.             pHP->ulClickFlag=WM_BUTTON1CLICK;
  457.         }
  458.     }
  459. /*                                                                                      *\
  460.  * Initialize PC2HOOK.DLL.                                                              *
  461. \*                                                                                      */
  462.     pSetParameters();                   /* Initialize data in PC2Hook DLL */
  463. /*                                                                                      *\
  464.  * Subclass smart icon menu bar to catch changes of presentation parameters.            *
  465. \*                                                                                      */
  466.                                         /* Get smarticon menu window handle */
  467.     pHP->hwndMenu=WinWindowFromID(pHP->hwndFrame, FID_MENU);
  468.                                         /* Subclass menu window procedure to get hold of
  469.                                            presentation parameter changes */
  470.     pfnMenuWindowProc=WinSubclassWindow(pHP->hwndMenu, SubclassedMenuWindowProc);
  471. /*                                                                                      *\
  472.  * Now setup the Popup-Menu by loading the data from the profile and install the hook   *
  473.  * into the system input queue, load the Popup-Menu while PC/2 startup.                 *
  474. \*                                                                                      */
  475.                                         /* Load popup menu */
  476.     pHP->hwndPopupMenu=WinLoadMenu(
  477.         pHP->hwndClient,                /* Owner window handle */
  478.         (HMODULE)0,                     /* Ressource in .EXE file */
  479.         ID_PC2POPUPMENU);               /* Menu identifier in ressource file */
  480.                                         /* Load the data from the profile */
  481.                                         /* Set this font also into the Popup-Menu */
  482.     WinSetPresParam(pHP->hwndPopupMenu, PP_FONTNAMESIZE,
  483.         sizeof(pHP->ucPopupMenuFont), pHP->ucPopupMenuFont);
  484.     WinPostMsg(pHP->hwndClient, WM_PC2STARTUP, NULL, NULL);
  485. /*                                                                                      *\
  486.  * Here we loop dispatching the messages...                                             *
  487. \*                                                                                      */
  488.     while(TRUE)
  489.         {
  490.         while(WinGetMsg(pHP->habPc2, &qmsg, 0, 0, 0))
  491.                                         /* Dispatch messages to window procedure */
  492.             WinDispatchMsg(pHP->habPc2, &qmsg);
  493.                                         /* Break only when one WM_QUIT occurred before, because
  494.                                            only after WM_CLOSE was processed we are really ready
  495.                                            to shutdown (and WM_CLOSE posts WM_QUIT again) */
  496.         if(pHP->ulStatusFlag & QUITFROMWINDOWLIST) break;
  497.                                         /* Dispatch WM_QUIT */
  498.         WinDispatchMsg(pHP->habPc2, &qmsg);
  499.                                         /* If PC/2 is running as a WPS replacement, tell the user
  500.                                            that the workplace process is always restarted */
  501.         if(pHP->ulStatusFlag&PC2RUNNINGASWPS)
  502.             USR_ERR(pHP->hwndFrame, HELP_PC2RUNNINGASWPS, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  503.                 "PC/2 is running as the WPS (WorkPlace Shell) process. The WorkPlace process mustn't be "\
  504.                 "terminated. PC/2 will ignore your request - continuing...");
  505.                                         /* If PC/2 is not running as a WPS replacement, or an
  506.                                            unrecoverable error occured, accept the shutdown request
  507.                                            but use WM_CLOSE to terminate in an orderly manner */
  508.         if((!(pHP->ulStatusFlag&PC2RUNNINGASWPS)) ||
  509.             (pHP->ulStatusFlag&PC2EMERGENCYEXIT))
  510.             {
  511.                                         /* Flag that we are using WM_CLOSE to make a clean shutdown */
  512.             pHP->ulStatusFlag|=QUITFROMWINDOWLIST;
  513.                                         /* Post WM_CLOSE to shutdown PC/2 in a orderly manner,
  514.                                            which posts WM_QUIT again afterwards PC/2 cleaned up */
  515.             WinPostMsg(pHP->hwndClient, WM_CLOSE, NULL, NULL);
  516.             }
  517.         }
  518.                                         /* Close window */
  519.     WinDestroyWindow(pHP->hwndFrame);
  520. } while (FALSE);
  521. free(pucFilenameProfile);               /* Free path references */
  522. free(pucFilenameHLP);
  523.                                         /* Wait for working thread to close */
  524. if(tidThread)
  525.     DosWaitThread(&tidThread, DCWW_WAIT);
  526. if(pHP!=NULL)
  527.     {
  528.                                         /* Destroy in memory bitmap */
  529.     if(pHP->hbmMemory) GpiDeleteBitmap(pHP->hbmMemory);
  530.                                         /* Destroy in memory presenation space */
  531.     if(pHP->hpsMemory) GpiDestroyPS(pHP->hpsMemory);
  532.                                         /* Destroy in memory device context */
  533.     if(hdcMemory) DevCloseDC(hdcMemory);
  534.     }
  535.                                         /* Free DLL references */
  536. if(hDLLPc2Spooler)
  537.     DosFreeModule(hDLLPc2Spooler);
  538. if(hDLLPc2Hook)
  539.     DosFreeModule(hDLLPc2Hook);
  540. if(WinCloseDown(&hwndHelp, &habPc2, &hmqPc2)==FALSE)
  541.     return(1);
  542. else
  543.     return(0);
  544. }
  545.  
  546. /*--------------------------------------------------------------------------------------*\
  547.  * This procedure is the PC/2 window procedure.                                         *
  548. \*--------------------------------------------------------------------------------------*/
  549. MRESULT EXPENTRY PC2_MainWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  550. {
  551. static ULONG    ulThreadReady=0;        /* Working thread ready indicator. It may be seen as (or
  552.                                            replaced by) a semaphore that is set (THREADBUSY) whenever the
  553.                                            working thread is doing his (time-consuming) work. Requests
  554.                                            for further work are ignored until the thread gets ready
  555.                                            again, to avoid queuing of work (which may be invalidated
  556.                                            by the last job) */
  557. static USHORT   usMenuCommand;          /* Last message from smarticons */
  558. static HPOINTER hPointerMove;           /* Pointer when moving windows on overview window */
  559. static HPOINTER hPointerAction;         /* Pointer when performing the other actions on windows */
  560. static USHORT   usTimer;                /* Timer identity used to update spooler regularily */
  561. static ULONG    ulTimerTicks=0;         /* Timer ticks (about every 5 seconds) */
  562.  
  563. switch(msg)
  564. {
  565. case WM_CREATE:                         /* Create window by WinCreateStdWindow() */
  566.                                         /* First call default window procedure */
  567.     WinDefWindowProc(hwnd, msg, mp1, mp2);
  568.     break;
  569.  
  570. case WM_PAINT:
  571.     {
  572.     HPS     hpsClient;
  573.     RECTL   rectlClient;
  574.  
  575.                                         /* Get a cached presentation space */
  576.     hpsClient=WinBeginPaint(hwnd, NULLHANDLE, &rectlClient);
  577.     if(pHP!=NULL)
  578.         WinPostMsg(pHP->hwndThread, WM_PAINT, NULL, NULL);
  579.     else
  580.         WinFillRect(hpsClient, &rectlClient, CLR_WHITE);
  581.     WinEndPaint(hpsClient);
  582.     }
  583.     break;
  584.  
  585. case WM_PRESPARAMCHANGED:
  586.     if((ULONG)mp1==PP_FONTNAMESIZE)
  587.         {
  588.         ULONG       ulAttrFound;
  589.         HPS         hpsClient;
  590.         FONTMETRICS fmPC2Font;
  591.         FATTRS      *pfatPC2Font;
  592.  
  593.                                         /* Get font selected for PC/2's overview window */
  594.         if(WinQueryPresParam(hwnd, PP_FONTNAMESIZE, 0, &ulAttrFound,
  595.             sizeof(pHP->ucPC2WindowFont), pHP->ucPC2WindowFont, 0))
  596.             {
  597.                                         /* Allocate FATTRS structure for overview window */
  598.             pfatPC2Font=(FATTRS *)malloc(sizeof(FATTRS));
  599.                                         /* Get new font */
  600.             hpsClient=WinGetPS(pHP->hwndClient);
  601.             GpiQueryFontMetrics(hpsClient, sizeof(FONTMETRICS), &fmPC2Font);
  602.             WinReleasePS(hpsClient);
  603.             pfatPC2Font->usRecordLength=sizeof(FATTRS);
  604.             pfatPC2Font->fsSelection=0;
  605.             pfatPC2Font->lMatch=0;
  606.             strcpy(pfatPC2Font->szFacename, fmPC2Font.szFacename);
  607.             pfatPC2Font->idRegistry=fmPC2Font.idRegistry;
  608.             pfatPC2Font->usCodePage=fmPC2Font.usCodePage;
  609.             if(fmPC2Font.fsDefn&FM_DEFN_OUTLINE)
  610.                 {
  611.                 pfatPC2Font->lMaxBaselineExt=fmPC2Font.lMaxBaselineExt;
  612.                 pfatPC2Font->lAveCharWidth=0;
  613.                 pfatPC2Font->fsType=0;
  614.                 pfatPC2Font->fsFontUse=FATTR_FONTUSE_OUTLINE|FATTR_FONTUSE_TRANSFORMABLE;
  615.                 }
  616.             else
  617.                 {
  618.                 pfatPC2Font->lMaxBaselineExt=fmPC2Font.lMaxBaselineExt;
  619.                 pfatPC2Font->lAveCharWidth=fmPC2Font.lAveCharWidth;
  620.                 pfatPC2Font->fsType=0;
  621.                 pfatPC2Font->fsFontUse=FATTR_FONTUSE_NOMIX;
  622.                 }
  623.                                         /* Post to working thread to change overview window font */
  624.             WinPostMsg(pHP->hwndThread, WM_PRESPARAMCHANGED,
  625.                 MPFROMLONG(PP_FONTNAMESIZE), MPFROMP(pfatPC2Font));
  626.             }
  627.         }
  628.     break;
  629.  
  630. /*                                                                                      *\
  631.  * Syntax: WM_PC2STARTUP, NULL, NULL                                                    *
  632. \*                                                                                      */
  633. case WM_PC2STARTUP:
  634. /*                                                                                      *\
  635.  * This message is posted during main window initialization, after the access to the    *
  636.  * HOOKPARAMETERS, loaded from PC2HOOK.DLL, is possible, to complete startup.           *
  637. \*                                                                                      */
  638.     {
  639.     SWP     swp;                        /* According to a tip from members of OS/2 development in
  640.                                            an article in OS/2 Developer magazine, it is more
  641.                                            efficient to call WinSetMultWindowPos(), because
  642.                                            WinSetWindowPost() internally calls WinSetMultWindowPos()
  643.                                            (even if just one window is moved) */
  644.     ULONG   ulChangedFCF=0;             /* Frame flags to adjust */
  645.  
  646.                                         /* Get the current screen resolution settings. We call
  647.                                            to avoid WM_SIZE/WM_MOVE messages to be processed in
  648.                                            between, because they rely on valid screen resolution data */
  649.     WinSendMsg(pHP->hwndClient, WM_PC2STARTUPSCREEN, NULL, NULL);
  650.                                         /* Setup a memory device contect to draw into, when
  651.                                            using a "fast" video subsystem, e.g. XGA-2 */
  652.     WinSendMsg(pHP->hwndClient, WM_PC2STARTUPMEMORYDC, NULL, NULL);
  653.                                         /* During initial creation, the (invisible) overview
  654.                                            window was created with a titlebar and smarticonbar.
  655.                                            If this assumption was false correct it */
  656.     if(pHP->ulStatusFlag&HIDETITLEBAR)
  657.         ulChangedFCF|=FCF_TITLEBAR|FCF_HIDEBUTTON;
  658.     if(pHP->ulStatusFlag&HIDESMARTICONBAR)
  659.         ulChangedFCF|=FCF_MENU;
  660.     if(ulChangedFCF)
  661.         WinSendMsg(pHP->hwndClient, WM_OVERVIEWFCF, MPFROMLONG(ulChangedFCF), NULL);
  662.                                         /* Load pointers from resource */
  663.     hPointerMove=WinLoadPointer(HWND_DESKTOP, NULLHANDLE, ID_MOVEPOINTER);
  664.     hPointerAction=WinLoadPointer(HWND_DESKTOP, NULLHANDLE, ID_ACTIONPOINTER);
  665.     usMenuCommand=ID_ICONMOVE;          /* Setup smarticon's control */
  666.     pHP->Windows.ulDesktop=(ULONG)-1;   /* Set to -1 because currently we don't know Desktop's name */
  667.                                         /* Set to -1 because currently we don't know Window List's name */
  668.     pHP->Windows.ulWindowList=(ULONG)-1;
  669.                                         /* For installation display help panels */
  670.     if(pHP->ulStatusFlag & INSTALLATIONHELP)
  671.         WinPostMsg(hwnd, WM_COMMAND, MPFROMSHORT(ID_HELP), NULL);
  672. /*                                                                                      *\
  673.  * Set text to titlebar. Size & position and ensurance that position is limited is      *
  674.  * done in WM_SIZE and WM_MOVE during window creation.                                  *
  675. \*                                                                                      */
  676.  
  677.     WinSetWindowText(pHP->hwndFrame, PC2_OVERVIEW_WINDOW);
  678.     swp.fl=SWP_HIDE|SWP_MOVE|SWP_SIZE|SWP_DEACTIVATE;
  679.     swp.x=pHP->swpPC2.x;
  680.     swp.y=pHP->swpPC2.y;
  681.     swp.cx=pHP->swpPC2.cx;
  682.     swp.cy=pHP->swpPC2.cy;
  683.     swp.hwndInsertBehind=NULLHANDLE;
  684.     swp.hwnd=pHP->hwndFrame;
  685.     WinSetMultWindowPos(pHP->habPc2, &swp, 1);
  686.                                         /* Create the working thread */
  687.     WinPostMsg(pHP->hwndClient, WM_PC2STARTUPTHREAD, NULL, NULL);
  688.                                         /* Now load the Popup-Menu from the profile */
  689.     WinPostMsg(pHP->hwndClient, WM_SETPOPUPMENU, NULL, NULL);
  690.                                         /* Now install the hook */
  691.     WinPostMsg(pHP->hwndClient, WM_LOADHOOK, NULL, NULL);
  692.     }
  693.     break;
  694.  
  695. /*                                                                                      *\
  696.  * Syntax: WM_PC2STARTUPSCREEN, NULL, NULL                                              *
  697. \*                                                                                      */
  698. case WM_PC2STARTUPSCREEN:
  699. /*                                                                                      *\
  700.  * Now get device resolution and save it and other initilization data to the structure  *
  701.  * used to communicate with PC2HOOK.DLL.                                                *
  702. \*                                                                                      */
  703.     {
  704.     POINTL  DesktopCountMin, DesktopCountMax;
  705.  
  706.                                         /* Query and save the device resolution, f.e.
  707.                                            1024 * 768 */
  708.     pHP->DesktopSize.x=pHP->swpScreen.cx=WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);
  709.     pHP->DesktopSize.y=pHP->swpScreen.cy=WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
  710.                                         /* Get number of horizontal and vertical Virtual Desktops */
  711.     DesktopCountMin.x=(pHP->ulHorizontalDesktops-1)>>1;
  712.     DesktopCountMin.y=(pHP->ulVerticalDesktops-1)>>1;
  713.     DesktopCountMax.x=pHP->ulHorizontalDesktops-DesktopCountMin.x;
  714.     DesktopCountMax.y=pHP->ulVerticalDesktops-DesktopCountMin.y;
  715.                                         /* Now initialized the virtual Desktop data */
  716.     pHP->LLHotBorder.x=pHP->DesktopSize.x*0.15;
  717.     pHP->LLHotBorder.y=pHP->DesktopSize.y*0.15;
  718.     pHP->URHotBorder.x=pHP->DesktopSize.x*0.85;
  719.     pHP->URHotBorder.y=pHP->DesktopSize.y*0.85;
  720.     pHP->VirtualDesktopPos.x=0;
  721.     pHP->VirtualDesktopPos.y=0;
  722.                                         /* Calculate absolute lower left and upper right
  723.                                            corners of the Virtual Desktop */
  724.     pHP->VirtualDesktopMin.x=(-pHP->DesktopSize.x)*DesktopCountMin.x;
  725.     pHP->VirtualDesktopMin.y=(-pHP->DesktopSize.y)*DesktopCountMin.y;
  726.     pHP->VirtualDesktopMax.x=pHP->DesktopSize.x*DesktopCountMax.x;
  727.     pHP->VirtualDesktopMax.y=pHP->DesktopSize.y*DesktopCountMax.y;
  728.     pHP->SlidingXFactor=(pHP->ulScrollPercentage*
  729.         pHP->DesktopSize.x)/100;
  730.     pHP->SlidingYFactor=(pHP->ulScrollPercentage*
  731.         pHP->DesktopSize.y)/100;
  732.     pHP->hwndDesktop=0;     /* Initialize to a value not used by PM (hopefully) */
  733.     pHP->hwndWPS=0;
  734.     }
  735.     break;
  736.  
  737. /*                                                                                      *\
  738.  * Syntax: WM_PC2STARTUPMEMORYDC, NULL, NULL                                            *
  739. \*                                                                                      */
  740. case WM_PC2STARTUPMEMORYDC:
  741. /*                                                                                      *\
  742.  * Now setup a memory device context and associate a presentation space into. Into this *
  743.  * presentation sapce we draw the overview window and clip it into the client area      *
  744.  * presenation space after all drawing was finished. This avoids the flickering in the  *
  745.  * Overview window caused that we draw all windows from the bottom to the top of the    *
  746.  * Z-order which causes windows to be drawn and overlayed immediately afterwards by     *
  747.  * another window.                                                                      *
  748. \*                                                                                      */
  749.     {
  750.     SIZEL   sizelClient;
  751.  
  752.                                             /* I'm not sure, why we can create a zero sized
  753.                                                presentation space, but draw any sized graphics
  754.                                                into, possibly because we associate a bitmap
  755.                                                to the presentation space, which is sized
  756.                                                according to the client area size. */
  757.     sizelClient.cx=0;
  758.     sizelClient.cy=0;
  759.                                             /* Get memory device context */
  760.     hdcMemory=DevOpenDC(pHP->habPc2, OD_MEMORY, "*", 0L, NULL, 0L);
  761.                                             /* Create a presentation space into the memory device context */
  762.     pHP->hpsMemory=GpiCreatePS(pHP->habPc2, hdcMemory, &sizelClient, PU_PELS|GPIA_ASSOC);
  763.                                             /* Change color table into RGB mode */
  764.     GpiCreateLogColorTable(pHP->hpsMemory, 0L, LCOLF_RGB, 0L, 0L, NULL);
  765.     }
  766.     break;
  767.  
  768. /*                                                                                      *\
  769.  * Syntax: WM_PC2STARTUPTHREAD, NULL, NULL                                              *
  770. \*                                                                                      */
  771. case WM_PC2STARTUPTHREAD:
  772. /*                                                                                      *\
  773.  * Now that frame and client windows are created (of course invisible), start working   *
  774.  * thread that opens a presentation space on the client area and performs all timely    *
  775.  * intensive tasks.                                                                     *
  776. \*                                                                                      */
  777.                                         /* Start working thread */
  778.     tidThread=_beginthread(PC2_Thread, NULL, 65536, NULL);
  779.     if(tidThread==(TID)-1)
  780.         {
  781.         USR_ERR(pHP->hwndFrame, HELP_PC2THREAD, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  782.             "PC/2 can't start its working thread - exiting...");
  783.                                         /* Post exit message */
  784.         pHP->ulStatusFlag|=PC2EMERGENCYEXIT;
  785.         WinPostMsg(pHP->hwndFrame, WM_QUIT, NULL, NULL);
  786.         }
  787.     break;
  788.  
  789. /*                                                                                      *\
  790.  * Syntax: WM_OVERVIEWFCF, ULONG ulChangedFCF, NULL                                     *
  791. \*                                                                                      */
  792. case WM_OVERVIEWFCF:
  793. /*                                                                                      *\
  794.  * This message is posted by Desktop configuration dialog to change the titlebar and    *
  795.  * menubar of PC/2 according to the user selection.                                     *
  796. \*                                                                                      */
  797.     {
  798.     ULONG   ulChangedFCF;
  799.  
  800.     ulChangedFCF=(ULONG)LONGFROMMP(mp1);
  801.                                         /* Update titlebar + hide button only if changed */
  802.     if(ulChangedFCF&(FCF_TITLEBAR|FCF_HIDEBUTTON))
  803.         {
  804.         if(pHP->ulOverviewFCF&(FCF_TITLEBAR|FCF_HIDEBUTTON))
  805.             {
  806.             FRAMECDATA  framecdataOverview;
  807.  
  808.             if(pHP->ulDebug>=DEBUG_FULL)
  809.                 printf("PC2: WM_OVERVIEWFCF creating titlebar\n");
  810.                                         /* Setup frame control data to create a titlebar and a
  811.                                            hide button */
  812.             framecdataOverview.cb=sizeof(FRAMECDATA);
  813.             framecdataOverview.flCreateFlags=FCF_TITLEBAR|FCF_HIDEBUTTON;
  814.             framecdataOverview.hmodResources=NULLHANDLE;
  815.             framecdataOverview.idResources=ID_PC2MAINWINDOW;
  816.             WinCreateFrameControls(pHP->hwndFrame, &framecdataOverview, NULL);
  817.             }
  818.         else
  819.             {                           /* If titlebar and hide button are no longer requested,
  820.                                            destroy them */
  821.             if(pHP->ulDebug>=DEBUG_FULL)
  822.                 printf("PC2: WM_OVERVIEWFCF destroying titlebar\n");
  823.             WinDestroyWindow(WinWindowFromID(pHP->hwndFrame, FID_TITLEBAR));
  824.             WinDestroyWindow(WinWindowFromID(pHP->hwndFrame, FID_MINMAX));
  825.             }
  826.         }
  827.                                         /* Update Smart Icon menubar only if changed */
  828.     if(ulChangedFCF&(FCF_MENU))
  829.         {
  830.         if(pHP->ulOverviewFCF&FCF_MENU)
  831.             {
  832.             if(pHP->ulDebug>=DEBUG_FULL)
  833.                 printf("PC2: WM_OVERVIEWFCF creating smarticonbar\n");
  834.             pHP->hwndMenu=WinLoadMenu(pHP->hwndFrame, NULLHANDLE, ID_PC2MAINWINDOW);
  835.             }
  836.         else
  837.             {                           /* If menubar is no longer requested, destroy it */
  838.             if(pHP->ulDebug>=DEBUG_FULL)
  839.                 printf("PC2: WM_OVERVIEWFCF destroying smarticonbar\n");
  840.             WinDestroyWindow(WinWindowFromID(pHP->hwndFrame, FID_MENU));
  841.             }
  842.         }
  843.                                         /* Force a redraw */
  844.     if(ulChangedFCF)
  845.         {
  846.         WinSendMsg(pHP->hwndFrame, WM_UPDATEFRAME, MPFROMLONG(ulChangedFCF), NULL);
  847.         WinSetWindowText(pHP->hwndFrame, PC2_OVERVIEW_WINDOW);
  848.         }
  849.     }
  850.     break;
  851.  
  852. /*                                                                                      *\
  853.  * Syntax: WM_THREADSTARTUPREADY, NULL, NULL                                            *
  854. \*                                                                                      */
  855. case WM_THREADSTARTUPREADY:
  856. /*                                                                                      *\
  857.  * Working thread is now ready, make overview window visible when requested.            *
  858. \*                                                                                      */
  859.     if(pHP->ulDebug>=DEBUG_FULL)
  860.         printf("PC2: WM_THREADSTARTUPREADY\n");
  861.                                         /* Set overview window font */
  862.     WinSetPresParam(pHP->hwndClient, PP_FONTNAMESIZE,
  863.         sizeof(pHP->ucPC2WindowFont), pHP->ucPC2WindowFont);
  864.                                         /* Display overview window if requested */
  865.     if(pHP->ulStatusFlag & OVERVIEW)
  866.         {
  867.         SWP     swp;
  868.  
  869.         swp.fl=SWP_SHOW|SWP_DEACTIVATE|SWP_ZORDER;
  870.         swp.x=swp.y=swp.cx=swp.cy=0;
  871.         swp.hwndInsertBehind=HWND_TOP;
  872.         swp.hwnd=pHP->hwndFrame;
  873.         WinSetMultWindowPos(pHP->habPc2, &swp, 1);
  874.         WinPostMsg(pHP->hwndThread, WM_PAINT, NULL, NULL);
  875.         }
  876.                                         /* Scan for autostart applications, however if the user
  877.                                            has for some reason disabled the general setting,
  878.                                            don't autostart the sessions, even when autostart
  879.                                            was selected */
  880.     if(!(pHP->ulStatusFlag&IGNOREAUTOSTART))
  881.         WinPostMsg(pHP->hwndThread, WM_AUTOSTART, NULL, NULL);
  882.     if(pHP->ulDebug>=DEBUG_FULL)
  883.         printf("PC2: Sending WM_AUTOSTART\n");
  884.                                         /* Create a timer to update the spooler container regularily */
  885.     for(usTimer=0; usTimer<=TID_USERMAX; usTimer++)
  886.         if(WinStartTimer(pHP->habPc2, hwnd, usTimer, 1000*10)) break;
  887.     break;
  888.  
  889. /*                                                                                      *\
  890.  * Syntax: WM_SETPOPUPMENU, NULL, NULL                                                  *
  891. \*                                                                                      */
  892. case WM_SETPOPUPMENU:
  893. /*                                                                                      *\
  894.  * Open the profile for reading the linked list containing the popup menu data. If the  *
  895.  * profile can't be opened, the file is assumed to be empty so the popup menu is empty. *
  896. \*                                                                                      */
  897.     {
  898.     ULONG   ulSpoolerError;
  899.  
  900.                                         /* Assume the Configuration Dialog isn't part
  901.                                            of the profile */
  902.     pHP->ulStatusFlag|=DISPLAYCONFIGDIALOG;
  903.     if((pHP->Pc2Profile=fopen(pucFilenameProfile, "r"))==NULL)
  904.         {
  905.                                         /* Allocate an empty MENUDATA structure used as
  906.                                            the first element of linked list */
  907.         pHP->pPopupMenu=AllocateMenuData();
  908.         USR_ERR(pHP->hwndFrame, HELP_PC2CFG, MB_INFORMATION|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  909.             "Cannot open confguration file - assuming empty file");
  910.         }
  911.     else
  912.         {
  913.         static UCHAR    Buffer[256];
  914.  
  915.                                         /* Allocate an empty MENUDATA structure used as
  916.                                            the first element of linked list */
  917.         pHP->pPopupMenu=AllocateMenuData();
  918.         fgets(Buffer, sizeof(Buffer), pHP->Pc2Profile);
  919.         while(!feof(pHP->Pc2Profile))
  920.             {
  921.             if(strstr(Buffer, "PROFILE START"))
  922.                 {
  923.                                         /* Load the rest by calling a recursive procedure */
  924.                 LoadMenu(pHP->pPopupMenu);
  925.                 break;
  926.                 }
  927.             fgets(Buffer, sizeof(Buffer), pHP->Pc2Profile);
  928.             }
  929.         fclose(pHP->Pc2Profile);
  930.         if(pHP->pPopupMenu->Item==ENTRYEMPTY)
  931.             USR_ERR(pHP->hwndFrame, HELP_PC2CFG, MB_INFORMATION|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  932.                 "Configuration file does not contain valid data - assuming empty file");
  933.         }
  934.                                         /* Initialize *MENUDATA for Configuration dialog
  935.                                            procedure to a known value */
  936.     pHP->pMenuData=pHP->pPopupMenu;
  937.                                         /* If PC/2 is the WPS process or user requested
  938.                                            to have the spooler window always, initialize spooler */
  939.     if(pSpoolerInitialize==0)
  940.         pHP->ulStatusFlag|=PC2SPOOLERERROR;
  941.     if(((pHP->ulStatusFlag & PC2RUNNINGASWPS) || (pHP->ulStatusFlag & SHOWSPOOLERWINDOW)) &&
  942.         !(pHP->ulStatusFlag & PC2SPOOLERERROR))
  943.                                         /* Initialize the spooler */
  944.         if((ulSpoolerError=pSpoolerInitialize(pHP->hDLLPc2Hook))!=NO_ERROR)
  945.             {
  946.             UCHAR   ucSpoolerError[256];
  947.  
  948.             sprintf(ucSpoolerError, "Initialisation of the OS/2 Spooler failed. Because the "\
  949.                 "Spooler PMSPL.DLL is part of any OS/2 installation, your installation may be"\
  950.                 "defective. The error code is %04X. - Continuing without PC/2 Spooler Control "\
  951.                 "Window...", (int)ulSpoolerError);
  952.             USR_ERR(pHP->hwndFrame, HELP_PC2SPOOLDLL, MB_INFORMATION|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  953.                 ucSpoolerError);
  954.             pHP->ulStatusFlag|=PC2SPOOLERERROR;
  955.                                         /* Release spooler DLL */
  956.             DosFreeModule(hDLLPc2Spooler);
  957.             hDLLPc2Spooler=NULLHANDLE;
  958.             }
  959.                                         /* If PC/2 is not the WPS process and Spooler is not enabled,
  960.                                            or available, disable spooler menu item in Popup Menu */
  961.     if(((!(pHP->ulStatusFlag & PC2RUNNINGASWPS)) && (!(pHP->ulStatusFlag & SHOWSPOOLERWINDOW))) ||
  962.         (pHP->ulStatusFlag & PC2SPOOLERERROR))
  963.         WinSendMsg(pHP->hwndPopupMenu, MM_SETITEMATTR,
  964.             MPFROM2SHORT(ID_SPOOLER, TRUE), MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED));
  965.                                         /* If PC/2 is running as the WPS, then disable the Exit
  966.                                            smarticon, and the Exit OS/2 menuentry */
  967.     if(pHP->ulStatusFlag&PC2RUNNINGASWPS)
  968.         {
  969.         WinSendMsg(pHP->hwndPopupMenu, MM_SETITEMATTR,
  970.             MPFROM2SHORT(ID_EXIT, TRUE), MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED));
  971.         WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  972.             MPFROM2SHORT(ID_ICONEXIT, TRUE), MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED));
  973.         }
  974.     }
  975.     break;
  976.  
  977. /*                                                                                      *\
  978.  * Syntax: WM_LOADHOOK, NULL, NULL                                                      *
  979. \*                                                                                      */
  980. case WM_LOADHOOK:
  981. /*                                                                                      *\
  982.  * Install the hook into the system input queue pointing to the PC2DLL_Hook() procedure *
  983.  * in the DLL PC2HOOK.DLL. If we can't do this we exit after an error message box.      *
  984. \*                                                                                      */
  985.     if(WinSetHook(                      /* Set a hook */
  986.         pHP->habPc2,                    /* Handle of anchor block */
  987.         NULLHANDLE,                     /* Hook into system message queue */
  988.         HK_INPUT,                       /* Hook of system input queue */
  989.         (PFN)pInputHook,                /* Pointer to hook procedure */
  990.         pHP->hDLLPc2Hook)==FALSE)
  991.         {
  992.         USR_ERR(pHP->hwndFrame, HELP_PC2HOOK, MB_INFORMATION|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  993.             "Hooking the system input queue failed - exiting...");
  994.         pHP->ulStatusFlag|=PC2EMERGENCYEXIT;
  995.         WinPostMsg(hwnd, WM_QUIT, NULL, NULL);
  996.         }
  997.                                         /* Hook of WinSendMsg() function when calling an
  998.                                            application's window procedure */
  999.     if(WinSetHook(pHP->habPc2, NULLHANDLE, HK_SENDMSG, (PFN)pWinSendMsgHook,
  1000.         pHP->hDLLPc2Hook)==FALSE)
  1001.         {
  1002.         USR_ERR(pHP->hwndFrame, HELP_PC2HOOK, MB_INFORMATION|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  1003.             "Hooking the system sendmessage dispatcher failed - exiting...");
  1004.         pHP->ulStatusFlag|=PC2EMERGENCYEXIT;
  1005.         WinPostMsg(hwnd, WM_QUIT, NULL, NULL);
  1006.         }
  1007.     break;
  1008.  
  1009. case WM_SIZE:
  1010. case WM_MOVE:
  1011. /*                                                                                      *\
  1012.  * One fundamental assumption is, that when getting here, the HOOKPARAMETERS structure  *
  1013.  * already contains the screen resolution data. Its therefore most important to call    *
  1014.  * WM_PC2STARTUPSCREEN using the synchronous WinSendMsg() in WM_PC2STARTUP, otherwise   *
  1015.  * we get a 0 sized Overview window.                                                    *
  1016. \*                                                                                      */
  1017.     {
  1018.     LONG    lXBorder=WinQuerySysValue(HWND_DESKTOP, SV_CXSIZEBORDER);
  1019.     LONG    lYBorder=WinQuerySysValue(HWND_DESKTOP, SV_CYSIZEBORDER);
  1020.     SWP     swpPC2;
  1021.  
  1022.                                         /* Get the frame area size */
  1023.     WinQueryWindowPos(pHP->hwndFrame, &swpPC2);
  1024.     if((swpPC2.cx>pHP->DesktopSize.x) || (pHP->swpPC2.cx<=0))
  1025.         pHP->swpPC2.cx=pHP->DesktopSize.x/3;
  1026.     else
  1027.         pHP->swpPC2.cx=swpPC2.cx;
  1028.     if((swpPC2.cy>pHP->DesktopSize.y) || (pHP->swpPC2.cy<=0))
  1029.         pHP->swpPC2.cy=pHP->DesktopSize.y/4;
  1030.     else
  1031.         pHP->swpPC2.cy=swpPC2.cy;
  1032.     if((swpPC2.x<(0-lXBorder)) ||
  1033.         ((swpPC2.x+pHP->swpPC2.cx)>(pHP->DesktopSize.x+lXBorder)))
  1034.         pHP->swpPC2.x=0;
  1035.     else
  1036.         pHP->swpPC2.x=swpPC2.x;
  1037.     if((swpPC2.y<(0-lYBorder)) ||
  1038.         ((swpPC2.y+pHP->swpPC2.cy)>(pHP->DesktopSize.y+lYBorder)))
  1039.         pHP->swpPC2.y=0;
  1040.     else
  1041.         pHP->swpPC2.y=swpPC2.y;
  1042.     pHP->swpPC2.fl=SWP_SIZE|SWP_MOVE;
  1043.     pHP->swpPC2.hwndInsertBehind=NULLHANDLE;
  1044.     pHP->swpPC2.hwnd=pHP->hwndFrame;
  1045.     WinSetMultWindowPos(pHP->habPc2, &pHP->swpPC2, 1);
  1046.                                         /* Get the client area size */
  1047.     WinQueryWindowPos(pHP->hwndClient, &pHP->swpPC2Client);
  1048.                                         /* Request repaint from working thread when resized */
  1049.     if(msg==WM_SIZE)
  1050.         {
  1051.  
  1052.         BITMAPINFOHEADER2   bmihMemory; /* In memory Overview window bitmap info header */
  1053.                                         /* Bitmapformat of current display & driver combination */
  1054.         LONG                lBitmapFormat[2];
  1055.  
  1056.                                         /* Get the number of planes and the bitcount of the current
  1057.                                            display & driver combination */
  1058.         GpiQueryDeviceBitmapFormats(pHP->hpsMemory, 2L, lBitmapFormat);
  1059.                                         /* Create a bitmap into the memory presentation space of same
  1060.                                            size as client area */
  1061.         memset(&bmihMemory, 0, sizeof(BITMAPINFOHEADER2));
  1062.         bmihMemory.cbFix=sizeof(BITMAPINFOHEADER2);
  1063.         bmihMemory.cx=pHP->swpPC2Client.cx;
  1064.         bmihMemory.cy=pHP->swpPC2Client.cy;
  1065.         bmihMemory.cPlanes=lBitmapFormat[0];
  1066.         bmihMemory.cBitCount=lBitmapFormat[1];
  1067.                                         /* Delete previous bitmap if it exists */
  1068.         if(pHP->hbmMemory)
  1069.             GpiDeleteBitmap(pHP->hbmMemory);
  1070.         pHP->hbmMemory=GpiCreateBitmap(pHP->hpsMemory, &bmihMemory, 0L, NULL, NULL);
  1071.         GpiSetBitmap(pHP->hpsMemory, pHP->hbmMemory);
  1072.                                         /* Now repaint Overview window */
  1073.         WinPostMsg(pHP->hwndThread, WM_PAINT, NULL, NULL);
  1074.         }
  1075.     }
  1076.     break;
  1077.  
  1078. /*                                                                                      *\
  1079.  * Syntax: WM_MOUSEMOVE, NULL, NULL                                                     *
  1080. \*                                                                                      */
  1081. case WM_MOUSEMOVE:                      /* Set mouse pointer accordingly to last smarticon pressed */
  1082.     if(usMenuCommand==ID_ICONMOVE) WinSetPointer(HWND_DESKTOP, hPointerMove);
  1083.     else WinSetPointer(HWND_DESKTOP, hPointerAction);
  1084.     break;
  1085.  
  1086. /*                                                                                      *\
  1087.  * Syntax: WM_HOTKEY, (USHORT usFlags, USHORT usCh), ULONG ulKeyDataIndex               *
  1088. \*                                                                                      */
  1089. case WM_HOTKEY:
  1090.                                         /* Get the currently active top level window */
  1091.     pHP->hwndActiveWindow=WinQueryActiveWindow(HWND_DESKTOP);
  1092.                                         /* Just pass this message to the working thread */
  1093.     WinPostMsg(pHP->hwndThread, WM_HOTKEY, MPFROMLONG(mp1), MPFROMLONG(mp2));
  1094.     break;
  1095.  
  1096. case WM_TIMER:
  1097. /*                                                                                      *\
  1098.  * The regular timer is used to refresh the container window, to have a real time       *
  1099.  * status for the spooler option and to query the WPS window handle regularily.         *
  1100. \*                                                                                      */
  1101.     {
  1102.     if(pHP->ulDebug>=DEBUG_LOW)
  1103.         {
  1104.         DosBeep(1000,20);
  1105.         DosBeep(250,20);
  1106.         DosBeep(1000,20);
  1107.         }
  1108.                                         /* Post message to query Desktop's/PM window handle */
  1109.     WinPostMsg(pHP->hwndThread, WM_SETDESKTOPHANDLE, NULL, NULL);
  1110.     if(++ulTimerTicks>=6)
  1111.         {
  1112.                                         /* Post message to refresh spooler */
  1113.         WinPostMsg(pHP->hwndSpooler, WM_REFRESHSPOOLER, NULL, NULL);
  1114.         ulTimerTicks=0;
  1115.         }
  1116.     }
  1117.     break;
  1118.  
  1119. case WM_BUTTON1DOWN:
  1120.     return((MRESULT)TRUE);              /* Avoid default window procedure which sets the
  1121.                                            focus to PC/2 */
  1122.  
  1123. /*                                                                                      *\
  1124.  * Syntax: WM_BUTTON2DOWN, (LONG lClickX), (LONG lClickY)                               *
  1125. \*                                                                                      */
  1126. case WM_BUTTON2DOWN:
  1127. /*                                                                                      *\
  1128.  * This message detected and passed from the PC/2 window procedure is used to track or  *
  1129.  * perform certain actions on windows on the Virtual Desktops.                          *
  1130. \*                                                                                      */
  1131.     {
  1132.     LONG        lClickX, lClickY;       /* Pointer position during click */
  1133.     ULONG       ulWindowIndex;          /* Index in Windows.wdWindow[] */
  1134.     BOOL        bFound=FALSE;           /* TRUE if one window was found */
  1135.     TRACKINFO   tiWindow;               /* Trackinfo structure for one window */
  1136.  
  1137.                                         /* Get position of mouse button 1 down */
  1138.     lClickX=(ULONG)(SHORT1FROMMP(mp1));
  1139.     lClickY=(ULONG)(SHORT2FROMMP(mp1));
  1140.     lClickX=((float)(lClickX-pHP->ptlOrigin.x))/pHP->fScaleX;
  1141.     lClickY=((float)(lClickY-pHP->ptlOrigin.y))/pHP->fScaleY;
  1142.                                         /* Loop for all windows, from topmost to bottommost, on
  1143.                                            overview window */
  1144.     for(ulWindowIndex=0;
  1145.         pHP->Windows.ulWindowLast!=(ULONG)-1 && ulWindowIndex<=pHP->Windows.ulWindowLast;
  1146.         ulWindowIndex++)
  1147.         {
  1148.                                         /* Ignore invisible windows */
  1149.         if(!(pHP->Windows.wdWindow[ulWindowIndex].ulStatus & VISIBLE)) continue;
  1150.         if((pHP->Windows.wdWindow[ulWindowIndex].swpWindow.x<=lClickX) &&
  1151.             (pHP->Windows.wdWindow[ulWindowIndex].swpWindow.x+pHP->Windows.wdWindow[ulWindowIndex].swpWindow.cx>=lClickX) &&
  1152.             (pHP->Windows.wdWindow[ulWindowIndex].swpWindow.y<=lClickY) &&
  1153.             (pHP->Windows.wdWindow[ulWindowIndex].swpWindow.y+pHP->Windows.wdWindow[ulWindowIndex].swpWindow.cy>=lClickY))
  1154.             {                           /* We have found one */
  1155.             bFound=TRUE;
  1156.             break;
  1157.             }
  1158.         }
  1159.     if(bFound)
  1160.         {
  1161.         SWP     swpWindow;              /* Copy of Window's position, because working thread may update it */
  1162.  
  1163.         memcpy(&swpWindow, &pHP->Windows.wdWindow[ulWindowIndex].swpWindow, sizeof(SWP));
  1164.         switch(usMenuCommand)
  1165.         {
  1166.         case ID_ICONMOVE:
  1167.                                         /* Calculate and draw starting tracking rectangle */
  1168.             tiWindow.rclTrack.xLeft=pHP->ptlOrigin.x+
  1169.                 (float)swpWindow.x*pHP->fScaleX;
  1170.             tiWindow.rclTrack.yBottom=pHP->ptlOrigin.y+
  1171.                 (float)swpWindow.y*pHP->fScaleY;
  1172.             tiWindow.rclTrack.xRight=tiWindow.rclTrack.xLeft+
  1173.                 (float)swpWindow.cx*pHP->fScaleX;
  1174.             tiWindow.rclTrack.yTop=tiWindow.rclTrack.yBottom+
  1175.                 (float)swpWindow.cy*pHP->fScaleY;
  1176.             tiWindow.cxBorder=1;        /* Border width */
  1177.             tiWindow.cyBorder=1;
  1178.             tiWindow.cxGrid=1;          /* Grid width */
  1179.             tiWindow.cyGrid=1;
  1180.             tiWindow.cxKeyboard=1;      /* Movement in pixel for keyboard (keyboard currently can't
  1181.                                            start tracking) */
  1182.             tiWindow.cyKeyboard=1;
  1183.                                         /* Minimum tracking size */
  1184.             tiWindow.ptlMinTrackSize.x=2;
  1185.             tiWindow.ptlMinTrackSize.y=2;
  1186.                                         /* Maximum tracking size */
  1187.             tiWindow.ptlMaxTrackSize.x=pHP->swpPC2Client.cx;
  1188.             tiWindow.ptlMaxTrackSize.y=pHP->swpPC2Client.cy;
  1189.                                         /* Boundary rectangle */
  1190.             tiWindow.rclBoundary.xLeft=0;
  1191.             tiWindow.rclBoundary.yBottom=0;
  1192.             tiWindow.rclBoundary.xRight=pHP->swpPC2Client.cx;
  1193.             tiWindow.rclBoundary.yTop=pHP->swpPC2Client.cy;
  1194.                                         /* Tracking options */
  1195.             tiWindow.fs=TF_SETPOINTERPOS | TF_MOVE | TF_ALLINBOUNDARY;
  1196.             if(WinTrackRect(hwnd, NULLHANDLE, &tiWindow))
  1197.                 {                       /* If tracking was successful reposition tracked window */
  1198.                 swpWindow.fl=SWP_MOVE|SWP_NOADJUST;
  1199.                 swpWindow.x=((float)(tiWindow.rclTrack.xLeft-pHP->ptlOrigin.x))/pHP->fScaleX;
  1200.                 swpWindow.y=((float)(tiWindow.rclTrack.yBottom-pHP->ptlOrigin.y))/pHP->fScaleY;
  1201.                 swpWindow.cx=swpWindow.cy=0;
  1202.                 swpWindow.hwndInsertBehind=NULLHANDLE;
  1203.                 WinSetMultWindowPos(pHP->habPc2, &swpWindow, 1);
  1204.                                         /* Force a reread of windows on Desktop */
  1205.                 WinPostMsg(hwnd, WM_SETUPSIZEPOSITION, MPFROMLONG(WM_MOVE), NULL);
  1206.                 }
  1207.             break;
  1208.  
  1209.         case ID_ICONHIDE:               /* Hide selected window */
  1210.             swpWindow.fl=SWP_HIDE;
  1211.             swpWindow.x=swpWindow.y=swpWindow.cx=swpWindow.cy=0;
  1212.             swpWindow.hwndInsertBehind=NULLHANDLE;
  1213.             WinSetMultWindowPos(pHP->habPc2, &swpWindow, 1);
  1214.                                         /* Force a reread of windows on Desktop */
  1215.             WinPostMsg(hwnd, WM_SETUPSIZEPOSITION, MPFROMLONG(WM_MOVE), NULL);
  1216.             break;
  1217.  
  1218.         case ID_ICONZORDERTOP:          /* Set selected window to top of Desktop */
  1219.             swpWindow.fl=SWP_ZORDER;
  1220.             swpWindow.x=swpWindow.y=swpWindow.cx=swpWindow.cy=0;
  1221.             swpWindow.hwndInsertBehind=HWND_TOP;
  1222.             WinSetMultWindowPos(pHP->habPc2, &swpWindow, 1);
  1223.             break;
  1224.  
  1225.         case ID_ICONZORDERBOTTOM:       /* Set selected window to bottom of Desktop */
  1226.             swpWindow.fl=SWP_ZORDER;
  1227.             swpWindow.x=swpWindow.y=swpWindow.cx=swpWindow.cy=0;
  1228.             swpWindow.hwndInsertBehind=HWND_BOTTOM;
  1229.             WinSetMultWindowPos(pHP->habPc2, &swpWindow, 1);
  1230.             break;
  1231.  
  1232.         case ID_ICONCLOSE:              /* Close selected window by simulating the selection of
  1233.                                            the "close" menuitem of the applications's system menu */
  1234.             WinPostMsg(swpWindow.hwnd, WM_SYSCOMMAND, MPFROMLONG(SC_CLOSE), MPFROM2SHORT(CMDSRC_MENU, FALSE));
  1235.             break;
  1236.  
  1237.         case ID_ICONMAXIMIZE:           /* Maximize selected window */
  1238.             swpWindow.fl=SWP_MAXIMIZE;
  1239.             swpWindow.x=swpWindow.y=swpWindow.cx=swpWindow.cy=0;
  1240.             swpWindow.hwndInsertBehind=NULLHANDLE;
  1241.             WinSetMultWindowPos(pHP->habPc2, &swpWindow, 1);
  1242.             break;
  1243.  
  1244.         case ID_ICONMINIMIZE:           /* Maximize selected window */
  1245.             swpWindow.fl=SWP_MINIMIZE;
  1246.             swpWindow.x=swpWindow.y=swpWindow.cx=swpWindow.cy=0;
  1247.             swpWindow.hwndInsertBehind=NULLHANDLE;
  1248.             WinSetMultWindowPos(pHP->habPc2, &swpWindow, 1);
  1249.             break;
  1250.  
  1251.         case ID_ICONRESTORE:            /* Maximize selected window */
  1252.             swpWindow.fl=SWP_RESTORE;
  1253.             swpWindow.x=swpWindow.y=swpWindow.cx=swpWindow.cy=0;
  1254.             swpWindow.hwndInsertBehind=NULLHANDLE;
  1255.             WinSetMultWindowPos(pHP->habPc2, &swpWindow, 1);
  1256.             break;
  1257.         }
  1258.         }
  1259.     }
  1260.     break;
  1261.  
  1262. case WM_BUTTON1DBLCLK:
  1263.     if(pHP->ulDebug>=DEBUG_LOW)
  1264.         {
  1265.         KEYDATA *pKD=pHP->pKeyData;
  1266.         ULONG   ulIndex;
  1267.      
  1268.         for(ulIndex=0; ulIndex<KEYDATACOUNT; ulIndex++, pKD++)
  1269.             printf("%s+%c : %c\n",
  1270.                 (pKD->usFlags==KC_CTRL ? "CTRL" : "ALT"), (char)pKD->usCh, (pKD->bUsed==FALSE ? ' ' : '<'));
  1271.         }
  1272.     if(pHP->ulDebug>=DEBUG_FULL)
  1273.         printf("PC2: WM_BUTTON1DBLCLK ");
  1274.     if(!(ulThreadReady & THREADMOVEBUSY))
  1275.         {
  1276.         if(pHP->ulDebug>=DEBUG_FULL)
  1277.             printf("thread not busy\n");
  1278.  
  1279.                                         /* Set that working thread possibly has to
  1280.                                            move windows on the Virtual Desktop */
  1281.         ulThreadReady|=THREADMOVEBUSY;
  1282.         WinPostMsg(pHP->hwndThread, WM_BUTTON1DBLCLK, MPFROMLONG(mp1), MPFROMLONG(mp2));
  1283.         }
  1284.     else
  1285.         if(pHP->ulDebug>=DEBUG_FULL)
  1286.             printf("thread busy\n");
  1287.     break;
  1288.  
  1289. /*                                                                                      *\
  1290.  * Syntax: WM_WINDOWLIST, (USHORT x, USHORT y), NULL                                    *
  1291. \*                                                                                      */
  1292. case WM_WINDOWLIST:
  1293.                                         /* Pass this message to the working thread */
  1294.     WinPostMsg(pHP->hwndThread, WM_WINDOWLIST, MPFROMLONG(mp1), MPFROMLONG(mp2));
  1295.     break;
  1296.  
  1297. /*                                                                                      *\
  1298.  * Syntax: WM_POPUPMENU, (SHORT x, SHORT y), HWND hwndPopup                             *
  1299. \*                                                                                      */
  1300. case WM_POPUPMENU:
  1301. /*                                                                                      *\
  1302.  * The hook found that button 1 was clicked on the Desktop and sent us this message. It *
  1303.  * is either a WM_BUTTON1CLICK or WM_BUTTON1DBLCLK. First we obtain the focus, to be    *
  1304.  * able to start our programs in the foreground. The coordinates passed are relative to *
  1305.  * the screen, that is, they need not to be translated from window coordinates to       *
  1306.  * Desktop coordinates. Currently we don't differentiate if button 1 was clicked on PM  *
  1307.  * or WPS, which can be determined by hwndPopup. The top level window active before the *
  1308.  * Popup-Menu got called was saved by the input hook.                                   *
  1309. \*                                                                                      */
  1310.     {
  1311.     POINTL      ptlPopupPosition;
  1312.     USHORT      fsOptions=PU_NONE | PU_KEYBOARD | PU_MOUSEBUTTON1 |
  1313.                           PU_HCONSTRAIN | PU_VCONSTRAIN;
  1314.  
  1315.                                         /* Display Configuration dialog if the Popup-Menu
  1316.                                            doesn't contain one */
  1317.     if(pHP->ulStatusFlag & DISPLAYCONFIGDIALOG)
  1318.                                         /* Post message to load Configure PC/2 dialog box.
  1319.                                            We post it because send is synchronous and will
  1320.                                            block the message queue which however must
  1321.                                            not be blocked in order to handle the
  1322.                                            Configuration dialog window procedure */
  1323.         WinPostMsg(hwnd, WM_COMMAND, MPFROMSHORT(ID_CONFIGDIALOG), MPFROMSHORT(CMDSRC_OTHER));
  1324.     else
  1325.         {                               /* If there is one menuentry in the Popup-Menu
  1326.                                            position on the first */
  1327.         if((pHP->pPopupMenu->Item!=ENTRYEMPTY) &&
  1328.             (pHP->ulStatusFlag & SELECTFIRSTITEM))
  1329.             fsOptions|=PU_POSITIONONITEM;
  1330.                                         /* Get the position and window, where the user
  1331.                                            clicked to get the Popup-Menu */
  1332.         ptlPopupPosition.x=(ULONG)SHORT1FROMMP(mp1);
  1333.         ptlPopupPosition.y=(ULONG)SHORT2FROMMP(mp1);
  1334.         if(!WinPopupMenu(               /* Pop up the popup menu */
  1335.             HWND_DESKTOP,               /* Parent window handle */
  1336.             hwnd,                       /* Owner window handle that receives all the
  1337.                                            notification messages generated by the pop-up
  1338.                                            menu */
  1339.                                         /* Popup menu window handle */
  1340.             pHP->hwndPopupMenu,
  1341.             ptlPopupPosition.x,         /* x-coordinate of mouse pointer for popup menu */
  1342.             ptlPopupPosition.y,         /* y-coordinate of mouse pointer for popup menu */
  1343.                                         /* Input item identity, if PU_POSITIONONITEM or
  1344.                                            PU_SELECTITEM is set */
  1345.             pHP->pPopupMenu->id,
  1346.             fsOptions)                  /* Input options */
  1347.         ) PM_ERR(pHP->habPc2, pHP->hwndFrame, HELP_CREATEWINDOW, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  1348.             "Activation of PC/2's Popup-Menu failed - continuing...");
  1349.         }
  1350.     break;
  1351.     }
  1352.  
  1353. /*                                                                                      *\
  1354.  * Syntax: WM_MOVEREQUEST, (USHORT usMouseXPos, USHORT usMouseYPos), (ULONG ulMoveFlag) *
  1355. \*                                                                                      */
  1356. case WM_MOVEREQUEST:
  1357. /*                                                                                      *\
  1358.  * This message is sent by the PC/2 Input Hook as a result of clicking mouse button 1   *
  1359.  * onto any border row or column of the Desktop, requesting to move to another Virtual  *
  1360.  * Desktop.                                                                             *
  1361. \*                                                                                      */
  1362.     if(pHP->ulDebug>=DEBUG_FULL)
  1363.         printf("PC2: WM_MOVEREQUEST ");
  1364.     if(!(ulThreadReady & THREADMOVEBUSY))
  1365.         {
  1366.         if(pHP->ulDebug>=DEBUG_FULL)
  1367.             printf("Thread not busy\n");
  1368.         ulThreadReady|=THREADMOVEBUSY;  /* Set that working thread gets something to do */
  1369.         WinPostMsg(pHP->hwndThread, WM_MOVEREQUEST, MPFROMLONG(mp1), MPFROMLONG(mp2));
  1370.         }
  1371.     else
  1372.         if(pHP->ulDebug>=DEBUG_FULL)
  1373.             printf("Thread busy\n");
  1374.     break;
  1375.  
  1376. /*                                                                                      *\
  1377.  * Syntax: WM_ZORDER, (USHORT usMouseXPos, USHORT usMouseYPos), (HWND hwndTitlebar)     *
  1378. \*                                                                                      */
  1379. case WM_ZORDER:
  1380. /*                                                                                      *\
  1381.  * The hook found that button 2 was clicked on a window's titlebar. After setting this  *
  1382.  * window to bottom, get the next window immediately below the mouse pointer and        *
  1383.  * activate it. The same code moved into the message queue hook does not work, I        *
  1384.  * assume it has something to do that the message queue hook is blocked until the hook  *
  1385.  * returns, and some calls here require a message queue the same time.                  *
  1386. \*                                                                                      */
  1387.     {
  1388.                                         /* Window handle of the titlebar user clicked on */
  1389.     HWND    hwndTitlebar;
  1390.                                         /* Window immediately below (z-order) the mouse pointer */
  1391.     HWND    hwndNextFrame;
  1392.     POINTL  ptlMouse;
  1393.                                         /* Get mouse position relative to the titlebar clicked on */
  1394.     ptlMouse.x=(ULONG)SHORT1FROMMP(mp1);
  1395.     ptlMouse.y=(ULONG)SHORT2FROMMP(mp1);
  1396.     hwndTitlebar=HWNDFROMMP(mp2);
  1397.     WinMapWindowPoints(                 /* Map coordinates from titlebar clicked on to screen */
  1398.         hwndTitlebar,                   /* Window to map from */
  1399.         HWND_DESKTOP,                   /* Window to map to */
  1400.         &ptlMouse,                      /* Point to remap */
  1401.         1);                             /* Map 1 point */
  1402.                                         /* Get the frame window handle under the mouse */
  1403.     hwndNextFrame=WinWindowFromPoint(
  1404.         HWND_DESKTOP,                   /* Child windows to be tested */
  1405.         &ptlMouse,                      /* Point */
  1406.         FALSE);                         /* Test only immediate child windows */
  1407.     if(hwndNextFrame!=WinQueryWindow(hwndTitlebar, QW_PARENT))
  1408.                                         /* Now switch to the new frame window, causing a new
  1409.                                            Z-order, but only when it is not the same window we
  1410.                                            set to bottom. It will generate all messages
  1411.                                            of deactivating old and activating the
  1412.                                            new window. */
  1413.         WinFocusChange(HWND_DESKTOP, WinWindowFromID(hwndNextFrame, FID_CLIENT), 0);
  1414.     }
  1415.     break;
  1416.  
  1417. /*                                                                                      *\
  1418.  * Syntax: WM_SETUPSIZEPOSITION, ULONG ulMsg, NULL                                      *
  1419. \*                                                                                      */
  1420. case WM_SETUPSIZEPOSITION:
  1421. /*                                                                                      *\
  1422.  * This message is sent by the PC/2 WinSendMsg()-Hook as a result of creating, sizing,  *
  1423.  * moving, destroying a frame window or switching to another frame window. This         *
  1424.  * requires that one or more windows need to be redrawn on the overview window.         *
  1425. \*                                                                                      */
  1426.     if(pHP->ulDebug>=DEBUG_FULL)
  1427.         printf("PC2: WM_SETUPSIZEPOSITION ");
  1428.     if(!(ulThreadReady & THREADWINDOWBUSY))
  1429.         {
  1430.         if(pHP->ulDebug>=DEBUG_FULL)
  1431.             printf("Thread not busy\n");
  1432.                                         /* Set that working thread gets something to do */
  1433.         ulThreadReady|=THREADWINDOWBUSY;
  1434.         WinPostMsg(pHP->hwndThread, WM_SETUPSIZEPOSITION, MPFROMLONG(mp1), NULL);
  1435.         }
  1436.     else
  1437.         {
  1438.         if(pHP->ulDebug>=DEBUG_FULL)
  1439.             printf("Thread busy, request queued\n");
  1440.                                         /* Save additional work until thread is not busy again */
  1441.         ulThreadReady|=THREADWINDOWQUEUED;
  1442.         }
  1443.     break;
  1444.  
  1445. /*                                                                                      *\
  1446.  * Syntax: WM_THREADREADY, ULONG ulFlag, NULL                                           *
  1447. \*                                                                                      */
  1448. case WM_THREADREADY:
  1449. /*                                                                                      *\
  1450.  * This message is sent by the PC/2 working thread when he finished handling a          *
  1451.  * WM_MOVEREQUEST or WM_SETUPSIZEPOSITION message. This ensures that pending messages   *
  1452.  * possibly caused by the same user action are not queued by WinPostMsg() calls.        *
  1453. \*                                                                                      */
  1454.     if(pHP->ulDebug>=DEBUG_ENTRY)
  1455.         {
  1456.         DosBeep(250,250);
  1457.         }
  1458.     if(pHP->ulDebug>=DEBUG_FULL)
  1459.         printf("PC2: WM_THREADREADY ");
  1460.     if(LONGFROMMP(mp1)==THREADMOVEBUSY)
  1461.         {
  1462.         if(pHP->ulDebug>=DEBUG_FULL)
  1463.             printf("return from move request\n");
  1464.         ulThreadReady&=(~THREADMOVEBUSY);
  1465.         }
  1466.     else
  1467.         {
  1468.         if(pHP->ulDebug>=DEBUG_FULL)
  1469.             printf("return from size request");
  1470.                                         /* Set that working thread finished his work of
  1471.                                            enumerating windows */
  1472.         ulThreadReady&=(~THREADWINDOWBUSY);
  1473.                                         /* If some work was queued do work once */
  1474.         if(ulThreadReady & THREADWINDOWQUEUED)
  1475.             {
  1476.             if(pHP->ulDebug>=DEBUG_FULL)
  1477.                 printf(", doing queued size request\n");
  1478.                                         /* We are doing one queued enumerating windows request */
  1479.             ulThreadReady&=(~THREADWINDOWQUEUED);
  1480.                                         /* Set that working thread gets something to do */
  1481.             ulThreadReady|=THREADWINDOWBUSY;
  1482.             WinPostMsg(pHP->hwndThread, WM_SETUPSIZEPOSITION, NULL, NULL);
  1483.             }
  1484.         else
  1485.             if(pHP->ulDebug>=DEBUG_FULL)
  1486.                 printf("\n");
  1487.         }
  1488.     break;
  1489.  
  1490. case WM_CLOSE:
  1491.                                         /* When an internal processiong failure occured, that
  1492.                                            requires immediate shutdown of PC/2, the flag
  1493.                                            PC2EMERGENCY is set, and a WM_QUIT message is posted,
  1494.                                            which breaks us out of the message loop */
  1495.     if(!(pHP->ulStatusFlag&PC2EMERGENCYEXIT))
  1496.         {
  1497.                                         /* When running as WPS, PC/2 has already disabled the
  1498.                                            menuentries to exit PC/2, so we don't need to touch
  1499.                                            them. */
  1500.         if(!(pHP->ulStatusFlag&PC2RUNNINGASWPS))
  1501.             {
  1502.                                         /* Disable menuitem during dialog display */
  1503.             WinSendMsg(pHP->hwndPopupMenu, MM_SETITEMATTR,
  1504.                 MPFROM2SHORT(ID_EXIT, TRUE), MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED));
  1505.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1506.                 MPFROM2SHORT(ID_ICONEXIT, TRUE), MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED));
  1507.             }
  1508.         if(WinMessageBox(               /* Ask the user if he really wants to exit */
  1509.             HWND_DESKTOP, HWND_DESKTOP,
  1510.             "Are you sure you want to close PC/2?",
  1511.             "PC/2 - Program Commander/2",
  1512.             ID_PC2MAINWINDOW,
  1513.             MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON1 | MB_MOVEABLE)!=MBID_YES)
  1514.             {
  1515.             if(!(pHP->ulStatusFlag&PC2RUNNINGASWPS))
  1516.                 {
  1517.                                         /* Enable menuitem again */
  1518.                 WinSendMsg(pHP->hwndPopupMenu, MM_SETITEMATTR,
  1519.                     MPFROM2SHORT(ID_EXIT, TRUE), MPFROM2SHORT(MIA_DISABLED, 0));
  1520.                 WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1521.                     MPFROM2SHORT(ID_ICONEXIT, TRUE), MPFROM2SHORT(MIA_DISABLED, 0));
  1522.                 }
  1523.                                         /* Reset */
  1524.             pHP->ulStatusFlag&=(~QUITFROMWINDOWLIST);
  1525.             return((MRESULT)TRUE);      /* Only exit if OK is pressed */
  1526.             }
  1527.         }
  1528.                                         /* Reduce WPS to original size */
  1529.     WinSendMsg(pHP->hwndThread, WM_EXPANDWPS, (MPARAM)FALSE, NULL);
  1530.                                         /* Allow the spooler container window to terminate itself */
  1531.     WinSendMsg(pHP->hwndSpooler, WM_DESTROY, NULL, NULL);
  1532.                                         /* Send WM_QUIT to thread window, because this message
  1533.                                            terminates the message loop */
  1534.     WinPostMsg(pHP->hwndThread, WM_QUIT, NULL, NULL);
  1535.                                         /* Write changes to PC2.INI */
  1536.     if(!INIAccess(pHP->ucFilenameINI, FALSE))
  1537.         USR_ERR(pHP->hwndFrame, HELP_PC2INI, MB_INFORMATION|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  1538.             "Error writing to PC2.INI - continuing...");
  1539.     if(WinReleaseHook(                  /* Release hook */
  1540.         pHP->habPc2,                    /* Handle  of anchor block */
  1541.         NULLHANDLE,                     /* Release from system hook chain */
  1542.         HK_INPUT,                       /* Hook of system input queue */
  1543.         (PFN)pInputHook,                /* Pointer to hook procedure */
  1544.         pHP->hDLLPc2Hook)==FALSE)
  1545.         USR_ERR(pHP->hwndFrame, HELP_PC2HOOK, MB_INFORMATION|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  1546.             "Unhooking the system input queue failed, System ShutDown suggested - exiting...");
  1547.                                         /* Release hook of system sendmessage */
  1548.     if(WinReleaseHook(pHP->habPc2, NULLHANDLE, HK_SENDMSG, (PFN)pWinSendMsgHook,
  1549.         pHP->hDLLPc2Hook)==FALSE)
  1550.         USR_ERR(pHP->hwndFrame, HELP_PC2HOOK, MB_INFORMATION|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  1551.             "Unhooking the sendmessage dispatcher failed, System ShutDown suggested - exiting...");
  1552.                                         /* Post exit message */
  1553.     WinPostMsg(hwnd, WM_QUIT, NULL, NULL);
  1554.     break;
  1555.  
  1556. case HM_ERROR:
  1557.     {
  1558.     PM_ERR(pHP->habPc2, pHP->hwndFrame, HELP_CREATEWINDOW, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  1559.         "Help request failed - continuing...");
  1560.     break;
  1561.     }
  1562.  
  1563. case WM_COMMAND:
  1564.     {
  1565.     USHORT      command;
  1566.  
  1567.     command=SHORT1FROMMP(mp1);          /* Extract the command value */
  1568. /*                                                                                      *\
  1569.  * Filter the IDs of the user defined items of the Popup-Menu. If one is found, call    *
  1570.  * SearchItem() to search for the corresponding MENUDATA structure, copy it to a        *
  1571.  * SESSIONDATA structure and start the session.                                         *
  1572. \*                                                                                      */
  1573.     if((command>=USERITEMFIRST) && (command<=USERITEMLAST))
  1574.         {
  1575.         SESSIONDATA     SessionData;    /* Used by Menu Installation dialog and by
  1576.                                            Program Installation dialog to store menu or
  1577.                                            program data, to be filled from the user or
  1578.                                            to be presented to the user. */
  1579.         ULONG           id=(ULONG)command;
  1580.         MENUDATA        *pMD=NULL;
  1581.  
  1582.                                         /* Search in the linked list for this entry */
  1583.         if((pMD=SearchItem(pHP->pPopupMenu, &id))!=NULL)
  1584.             if(pMD->Item==ENTRYMENUITEM)
  1585.                 {
  1586.                                         /* Load SessionData with MENUDATA structure */
  1587.                 LoadMenuData2SessionData(pMD, &SessionData);
  1588.                                         /* If user defined size and position defined
  1589.                                            set SWP_MOVEWINDOW flag, which invokes a
  1590.                                            reposition of the window, the first time the
  1591.                                            started application's window is found. The flag
  1592.                                            will be reset afterwards */
  1593.                 if(pMD->PgmControl & SSF_CONTROL_SETPOS)
  1594.                     {                   /* If have to move the window create it invisible
  1595.                                            first to avoid drawing of window before movement.
  1596.                                            If the window should be not invisible set flag
  1597.                                            SWP_MOVEWINDOWVISIBLE to show window after movement.
  1598.                                            The flag will be reset afterwards. */
  1599.                     if(pMD->PgmControl & SSF_CONTROL_INVISIBLE)
  1600.                         pMD->SwpFlag|=SWP_MOVEWINDOW;
  1601.                     else
  1602.                         pMD->SwpFlag|=(SWP_MOVEWINDOW | SWP_MOVEWINDOWVISIBLE);
  1603.                                         /* Create window invisible before movement */
  1604.                     SessionData.PgmControl|=SSF_CONTROL_INVISIBLE;
  1605.                     }
  1606.                                         /* If the session should be started not in background
  1607.                                            switch PC/2 into foreground, because only the foreground
  1608.                                            application can start another session in foreground */
  1609.                 if(!(pMD->FgBg&SSF_FGBG_BACK))
  1610.                     WinSwitchToProgram(WinQuerySwitchHandle(pHP->hwndFrame, pHP->pPib->pib_ulpid));
  1611.                                         /* Start the session */
  1612.                 StartSession(&SessionData);
  1613.                                         /* If the session was started in background, try
  1614.                                            to activate the top level window that was active
  1615.                                            before PC/2 got active */
  1616.                 if(pMD->FgBg&SSF_FGBG_BACK)
  1617.                     {
  1618.                     WinFocusChange(HWND_DESKTOP, WinWindowFromID(pHP->hwndActiveWindow, FID_CLIENT),
  1619.                         FC_NOSETACTIVE);
  1620.                     WinPostMsg(pHP->hwndActiveWindow, WM_ACTIVATE,
  1621.                         MPFROMSHORT(TRUE), MPFROMHWND(WinWindowFromID(pHP->hwndActiveWindow, FID_TITLEBAR)));
  1622.                     }
  1623.                                         /* Because a working directory may have been set change
  1624.                                            to root directory of all local drives */
  1625.                 WinPostMsg(pHP->hwndThread, WM_SETDRIVEROOT, NULL, NULL);
  1626.                 }
  1627.         break;                          /* We don't need further testing for this command */
  1628.         }
  1629.     pHP->hwndMenu=WinWindowFromID(pHP->hwndFrame, FID_MENU);
  1630.     switch(command)
  1631.     {
  1632.                                         /* Test for messages sent from smarticons */
  1633.     case ID_ICONEXIT:
  1634.         if(usMenuCommand!=ID_ICONMOVE)
  1635.             {
  1636.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1637.                 MPFROM2SHORT(usMenuCommand, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, 0));
  1638.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1639.                 MPFROM2SHORT(ID_ICONMOVE, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, MIA_FRAMED|MIA_HILITED));
  1640.             }
  1641.         usMenuCommand=ID_ICONMOVE;
  1642.                                         /* Post exit to PC/2 */
  1643.         WinPostMsg(hwnd, WM_COMMAND, MPFROMSHORT(ID_EXIT), MPFROMSHORT(CMDSRC_MENU));
  1644.         break;
  1645.  
  1646.     case ID_ICONMOVE:
  1647.         if(usMenuCommand!=ID_ICONMOVE)
  1648.             {                           /* Remove frame attribute from previous smarticon to move smarticon */
  1649.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1650.                 MPFROM2SHORT(usMenuCommand, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, 0));
  1651.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1652.                 MPFROM2SHORT(ID_ICONMOVE, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, MIA_FRAMED|MIA_HILITED));
  1653.             }
  1654.         usMenuCommand=ID_ICONMOVE;
  1655.         break;
  1656.  
  1657.     case ID_ICONHIDE:
  1658.         if(usMenuCommand!=ID_ICONHIDE)
  1659.             {                           /* Remove frame attribute from previous smarticon to move smarticon */
  1660.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1661.                 MPFROM2SHORT(usMenuCommand, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, 0));
  1662.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1663.                 MPFROM2SHORT(ID_ICONHIDE, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, MIA_FRAMED|MIA_HILITED));
  1664.             }
  1665.         usMenuCommand=ID_ICONHIDE;
  1666.         break;
  1667.  
  1668.     case ID_ICONZORDERTOP:
  1669.         if(usMenuCommand!=ID_ICONZORDERTOP)
  1670.             {
  1671.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1672.                 MPFROM2SHORT(usMenuCommand, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, 0));
  1673.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1674.                 MPFROM2SHORT(ID_ICONZORDERTOP, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, MIA_FRAMED|MIA_HILITED));
  1675.             }
  1676.         usMenuCommand=ID_ICONZORDERTOP;
  1677.         break;
  1678.  
  1679.     case ID_ICONZORDERBOTTOM:
  1680.         if(usMenuCommand!=ID_ICONZORDERBOTTOM)
  1681.             {
  1682.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1683.                 MPFROM2SHORT(usMenuCommand, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, 0));
  1684.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1685.                 MPFROM2SHORT(ID_ICONZORDERBOTTOM, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, MIA_FRAMED|MIA_HILITED));
  1686.             }
  1687.         usMenuCommand=ID_ICONZORDERBOTTOM;
  1688.         break;
  1689.  
  1690.     case ID_ICONCLOSE:
  1691.         if(usMenuCommand!=ID_ICONCLOSE)
  1692.             {
  1693.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1694.                 MPFROM2SHORT(usMenuCommand, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, 0));
  1695.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1696.                 MPFROM2SHORT(ID_ICONCLOSE, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, MIA_FRAMED|MIA_HILITED));
  1697.             }
  1698.         usMenuCommand=ID_ICONCLOSE;
  1699.         break;
  1700.  
  1701.     case ID_ICONMAXIMIZE:
  1702.         if(usMenuCommand!=ID_ICONMAXIMIZE)
  1703.             {
  1704.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1705.                 MPFROM2SHORT(usMenuCommand, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, 0));
  1706.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1707.                 MPFROM2SHORT(ID_ICONMAXIMIZE, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, MIA_FRAMED|MIA_HILITED));
  1708.             }
  1709.         usMenuCommand=ID_ICONMAXIMIZE;
  1710.         break;
  1711.  
  1712.     case ID_ICONMINIMIZE:
  1713.         if(usMenuCommand!=ID_ICONMINIMIZE)
  1714.             {
  1715.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1716.                 MPFROM2SHORT(usMenuCommand, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, 0));
  1717.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1718.                 MPFROM2SHORT(ID_ICONMINIMIZE, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, MIA_FRAMED|MIA_HILITED));
  1719.             }
  1720.         usMenuCommand=ID_ICONMINIMIZE;
  1721.         break;
  1722.  
  1723.     case ID_ICONRESTORE:
  1724.         if(usMenuCommand!=ID_ICONRESTORE)
  1725.             {
  1726.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1727.                 MPFROM2SHORT(usMenuCommand, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, 0));
  1728.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1729.                 MPFROM2SHORT(ID_ICONRESTORE, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, MIA_FRAMED|MIA_HILITED));
  1730.             }
  1731.         usMenuCommand=ID_ICONRESTORE;
  1732.         break;
  1733.  
  1734.     case ID_ICONSHUTDOWN:
  1735.         if(usMenuCommand!=ID_ICONMOVE)
  1736.             {
  1737.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1738.                 MPFROM2SHORT(usMenuCommand, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, 0));
  1739.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1740.                 MPFROM2SHORT(ID_ICONMOVE, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, MIA_FRAMED|MIA_HILITED));
  1741.             }
  1742.         usMenuCommand=ID_ICONMOVE;
  1743.                                         /* Write changes to PC2.INI */
  1744.         INIAccess(pHP->ucFilenameINI, FALSE);
  1745.                                         /* Post ShutDowm OS/2 to PC/2 */
  1746.         WinPostMsg(hwnd, WM_COMMAND, MPFROMSHORT(ID_SHUTDOWN), MPFROMSHORT(CMDSRC_MENU));
  1747.         break;
  1748.  
  1749.     case ID_ICONHELP:
  1750.         if(usMenuCommand!=ID_ICONMOVE)
  1751.             {
  1752.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1753.                 MPFROM2SHORT(usMenuCommand, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, 0));
  1754.             WinSendMsg(pHP->hwndMenu, MM_SETITEMATTR,
  1755.                 MPFROM2SHORT(ID_ICONMOVE, TRUE), MPFROM2SHORT(MIA_FRAMED|MIA_HILITED, MIA_FRAMED|MIA_HILITED));
  1756.             }
  1757.         usMenuCommand=ID_ICONMOVE;
  1758.                                         /* Post help to PC/2 */
  1759.         WinPostMsg(hwnd, WM_COMMAND, MPFROMSHORT(ID_HELP), MPFROMSHORT(CMDSRC_MENU));
  1760.         break;
  1761.  
  1762.     case ID_HELP:                       /* Display general help panel */
  1763.         if(pHP->hwndHelp!=NULLHANDLE) WinSendMsg(
  1764.             pHP->hwndHelp,  /* Help window */
  1765.             HM_DISPLAY_HELP,            /* Display a help panel */
  1766.             MPFROMSHORT(ID_HELP),       /* Panel ID in ressource file */
  1767.             HM_RESOURCEID);             /* MP1 points to the help window identity */
  1768.         break;
  1769.  
  1770.     case ID_CONFIGDIALOG:               /* Popup menuitem Configure Menu selected */
  1771.                                         /* Disable menuitem during dialog display */
  1772.         WinSendMsg(pHP->hwndPopupMenu, MM_SETITEMATTR,
  1773.             MPFROM2SHORT(ID_CONFIGDIALOG, TRUE), MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED));
  1774.         if(!WinDlgBox(                  /* Start Configure PC/2 dialog box */
  1775.             HWND_DESKTOP,               /* DESKTOP is parent */
  1776.             HWND_DESKTOP,               /* DESKTOP is owner */
  1777.             CD_DialogProcedure,         /* Dialog procedure of Program Installation
  1778.                                            dialog */
  1779.             0,                          /* Ressource is .EXE file */
  1780.             CDID_CONFIGDIALOG,          /* ID of Configure PC/2 dialog */
  1781.             0))                         /* No initialization data */
  1782.         PM_ERR(pHP->habPc2, pHP->hwndFrame, HELP_CREATEDIALOG, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  1783.             "Creation of a dialog box failed - continuing...");
  1784.                                         /* Enable menuitem again */
  1785.         WinSendMsg(pHP->hwndPopupMenu, MM_SETITEMATTR,
  1786.             MPFROM2SHORT(ID_CONFIGDIALOG, TRUE), MPFROM2SHORT(MIA_DISABLED, 0));
  1787.         break;
  1788.  
  1789.     case ID_DESKTOPDIALOG:              /* Popup menuitem Configure Desktop selected */
  1790.                                         /* Disable menuitem during dialog display */
  1791.         WinSendMsg(pHP->hwndPopupMenu, MM_SETITEMATTR,
  1792.             MPFROM2SHORT(ID_DESKTOPDIALOG, TRUE), MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED));
  1793.         if(!WinDlgBox(HWND_DESKTOP, HWND_DESKTOP, DD_DialogProcedure,
  1794.             0, DDID_DESKTOPDIALOG, 0))
  1795.         PM_ERR(pHP->habPc2, pHP->hwndFrame, HELP_CREATEDIALOG, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  1796.             "Creation of a dialog box failed - continuing...");
  1797.                                         /* Enable menuitem again */
  1798.         WinSendMsg(pHP->hwndPopupMenu, MM_SETITEMATTR,
  1799.             MPFROM2SHORT(ID_DESKTOPDIALOG, TRUE), MPFROM2SHORT(MIA_DISABLED, 0));
  1800.         break;
  1801.  
  1802.     case ID_SHUTDOWN:                   /* ShutDown OS/2 menuitem selected */
  1803.         if(pHP->ulStatusFlag&NORMALSHUTDOWN)
  1804.                                         /* If the user requested the shutdown he knows from the
  1805.                                            WPS's context menu, do what is requested */
  1806.             WinShutdownSystem(pHP->habPc2, pHP->hmqPc2);
  1807.         else
  1808.             {
  1809.             if(WinMessageBox(           /* Ask the user if he really wants to shut down OS/2 */
  1810.             HWND_DESKTOP, HWND_DESKTOP,
  1811.             "Are you really sure you want to ShutDown OS/2?",
  1812.             "PC/2 - Program Commander/2",
  1813.             ID_PC2MAINWINDOW,
  1814.             MB_OKCANCEL | MB_ICONQUESTION | MB_DEFBUTTON1)!=MBID_OK)
  1815.             return((MRESULT)TRUE);      /* Only shut down if OK is pressed */
  1816.             if(!WinDlgBox(              /* Start ShutDown OS/2 dialog box */
  1817.                 HWND_DESKTOP, HWND_DESKTOP, SD_DialogProcedure, 0,
  1818.                 SDID_SHUTDOWNDIALOG, 0))
  1819.             PM_ERR(pHP->habPc2, pHP->hwndFrame, HELP_CREATEDIALOG, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  1820.                 "Creation of a dialog box failed - continuing...");
  1821.             }
  1822.         break;
  1823.  
  1824.     case ID_EXIT:                       /* User selected F3 to shutdown PC/2, selected Exit PC/2
  1825.                                            from the Popup-Menu or selected the Exit smarticon. */
  1826.  
  1827.                                         /* If running as the WPS replacement, don't exit */
  1828.         if(!(pHP->ulStatusFlag&PC2RUNNINGASWPS))
  1829.             {
  1830.                                         /* Because WM_CLOSE is called, PC/2 is shutdown in
  1831.                                            an orderly manner, so we can accept the next WM_QUIT
  1832.                                            (a WM_QUIT terminates the message loop) message,
  1833.                                            posted during WM_CLOSE immediately when the user really
  1834.                                            wants to quit, to terminate PC/2 without any way to
  1835.                                            return then, by setting QUITFROMWINDOWLIST */
  1836.             pHP->ulStatusFlag|=QUITFROMWINDOWLIST;
  1837.             WinPostMsg(hwnd, WM_CLOSE, 0, 0);
  1838.             }
  1839.         break;
  1840.  
  1841.     case ID_EMERGENCYEXIT:              /* User selected CTRL+ALT+F3 to exit in an undocumented way,
  1842.                                            which is useful for testing or debugging when running
  1843.                                            as WPS replacement */
  1844.         pHP->ulStatusFlag|=QUITFROMWINDOWLIST;
  1845.         WinPostMsg(hwnd, WM_CLOSE, 0, 0);
  1846.         break;
  1847.  
  1848.     case ID_ABOUTDIALOG:                /* User selected About PC/2 dialog */
  1849.                                         /* Disable menuitem during dialog display */
  1850.         WinSendMsg(pHP->hwndPopupMenu, MM_SETITEMATTR,
  1851.             MPFROM2SHORT(ID_ABOUTDIALOG, TRUE), MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED));
  1852.         if(!WinDlgBox(                  /* Start About PC/2 dialog box */
  1853.             HWND_DESKTOP, HWND_DESKTOP, AD_DialogProcedure, 0,
  1854.             ADID_ABOUTDIALOG, 0))
  1855.             PM_ERR(pHP->habPc2, pHP->hwndFrame, HELP_CREATEDIALOG, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  1856.                 "Creation of a dialog box failed - continuing...");
  1857.                                         /* Enable menuitem again */
  1858.         WinSendMsg(pHP->hwndPopupMenu, MM_SETITEMATTR,
  1859.             MPFROM2SHORT(ID_ABOUTDIALOG, TRUE), MPFROM2SHORT(MIA_DISABLED, 0));
  1860.         break;
  1861.  
  1862.     case ID_SPOOLER:                    /* User selected PC/2 spooler dialog */
  1863.         WinPostMsg(pHP->hwndSpooler, WM_SHOWSPOOLER, NULL, NULL);
  1864.         break;
  1865.  
  1866.     case ID_DEBUG:                      /* User pressed SysRq key to toggle debug status */
  1867.         pHP->ulDebug++;
  1868.         if(pHP->ulDebug>DEBUG_FULL) pHP->ulDebug=DEBUG_NO;
  1869.                                         /* Write changes to PC2.INI */
  1870.         INIAccess(pHP->ucFilenameINI, FALSE);
  1871.         break;
  1872.     }
  1873.     break;
  1874.     }
  1875.  
  1876. default:                                /* Default window procedure must be called */
  1877.     return((MRESULT)WinDefWindowProc(hwnd, msg, mp1, mp2));
  1878. }
  1879. return((MRESULT)FALSE);                 /* We have handled the message */
  1880. }
  1881.  
  1882. /*--------------------------------------------------------------------------------------*\
  1883.  * This dialog procedure handles the PC/2 - Configuration (Setup) dialog.               *
  1884.  * Req: none                                                                            *
  1885. \*--------------------------------------------------------------------------------------*/
  1886. MRESULT  EXPENTRY CD_DialogProcedure(HWND hwndDlg, ULONG msg, MPARAM mp1, MPARAM mp2)
  1887. {
  1888. static ULONG    ulListboxCursor;        /* Index of the listbox item that has the cursor selection */
  1889.  
  1890. switch(msg)
  1891. {
  1892. case WM_INITDLG:
  1893.     {
  1894.     SWP         swp;
  1895.  
  1896.     WinQueryWindowPos(                  /* Query position of dialog window */
  1897.         hwndDlg,                        /* Handle of dialog window */
  1898.         &swp);                          /* Fill with position */
  1899.     swp.fl=SWP_MOVE;                    /* Center dialog window */
  1900.     swp.x=(pHP->swpScreen.cx-swp.cx)>>1;
  1901.     swp.y=(pHP->swpScreen.cy-swp.cy)>>1;
  1902.     swp.cx=swp.cy=0;
  1903.     swp.hwndInsertBehind=NULLHANDLE;
  1904.     WinSetMultWindowPos(pHP->habPc2, &swp, 1);
  1905.                                         /* Subclass CDLB_MENUPROGRAM listbox to allow dropping into */
  1906.     pfnListboxWindowProc=WinSubclassWindow(WinWindowFromID(hwndDlg, CDLB_MENUPROGRAM),
  1907.         SubclassedListboxWindowProc);
  1908.     ulListboxCursor=0;                  /* Set cursor selection to first entry */
  1909.                                         /* Initialize the listbox */
  1910.     WinSendMsg(hwndDlg, WM_LOADPOPUPMENU, MPFROMP(pHP->pMenuData), NULL);
  1911.     break;
  1912.     }
  1913.  
  1914. /*                                                                                      *\
  1915.  * Syntax: WM_LOADPOPUPMENU, *MENUDATA, NULL                                            *
  1916. \*                                                                                      */
  1917. case WM_LOADPOPUPMENU:                  /* Load the current level of the Popup-Menu in
  1918.                                            the listbox after removing the old items */
  1919.     {
  1920.     MENUDATA    *pMD;
  1921.  
  1922.     pMD=PVOIDFROMMP(mp1);               /* Get the pointer to the first MENUDATA of the
  1923.                                            current level */
  1924.     WinSendDlgItemMsg(                  /* Send message to listbox */
  1925.         hwndDlg,                        /* Handle of dialog window */
  1926.         CDLB_MENUPROGRAM,               /* Submenu & Program listbox */
  1927.         LM_DELETEALL,                   /* Delete all list box items */
  1928.         (MPARAM)NULL,
  1929.         (MPARAM)NULL);
  1930.     if(pMD==NULL) break;                /* If linked list is empty break out */
  1931.     do
  1932.     {
  1933.         if(pMD->Item==ENTRYSUBMENU)     /* It is a Submenu */
  1934.             {
  1935.             UCHAR       Buffer[MAXNAMEL+4];
  1936.                                         /* Add >> for a Submenu */
  1937.             sprintf(Buffer, "%s >>", pMD->PgmTitle);
  1938.             WinSendDlgItemMsg(
  1939.                 hwndDlg,
  1940.                 CDLB_MENUPROGRAM,
  1941.                 LM_INSERTITEM,          /* Insert Submenu Title at the end */
  1942.                 MPFROMSHORT(LIT_END),
  1943.                 MPFROMP(Buffer));
  1944.             }
  1945.         if(pMD->Item==ENTRYMENUITEM)    /* It's a Menuitem */
  1946.             WinSendDlgItemMsg(
  1947.                 hwndDlg,
  1948.                 CDLB_MENUPROGRAM,
  1949.                 LM_INSERTITEM,          /* Insert Menuitem Title at the end */
  1950.                 MPFROMSHORT(LIT_END),
  1951.                 MPFROMP(pMD->PgmTitle));
  1952.         if(pMD->Item==ENTRYCONTROL)     /* It's a Control */
  1953.             {
  1954.             WinSendDlgItemMsg(
  1955.                 hwndDlg,
  1956.                 CDLB_MENUPROGRAM,
  1957.                 LM_INSERTITEM,          /* Insert Control Title at the end */
  1958.                 MPFROMSHORT(LIT_END),
  1959.                 MPFROMP(pMD->PgmTitle));
  1960.             }
  1961.                                         /* It may also be an empty entry, but then we
  1962.                                            ignore it, because it must be filled with
  1963.                                            Menuitem or Submenu data first */
  1964.         if(pMD->Next!=NULL)             /* Get through linked list without diving into
  1965.                                            Submenus */
  1966.                 pMD=pMD->Next;
  1967.         else break;                     /* We're at the end of the linked list */
  1968.     }while(TRUE);
  1969.                                         /* Set cursor selection to first entry */
  1970.     WinSendDlgItemMsg(hwndDlg, CDLB_MENUPROGRAM, LM_SETTOPINDEX,
  1971.         MPFROMSHORT((USHORT)(ulListboxCursor>7 ? (ulListboxCursor-7) : ulListboxCursor)), MPFROMCHAR(TRUE));
  1972.     WinSendDlgItemMsg(hwndDlg, CDLB_MENUPROGRAM, LM_SELECTITEM,
  1973.         MPFROMSHORT((USHORT)ulListboxCursor), MPFROMCHAR(TRUE));
  1974.     break;
  1975.     }
  1976.  
  1977. /*                                                                                      *\
  1978.  * Syntax: WM_SAVEPOPUPMENU, NULL, NULL                                                 *
  1979. \*                                                                                      */
  1980. case WM_SAVEPOPUPMENU:                  /* Save the Popup-Menu to the configuraion file */
  1981.     if((pHP->Pc2Profile=fopen(pucFilenameProfile, "w"))==NULL)
  1982.         USR_ERR(pHP->hwndFrame, HELP_PC2CFG, MB_INFORMATION|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  1983.             "Cannot open confguration file - changes won't be saved");
  1984.     else
  1985.         {
  1986.         fprintf(pHP->Pc2Profile, "PROFILE START\n");
  1987.                                         /* Save the menu linked list */
  1988.         SaveMenu(pHP->pPopupMenu);
  1989.         fprintf(pHP->Pc2Profile, "PROFILE END\n");
  1990.         fclose(pHP->Pc2Profile);
  1991.         }
  1992.     break;
  1993.  
  1994. case WM_HELP:                           /* Help pressed */
  1995.     WinSendMsg(
  1996.         pHP->hwndHelp,                  /* Help window */
  1997.         HM_DISPLAY_HELP,                /* Display a help panel */
  1998.         MPFROMSHORT(ID_CONFIGDIALOG),   /* Panel ID in resource file */
  1999.         HM_RESOURCEID);                 /* MP1 points to the help window identity */
  2000.     break;
  2001.  
  2002. case WM_CONTROL:
  2003.                                         /* If user doubleclicked on an listbox item process */
  2004.     if(SHORT1FROMMP(mp1)==CDLB_MENUPROGRAM)
  2005.         switch(SHORT2FROMMP(mp1))
  2006.         {
  2007.         case LN_ENTER:
  2008.             {
  2009.             MENUDATA        *pMD;
  2010.             SHORT           sCount;
  2011.  
  2012.                                         /* Point to the first element of the linked list
  2013.                                            at the current level */
  2014.             pMD=pHP->pMenuData;
  2015.                                         /* Send message to listbox */
  2016.             sCount=(SHORT)WinSendDlgItemMsg(
  2017.                 hwndDlg,                /* Handle of dialog window */
  2018.                 CDLB_MENUPROGRAM,       /* Submenu & Program listbox */
  2019.                 LM_QUERYSELECTION,      /* Query first selected list box item */
  2020.                 MPFROMSHORT(LIT_FIRST),
  2021.                 (MPARAM)NULL);
  2022.                                         /* Save cursor selection */
  2023.             ulListboxCursor=(ULONG)sCount;
  2024.             if(sCount==LIT_NONE)        /* If no item selected ignore this button */
  2025.                 return((MRESULT)FALSE);
  2026.             for( ;sCount>0; sCount--)   /* Walk through the linked list to the selected
  2027.                                            item */
  2028.                 pMD=pMD->Next;
  2029.                                         /* For a Menuitem simulate change entry, for a Submenu
  2030.                                            simulate level down */
  2031.             if(pMD->Item==ENTRYSUBMENU)
  2032.                 WinPostMsg(hwndDlg, WM_COMMAND, MPFROM2SHORT(CDID_LEVELDOWN, CMDSRC_OTHER), (MPARAM)TRUE);
  2033.             if(pMD->Item==ENTRYMENUITEM)
  2034.                 WinPostMsg(hwndDlg, WM_COMMAND, MPFROM2SHORT(CDID_CHANGEENTRY, CMDSRC_OTHER), (MPARAM)TRUE);
  2035.             }
  2036.             break;
  2037.         }
  2038.         break;
  2039.  
  2040.  
  2041. case WM_COMMAND:                        /* Button pressed */
  2042.     switch(SHORT1FROMMP(mp1))
  2043.     {
  2044. /*                                                                                      *\
  2045.  * Chain up the linked list until we find the node, where this part-list comes from or  *
  2046.  * the beginning of the complete list. The pointer pMenuData is adjusted.               *
  2047. \*                                                                                      */
  2048.     case CDID_LEVELUP:                  /* Get up one level in the linked list */
  2049.         {
  2050.         MENUDATA        *pMD;           /* Pointer to a MENUDATA structure to find the
  2051.                                            Submenu where this part-list starts */
  2052.  
  2053.         pMD=pHP->pMenuData; /* Point to the first element of the linked list
  2054.                                            at the current level */
  2055.         if(pMD->Back==NULL)             /* If we're at the beginning of the complete linked
  2056.                                            list ignore button */
  2057.             return((MRESULT)FALSE);
  2058.         else pMD=pMD->Back;             /* Submenu which started current level */
  2059.                                         /* Now chain back through the linked list and find
  2060.                                            the element, where the pointer to a Submenu
  2061.                                            equals the back pointer of the first element
  2062.                                            in this Submenu. Then we've found the node */
  2063.         while(TRUE)
  2064.             {
  2065.             if(pMD->Back==NULL)         /* If we're now at the beginning break */
  2066.                 break;
  2067.             if((pMD->Back)->Submenu==pMD)
  2068.                 break;
  2069.             else pMD=pMD->Back;
  2070.             }
  2071.         pHP->pMenuData=pMD; /* Load as the top element of the current item */
  2072.                                         /* Set cursor selection */
  2073.         ulListboxCursor=0;
  2074.                                         /* Now redraw items in listbox */
  2075.         WinSendMsg(hwndDlg, WM_LOADPOPUPMENU, MPFROMP(pHP->pMenuData), NULL);
  2076.         return((MRESULT)FALSE);         /* We handled this button */
  2077.         }
  2078.  
  2079. /*                                                                                      *\
  2080.  * Test the user selection for being a Submenu. If one found chain into this submenu    *
  2081.  * and adjust the pointer pMenuData.                                                    *
  2082. \*                                                                                      */
  2083.     case CDID_LEVELDOWN:                /* Get down one level in the linked list */
  2084.         {
  2085.         MENUDATA        *pMD;           /* Pointer to a MENUDATA structure to find the
  2086.                                            Submenu to chain into */
  2087.         SHORT           sCount;
  2088.  
  2089.         pMD=pHP->pMenuData; /* Point to the first element of the linked list
  2090.                                            at the current level */
  2091.                                         /* Send message to listbox */
  2092.         sCount=(SHORT)WinSendDlgItemMsg(
  2093.             hwndDlg,                    /* Handle of dialog window */
  2094.             CDLB_MENUPROGRAM,           /* Submenu & Program listbox */
  2095.             LM_QUERYSELECTION,          /* Query first selected list box item */
  2096.             MPFROMSHORT(LIT_FIRST),
  2097.             (MPARAM)NULL);
  2098.                                         /* If no item selected, ignore this button */
  2099.         if(sCount==LIT_NONE)
  2100.             return((MRESULT)FALSE);
  2101.         for( ;sCount>0; sCount--)       /* Walk through the linked list to the selected
  2102.                                            item */
  2103.             pMD=pMD->Next;
  2104.         if(pMD->Item!=ENTRYSUBMENU)     /* It's not a Submenu that's selected, ignore */
  2105.             return((MRESULT)FALSE);
  2106.                                         /* Otherwise chain into this part-list */
  2107.         pHP->pMenuData=pMD->Submenu;
  2108.                                         /* Set cursor selection */
  2109.         ulListboxCursor=0;
  2110.                                         /* Now redraw items in listbox */
  2111.         WinSendMsg(hwndDlg, WM_LOADPOPUPMENU, MPFROMP(pHP->pMenuData), NULL);
  2112.         return((MRESULT)FALSE);         /* We handled this button */
  2113.         }
  2114.  
  2115. /*                                                                                      *\
  2116.  * The user selected to add a (Sub)Menu. Thus dismiss the PC/2 Configuration dialog and *
  2117.  * load the (Sub)Menu Installation dialog. The new (Sub)Menu is entered in a            *
  2118.  * STARTSESSION structure named StartSession. Save the changes and reload the PC/2      *
  2119.  * Configuration dialog again.                                                          *
  2120. \*                                                                                      */
  2121.     case CDID_ADDMENU:                  /* Add a Menu to PC/2 Configuration selected */
  2122. /*                                                                                      *\
  2123.  * The user selected to add a Program. Thus dismiss the PC/2 Configuration dialog and   *
  2124.  * load the Menucontrol Addition dialog. The new control style is selected via          *
  2125.  * autoradiobuttons and is fille STARTSESSION structure named StartSession. Save the    *
  2126.  * changes and reload the PC/2 Configuration dialog again.                              *
  2127. \*                                                                                      */
  2128.     case CDID_ADDPROGRAM:               /* Add a Program to PC/2 Configuration selected */
  2129. /*                                                                                      *\
  2130.  * The user selected to add a Control Style. Thus dismiss the PC/2 Configuration dialog *
  2131.  * and load the Program Installation dialog. The new session data is entered in a       *
  2132.  * STARTSESSION structure named StartSession. Save the changes and reload the PC/2      *
  2133.  * Configuration dialog again.                                                          *
  2134. \*                                                                                      */
  2135.     case CDID_ADDCONTROL:               /* Add a Control Entry to PC/2 Configuration selected */
  2136.         {
  2137.         SESSIONDATA     SessionData;    /* Used by Menu Installation dialog and by
  2138.                                            Program Installation dialog to store menu or
  2139.                                            program data, to be filled from the user or
  2140.                                            to be presented to the user. */
  2141.         UCHAR           *pU;            /* Temporary character pointer */
  2142.         MENUDATA        *pMD;           /* Pointer to a MENUDATA structure to insert a
  2143.                                            new MENUDATA stucture after */
  2144.         MENUDATA        *pMDNew;        /* Temporary pointer for the new item to be inserted
  2145.                                            after pMD */
  2146.         ULONG           ulResult;       /* Each dialog procedure returns the result with
  2147.                                            WinDismissDlg() */
  2148.         SHORT           sCount;
  2149.  
  2150.         pMD=pHP->pMenuData; /* Point to the first element of the linked list
  2151.                                            at the current level */
  2152.                                         /* Send message to listbox */
  2153.         sCount=(SHORT)WinSendDlgItemMsg(
  2154.             hwndDlg,                    /* Handle of dialog window */
  2155.             CDLB_MENUPROGRAM,           /* Submenu & Program listbox */
  2156.             LM_QUERYSELECTION,          /* Query first selected list box item */
  2157.             MPFROMSHORT(LIT_FIRST),
  2158.             (MPARAM)NULL);
  2159.                                         /* Save cursor selection */
  2160.         if(sCount!=LIT_NONE)
  2161.             ulListboxCursor=(ULONG)sCount;
  2162.                                         /* If no item selected, and there exists one,
  2163.                                            add the new Menuitem after the last available
  2164.                                            Menuitem by querying the number from the listbox.
  2165.                                            Subtract 0 because we use 0-based instead 1-based. */
  2166.         if((sCount==LIT_NONE) && (pHP->pMenuData->Item!=ENTRYEMPTY))
  2167.             sCount=(SHORT)WinSendDlgItemMsg(hwndDlg, CDLB_MENUPROGRAM, LM_QUERYITEMCOUNT,
  2168.                 MPFROM2SHORT(NULL, NULL), (MPARAM)NULL)-1;
  2169.  
  2170.         for( ;sCount>0; sCount--)       /* Walk through the linked list to the selected
  2171.                                            item */
  2172.             pMD=pMD->Next;
  2173.                                         /* Allocate a new item */
  2174.         pMDNew=AllocateMenuData();
  2175.         if(SHORT1FROMMP(mp1)!=CDID_ADDCONTROL)
  2176.             {                           /* Don't modify MenuData structure if a control entry
  2177.                                            is added */
  2178.             strcpy(pU=malloc(strlen("Insert here please")+1), "Insert here please");
  2179.             free(pMDNew->PgmTitle);
  2180.             pMDNew->PgmTitle=pU;
  2181.                                         /* Increment ID only for non control entries, because
  2182.                                            control entries get their own unique ID */
  2183.             pMDNew->id=pHP->MenuDataId++;
  2184.             }
  2185.         LoadMenuData2SessionData(pMDNew, &SessionData);
  2186.         if(SHORT1FROMMP(mp1)==CDID_ADDMENU)
  2187.             {
  2188.             if(!(ulResult=WinDlgBox(    /* Start Addmenu PC/2 dialog box */
  2189.                 HWND_DESKTOP,           /* DESKTOP is parent */
  2190.                 HWND_DESKTOP,           /* DESKTOP is owner */
  2191.                 MI_DialogProcedure,     /* Dialog procedure of Program Installation
  2192.                                            dialog */
  2193.                 0,                      /* Ressource is .EXE file */
  2194.                 MIID_MENUDIALOG,        /* ID of Addmenu PC/2 dialog */
  2195.                 &SessionData)))         /* Initialization data */
  2196.                 PM_ERR(pHP->habPc2, pHP->hwndFrame, HELP_CREATEDIALOG, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  2197.                     "Creation of a dialog box failed - continuing...");
  2198.             }
  2199.         if(SHORT1FROMMP(mp1)==CDID_ADDPROGRAM)
  2200.             {
  2201.             if(!(ulResult=WinDlgBox(    /* Start Program Installation dialog box */
  2202.                 HWND_DESKTOP,           /* DESKTOP is parent */
  2203.                 hwndDlg,                /* This dialog is owner */
  2204.                 PI_DialogProcedure,     /* Dialog procedure of Program Installation
  2205.                                            dialog */
  2206.                 0,                      /* Ressource is .EXE file */
  2207.                 PIID_PROGRAMDIALOG,     /* ID of Addprogram PC/2 dialog */
  2208.                 &SessionData)))         /* Initialization data */
  2209.                 PM_ERR(pHP->habPc2, pHP->hwndFrame, HELP_CREATEDIALOG, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  2210.                     "Creation of a dialog box failed - continuing...");
  2211.             }
  2212.         if(SHORT1FROMMP(mp1)==CDID_ADDCONTROL)
  2213.             {
  2214.             if(!(ulResult=WinDlgBox(    /* Start Menucontrol Addition dialog box */
  2215.                 HWND_DESKTOP,           /* DESKTOP is parent */
  2216.                 hwndDlg,                /* This dialog is owner */
  2217.                 MD_DialogProcedure,     /* Dialog procedure of Menucontrol Addition
  2218.                                            dialog */
  2219.                 0,                      /* Ressource is .EXE file */
  2220.                 MDID_CONTROLDIALOG,     /* ID of Addmenucontrol PC/2 dialog */
  2221.                 &SessionData)))         /* Initialization data */
  2222.                 PM_ERR(pHP->habPc2, pHP->hwndFrame, HELP_CREATEDIALOG, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  2223.                     "Creation of a dialog box failed - continuing...");
  2224.             }
  2225.                                         /* Check for break separators, because they can only
  2226.                                            be applied to functional menuitems as a change of
  2227.                                            their style. Break separators also can't be applied
  2228.                                            an break saparators. */
  2229.         if(!strcmp(SessionData.PgmTitle, CTRL_BREAKSEPARATOR))
  2230.             if((pMD->Back==NULL) || (pMD->Back->Submenu==pMD) ||
  2231.                 (!strcmp(pMD->PgmTitle, CTRL_BREAKSEPARATOR)))
  2232.                 ulResult=DID_CANCEL;    /* If user wants to apply a break separator style to
  2233.                                            first entry of a submenu, ignore request */
  2234.  
  2235.         if(ulResult==DID_OK)            /* If manipulation is done successfully, then load
  2236.                                            the SESSIONDATA structure back to the MENUDATA
  2237.                                            structure and save the changes */
  2238.             {
  2239.             LoadSessionData2MenuData(pMDNew, &SessionData);
  2240.             if(pMD->Item!=ENTRYEMPTY)   /* Add new entry, if the current entry isn't empty */
  2241.                 {
  2242.                 if(SHORT1FROMMP(mp1)==CDID_ADDMENU)
  2243.                     {                   /* It it is a Submenu, we also must add an empty
  2244.                                            first item for it */
  2245.                     MENUDATA    *pMDTemp;
  2246.  
  2247.                     pMDTemp=AllocateMenuData();
  2248.                     pMDNew->Submenu=pMDTemp;
  2249.                     pMDTemp->Back=pMDNew;
  2250.                     pMDNew->Item=ENTRYSUBMENU;
  2251.                     }
  2252.                 if(SHORT1FROMMP(mp1)==CDID_ADDPROGRAM) pMDNew->Item=ENTRYMENUITEM;
  2253.                 if(SHORT1FROMMP(mp1)==CDID_ADDCONTROL)
  2254.                     {                   /* For controls also add the ID of the control. These
  2255.                                            controls have an predefined ID because the action
  2256.                                            they should start is allways the same */
  2257.                     pMDNew->Item=ENTRYCONTROL;
  2258.                     if(!strcmp(pMDNew->PgmTitle, CTRL_CONFIGMENU))
  2259.                         {               /* If the Configuration Dialog is found reset flag
  2260.                                            to false */
  2261.                         pHP->ulStatusFlag&=(~DISPLAYCONFIGDIALOG);
  2262.                         pMDNew->id=ID_CONFIGDIALOG;
  2263.                         }
  2264.                     if(!strcmp(pMDNew->PgmTitle, CTRL_CONFIGDESKTOP)) pMDNew->id=ID_DESKTOPDIALOG;
  2265.                     if(!strcmp(pMDNew->PgmTitle, CTRL_ABOUT)) pMDNew->id=ID_ABOUTDIALOG;
  2266.                     if(!strcmp(pMDNew->PgmTitle, CTRL_SHUTDOWN)) pMDNew->id=ID_SHUTDOWN;
  2267.                     if(!strcmp(pMDNew->PgmTitle, CTRL_HELP)) pMDNew->id=ID_HELP;
  2268.                     if(!strcmp(pMDNew->PgmTitle, CTRL_EXIT)) pMDNew->id=ID_EXIT;
  2269.                     if(!strcmp(pMDNew->PgmTitle, CTRL_SPOOLER)) pMDNew->id=ID_SPOOLER;
  2270.                                         /* We assume that more than 1 separator may occur
  2271.                                            so to be able add, modify or delete on menuitems
  2272.                                            define a unique one */
  2273.                     if(!strcmp(pMDNew->PgmTitle, CTRL_BREAKSEPARATOR)) pMDNew->id=pHP->MenuDataId++;;
  2274.                     if(!strcmp(pMDNew->PgmTitle, CTRL_SEPARATOR)) pMDNew->id=pHP->MenuDataId++;;
  2275.                     }
  2276.                 if(pMD->Next!=NULL) (pMD->Next)->Back=pMDNew;
  2277.                 pMDNew->Next=pMD->Next;
  2278.                 pMDNew->Back=pMD;
  2279.                 pMD->Next=pMDNew;
  2280.                                         /* Insert item after the existing item */
  2281.                 SetPopupMenu(MM_INSERTITEMMENUITEM, MPFROMP(pMDNew), MPFROMP(pMD));
  2282.                 }
  2283.             else                        /* If it is an empty entry fill it with user data */
  2284.                 {
  2285.                 UCHAR   *pC;            /* Temporary character pointer */
  2286.  
  2287.                 pMD->id=pMDNew->id;
  2288.                 if(SHORT1FROMMP(mp1)==CDID_ADDMENU)
  2289.                     {                   /* It it is a Submenu, we also must add an empty
  2290.                                            first item for it */
  2291.                     MENUDATA    *pMDTemp;
  2292.  
  2293.                     pMDTemp=AllocateMenuData();
  2294.                     pMD->Submenu=pMDTemp;
  2295.                     pMDTemp->Back=pMD;
  2296.                     pMD->Item=ENTRYSUBMENU;
  2297.                     }
  2298.                 if(SHORT1FROMMP(mp1)==CDID_ADDPROGRAM) pMD->Item=ENTRYMENUITEM;
  2299.                 if(SHORT1FROMMP(mp1)==CDID_ADDCONTROL)
  2300.                     {                   /* For controls also add the ID of the control. These
  2301.                                            controls have an predefined ID because the action
  2302.                                            they should start is allways the same */
  2303.                     pMD->Item=ENTRYCONTROL;
  2304.                     if(!strcmp(pMDNew->PgmTitle, CTRL_CONFIGMENU))
  2305.                         {               /* If the Configuration Dialog is found reset flag
  2306.                                            to false */
  2307.                         pHP->ulStatusFlag&=(~DISPLAYCONFIGDIALOG);
  2308.                         pMD->id=ID_CONFIGDIALOG;
  2309.                         }
  2310.                     if(!strcmp(pMDNew->PgmTitle, CTRL_CONFIGDESKTOP)) pMD->id=ID_DESKTOPDIALOG;
  2311.                     if(!strcmp(pMDNew->PgmTitle, CTRL_ABOUT)) pMD->id=ID_ABOUTDIALOG;
  2312.                     if(!strcmp(pMDNew->PgmTitle, CTRL_SHUTDOWN)) pMD->id=ID_SHUTDOWN;
  2313.                     if(!strcmp(pMDNew->PgmTitle, CTRL_HELP)) pMD->id=ID_HELP;
  2314.                     if(!strcmp(pMDNew->PgmTitle, CTRL_EXIT)) pMD->id=ID_EXIT;
  2315.                     if(!strcmp(pMDNew->PgmTitle, CTRL_SPOOLER)) pMD->id=ID_SPOOLER;
  2316.                                         /* We assume that more than 1 separator may occur
  2317.                                            so to be able add, modify or delete on menuitems
  2318.                                            define a unique one */
  2319.                     if(!strcmp(pMDNew->PgmTitle, CTRL_BREAKSEPARATOR)) pMD->id=pHP->MenuDataId++;;
  2320.                     if(!strcmp(pMDNew->PgmTitle, CTRL_SEPARATOR)) pMD->id=pHP->MenuDataId++;;
  2321.                     }
  2322.                 strcpy(pC=malloc(strlen(pMDNew->PgmTitle)+1), pMDNew->PgmTitle);
  2323.                 free(pMD->PgmTitle);
  2324.                 pMD->PgmTitle=pC;
  2325.                 strcpy(pC=malloc(strlen(pMDNew->WindowTitle)+1), pMDNew->WindowTitle);
  2326.                 free(pMD->WindowTitle);
  2327.                 pMD->WindowTitle=pC;
  2328.                 strcpy(pC=malloc(strlen(pMDNew->PgmName)+1), pMDNew->PgmName);
  2329.                 free(pMD->PgmName);
  2330.                 pMD->PgmName=pC;
  2331.                 strcpy(pC=malloc(strlen(pMDNew->PgmDirectory)+1), pMDNew->PgmDirectory);
  2332.                 free(pMD->PgmDirectory);
  2333.                 pMD->PgmDirectory=pC;
  2334.                 strcpy(pC=malloc(strlen(pMDNew->PgmInputs)+1), pMDNew->PgmInputs);
  2335.                 free(pMD->PgmInputs);
  2336.                 pMD->PgmInputs=pC;
  2337.                 strcpy(pC=malloc(strlen(pMDNew->PgmDosSettings)+1), pMDNew->PgmDosSettings);
  2338.                 free(pMD->PgmDosSettings);
  2339.                 pMD->PgmDosSettings=pC;
  2340.                 pMD->SessionType=pMDNew->SessionType;
  2341.                 pMD->PgmControl=pMDNew->PgmControl;
  2342.                 pMD->FgBg=pMDNew->FgBg;
  2343.                 pMD->InitXPos=pMDNew->InitXPos;
  2344.                 pMD->InitYPos=pMDNew->InitYPos;
  2345.                 pMD->InitXSize=pMDNew->InitXSize;
  2346.                 pMD->InitYSize=pMDNew->InitYSize;
  2347.                 (pMD->KeyData).usFlags=(pMDNew->KeyData).usFlags;
  2348.                 (pMD->KeyData).usCh=(pMDNew->KeyData).usCh;
  2349.                 pMD->PriorityClass=pMDNew->PriorityClass;
  2350.                 pMD->SwpFlag=pMDNew->SwpFlag;
  2351.                 pMD->PriorityDelta=pMDNew->PriorityDelta;
  2352.                 if(pMD->Back!=NULL)     /* This is the first item of a Submenu, then
  2353.                                            insert it there */
  2354.                     SetPopupMenu(MM_INSERTITEMSUBMENU, MPFROMP(pMD), MPFROMP(pMD->Back));
  2355.                 else                    /* This is the complete first item of the linked
  2356.                                            list, so insert at the end */
  2357.                     SetPopupMenu(MM_INSERTITEMMENUITEM, MPFROMP(pMD), MPFROMP(NULL));
  2358.                 free(pMDNew->PgmTitle); /* Free temporary used structure */
  2359.                 free(pMDNew->WindowTitle);
  2360.                 free(pMDNew->PgmName);
  2361.                 free(pMDNew->PgmDirectory);
  2362.                 free(pMDNew->PgmInputs);
  2363.                 free(pMDNew->PgmDosSettings);
  2364.                 free(pMDNew);
  2365.                 }
  2366.             }
  2367.         else
  2368.             {
  2369.             free(pMDNew->PgmTitle);     /* Free temporary MENUDATA structure */
  2370.             free(pMDNew->WindowTitle);
  2371.             free(pMDNew->PgmName);
  2372.             free(pMDNew->PgmDirectory);
  2373.             free(pMDNew->PgmInputs);
  2374.             free(pMDNew->PgmDosSettings);
  2375.             free(pMDNew);
  2376.             }
  2377.                                         /* Initialize the listbox */
  2378.         WinSendMsg(hwndDlg, WM_LOADPOPUPMENU, MPFROMP(pHP->pMenuData), NULL);
  2379.                                         /* If PC/2 is not the WPS process and not enabled,
  2380.                                            disable spooler menu item in Popup Menu */
  2381.         if((!(pHP->ulStatusFlag & PC2RUNNINGASWPS)) && (!(pHP->ulStatusFlag & SHOWSPOOLERWINDOW)))
  2382.             WinSendMsg(pHP->hwndPopupMenu, MM_SETITEMATTR,
  2383.                 MPFROM2SHORT(ID_SPOOLER, TRUE), MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED));
  2384.                                         /* Disable Exit OS/2 menuitem when running as the
  2385.                                            WPS replacement */
  2386.         if(pHP->ulStatusFlag&PC2RUNNINGASWPS)
  2387.             {
  2388.             WinSendMsg(pHP->hwndPopupMenu, MM_SETITEMATTR,
  2389.                 MPFROM2SHORT(ID_EXIT, TRUE), MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED));
  2390.             }
  2391.         break;
  2392.         }
  2393.  
  2394. /*                                                                                      *\
  2395.  * The user selected to change an item. Thus dismiss the PC/2 Configuration dialog and  *
  2396.  * load the Menu or Program Installation dialog. The new session data is entered in a   *
  2397.  * STARTSESSION structure named StartSession.                                           *
  2398.  * Then reload the PC/2 Configuration dialog again.                                     *
  2399. \*                                                                                      */
  2400.     case CDID_CHANGEENTRY:              /* Change a Menu or Program configuration selected */
  2401.         {
  2402.         SESSIONDATA     SessionData;    /* Used by Menu Installation dialog and by
  2403.                                            Program Installation dialog to store menu or
  2404.                                            program data, to be filled from the user or
  2405.                                            to be presented to the user. */
  2406.         ULONG           ulResult;       /* Each dialog procedure returns the result with
  2407.                                            WinDismissDlg() */
  2408.         MENUDATA        *pMD;
  2409.         SHORT           sCount;
  2410.  
  2411.         pMD=pHP->pMenuData; /* Point to the first element of the linked list
  2412.                                            at the current level */
  2413.                                         /* Send message to listbox */
  2414.         sCount=(SHORT)WinSendDlgItemMsg(
  2415.             hwndDlg,                    /* Handle of dialog window */
  2416.             CDLB_MENUPROGRAM,           /* Submenu & Program listbox */
  2417.             LM_QUERYSELECTION,          /* Query first selected list box item */
  2418.             MPFROMSHORT(LIT_FIRST),
  2419.             (MPARAM)NULL);
  2420.         if(sCount==LIT_NONE)            /* If no item selected ignore this button */
  2421.             return((MRESULT)FALSE);
  2422.                                         /* Save cursor selection */
  2423.         ulListboxCursor=(ULONG)sCount;
  2424.         for( ;sCount>0; sCount--)       /* Walk through the linked list to the selected
  2425.                                            item */
  2426.             pMD=pMD->Next;
  2427.                                         /* Controls can't be changed, they must be
  2428.                                            removed and newly inserted */
  2429.         if(pMD->Item==ENTRYCONTROL) return((MRESULT)FALSE);
  2430.                                         /* Now load the MENUDATA to SESSIONDATA structure
  2431.                                            where the manipulations will take effect */
  2432.         LoadMenuData2SessionData(pMD, &SessionData);
  2433.         if(pMD->Item==ENTRYMENUITEM)
  2434.                                         /* It's a Menuitem  so call the Program
  2435.                                            Installation dialog box */
  2436.             if(!(ulResult=WinDlgBox(    /* Start Program Installation dialog box */
  2437.                 HWND_DESKTOP,           /* DESKTOP is parent */
  2438.                 hwndDlg,                /* This dialog is owner */
  2439.                 PI_DialogProcedure,     /* Dialog procedure of Program Installation
  2440.                                            dialog */
  2441.                 0,                      /* Ressource is .EXE file */
  2442.                 PIID_PROGRAMDIALOG,     /* ID of Program Installation PC/2 dialog */
  2443.                 &SessionData)))         /* Initialization data */
  2444.                 PM_ERR(pHP->habPc2, pHP->hwndFrame, HELP_CREATEDIALOG, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  2445.                     "Creation of a dialog box failed - continuing...");
  2446.         if(pMD->Item==ENTRYSUBMENU)
  2447.                                         /* It's a Submenu so call the Menu Installation
  2448.                                            dialog box */
  2449.             if(!(ulResult=WinDlgBox(    /* Start Addmenu PC/2 dialog box */
  2450.                 HWND_DESKTOP,           /* DESKTOP is parent */
  2451.                 hwndDlg,                /* This dialog is owner */
  2452.                 MI_DialogProcedure,     /* Dialog procedure of Program Installation
  2453.                                            dialog */
  2454.                 0,                      /* Ressource is .EXE file */
  2455.                 MIID_MENUDIALOG,        /* ID of Addmenu PC/2 dialog */
  2456.                 &SessionData)))         /* Initialization data */
  2457.                 PM_ERR(pHP->habPc2, pHP->hwndFrame, HELP_CREATEDIALOG, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  2458.                     "Creation of a dialog box failed - continuing...");
  2459.         if(ulResult==DID_OK)            /* If manipulation is done successfully, then load
  2460.                                            the SESSIONDATA structure back to the MENUDATA
  2461.                                            structure and save the changes */
  2462.             {
  2463.             LoadSessionData2MenuData(pMD, &SessionData);
  2464.                                         /* Now change the menuitem text to the new one */
  2465.             SetPopupMenu(MM_SETITEMTEXT, MPFROMP(pMD), MPFROMLONG(pMD->id));
  2466.                                         /* Initialize the listbox */
  2467.             WinSendMsg(hwndDlg, WM_LOADPOPUPMENU, MPFROMP(pHP->pMenuData), NULL);
  2468.             }
  2469.         break;
  2470.         }
  2471.  
  2472. /*                                                                                      *\
  2473.  * The user selected to remove an item. If thist item is the only one in the linked     *
  2474.  * list or the first item of a submenu set it to empty otherwise free it's ressources   *
  2475.  * and remove the entry.                                                                *
  2476. \*                                                                                      */
  2477.     case CDID_REMOVEENTRY:              /* Remove a item of the PC/2 Configuration selected */
  2478.         {
  2479.         UCHAR           *pU;
  2480.         MENUDATA        *pMD;
  2481.         SHORT           sCount;
  2482.  
  2483.         pMD=pHP->pMenuData;             /* Point to the first element of the linked list
  2484.                                            at the current level */
  2485.                                         /* Send message to listbox */
  2486.         sCount=(SHORT)WinSendDlgItemMsg(
  2487.             hwndDlg,                    /* Handle of dialog window */
  2488.             CDLB_MENUPROGRAM,           /* Submenu & Program listbox */
  2489.             LM_QUERYSELECTION,          /* Query first selected list box item */
  2490.             MPFROMSHORT(LIT_FIRST),
  2491.             (MPARAM)NULL);
  2492.         if(sCount==LIT_NONE)            /* If no item selected ignore this button */
  2493.             return((MRESULT)FALSE);
  2494.                                         /* Save cursor selection */
  2495.         ulListboxCursor=(ULONG)sCount;
  2496.                                         /* Save cursor selection */
  2497.         ulListboxCursor=(ULONG)sCount;
  2498.         for( ;sCount>0; sCount--)       /* Walk through the linked list to the selected
  2499.                                            item */
  2500.             pMD=pMD->Next;
  2501.         while(TRUE)
  2502.             {
  2503.                                         /* If the selected menu entry is followed by a
  2504.                                            break separator, which is just a style of the
  2505.                                            menu entry itself, disallow removal before the
  2506.                                            break separator is deleted, because this avoids
  2507.                                            very complex logic to correctly remove the
  2508.                                            break separator / append it to previous menu entry */
  2509.             if(pMD->Next!=NULL)
  2510.                 if(!strcmp(pMD->Next->PgmTitle, CTRL_BREAKSEPARATOR))
  2511.                     break;
  2512.             if((pMD->Back==NULL) && (pMD->Next!=NULL))
  2513.                 {                       /* Remove the first item of the complete linked list */
  2514.                 if(pMD->Item==ENTRYSUBMENU)
  2515.                 if((pMD->Submenu)->Item==ENTRYEMPTY)
  2516.                     {                   /* If it is an empty Submenu remove it completely */
  2517.                                         /* Remove the Submenu and the empty first item
  2518.                                            from the Popup-Menu */
  2519.                     SetPopupMenu(MM_DELETEITEM, MPFROMP(pMD), MPFROMP(pMD));
  2520.                     free((pMD->Submenu)->PgmTitle);
  2521.                     free((pMD->Submenu)->WindowTitle);
  2522.                     free((pMD->Submenu)->PgmName);
  2523.                     free((pMD->Submenu)->PgmDirectory);
  2524.                     free((pMD->Submenu)->PgmInputs);
  2525.                     free((pMD->Submenu)->PgmDosSettings);
  2526.                     free(pMD->Submenu);
  2527.                     (pMD->Next)->Back=NULL;
  2528.                                         /* Now next element is the first one */
  2529.                     pHP->pPopupMenu=pMD->Next;
  2530.                     pHP->pMenuData=pMD->Next;
  2531.                     free(pMD->PgmTitle);
  2532.                     free(pMD->WindowTitle);
  2533.                     free(pMD->PgmName);
  2534.                     free(pMD->PgmDirectory);
  2535.                     free(pMD->PgmInputs);
  2536.                     free(pMD->PgmDosSettings);
  2537.                     free(pMD);
  2538.                     break;              /* Ensure we only test once, because each routine
  2539.                                            changes the pointers */
  2540.                     }
  2541.                 if((pMD->Item==ENTRYMENUITEM) || (pMD->Item==ENTRYCONTROL))
  2542.                     {                   /* If it is an empty Menuitem or Control remove it completly */
  2543.                                         /* If the Configuration Dialog is found set flag
  2544.                                            to false */
  2545.                     if(!strcmp(pMD->PgmTitle, CTRL_CONFIGDESKTOP)) pHP->ulStatusFlag &= (~DISPLAYCONFIGDIALOG);
  2546.                     (pMD->Next)->Back=NULL;
  2547.                                         /* Now next element is the first one */
  2548.                     pHP->pPopupMenu=pMD->Next;
  2549.                     pHP->pMenuData=pMD->Next;
  2550.                                         /* Remove the item from the Popup-Menu */
  2551.                     SetPopupMenu(MM_DELETEITEM, MPFROMP(pMD), MPFROMP(pMD));
  2552.                     free(pMD->PgmTitle);
  2553.                     free(pMD->WindowTitle);
  2554.                     free(pMD->PgmName);
  2555.                     free(pMD->PgmDirectory);
  2556.                     free(pMD->PgmInputs);
  2557.                     free(pMD->PgmDosSettings);
  2558.                     free(pMD);
  2559.                     break;              /* Ensure we only test once, because each routine
  2560.                                            changes the pointers */
  2561.                     }
  2562.                 }
  2563.             if((pMD->Back==NULL) && (pMD->Next==NULL))
  2564.                 {                       /* If it is the one and only item of the linked list
  2565.                                            set it to empty */
  2566.                 if(pMD->Item==ENTRYSUBMENU)
  2567.                 if((pMD->Submenu)->Item==ENTRYEMPTY)
  2568.                     {                   /* If it is an empty Submenu remove the empty
  2569.                                            item completely */
  2570.                                         /* Remove the item from the Popup-Menu */
  2571.                     SetPopupMenu(MM_DELETEITEM, MPFROMP(pMD), MPFROMP(pMD));
  2572.                     free((pMD->Submenu)->PgmTitle);
  2573.                     free((pMD->Submenu)->WindowTitle);
  2574.                     free((pMD->Submenu)->PgmName);
  2575.                     free((pMD->Submenu)->PgmDirectory);
  2576.                     free((pMD->Submenu)->PgmInputs);
  2577.                     free((pMD->Submenu)->PgmDosSettings);
  2578.                     free(pMD->Submenu);
  2579.                     free(pMD->PgmTitle);
  2580.                     strcpy(pU=malloc(strlen("")+1), "");
  2581.                     pMD->PgmTitle=pU;
  2582.                     free(pMD->WindowTitle);
  2583.                     strcpy(pU=malloc(strlen("")+1), "");
  2584.                     pMD->WindowTitle=pU;
  2585.                     free(pMD->PgmName);
  2586.                     strcpy(pU=malloc(strlen("")+1), "");
  2587.                     pMD->PgmName=pU;
  2588.                     free(pMD->PgmDirectory);
  2589.                     strcpy(pU=malloc(strlen("")+1), "");
  2590.                     pMD->PgmDirectory=pU;
  2591.                     free(pMD->PgmInputs);
  2592.                     strcpy(pU=malloc(strlen("")+1), "");
  2593.                     pMD->PgmInputs=pU;
  2594.                     free(pMD->PgmDosSettings);
  2595.                     strcpy(pU=malloc(strlen("")+1), "");
  2596.                     pMD->PgmDosSettings=pU;
  2597.                     pMD->Item=ENTRYEMPTY;
  2598.                     pMD->Back=NULL;
  2599.                     pMD->Submenu=NULL;
  2600.                     pMD->Next=NULL;
  2601.                     break;              /* Ensure we only test once, because each routine
  2602.                                            changes the pointers */
  2603.                     }
  2604.                 if((pMD->Item==ENTRYMENUITEM) || (pMD->Item==ENTRYCONTROL))
  2605.                     {                   /* If it is a Menuitem or Control set it to empty */
  2606.                                         /* Remove the item from the Popup-Menu */
  2607.                     SetPopupMenu(MM_DELETEITEM, MPFROMP(pMD), MPFROMP(pMD));
  2608.                     free(pMD->PgmTitle);
  2609.                     strcpy(pU=malloc(strlen("")+1), "");
  2610.                     pMD->PgmTitle=pU;
  2611.                     free(pMD->WindowTitle);
  2612.                     strcpy(pU=malloc(strlen("")+1), "");
  2613.                     pMD->WindowTitle=pU;
  2614.                     free(pMD->PgmName);
  2615.                     strcpy(pU=malloc(strlen("")+1), "");
  2616.                     pMD->PgmName=pU;
  2617.                     free(pMD->PgmDirectory);
  2618.                     strcpy(pU=malloc(strlen("")+1), "");
  2619.                     pMD->PgmDirectory=pU;
  2620.                     free(pMD->PgmInputs);
  2621.                     strcpy(pU=malloc(strlen("")+1), "");
  2622.                     pMD->PgmInputs=pU;
  2623.                     free(pMD->PgmDosSettings);
  2624.                     strcpy(pU=malloc(strlen("")+1), "");
  2625.                     pMD->PgmDosSettings=pU;
  2626.                     pMD->Item=ENTRYEMPTY;
  2627.                     pMD->Back=NULL;
  2628.                     pMD->Submenu=NULL;
  2629.                     pMD->Next=NULL;
  2630.                     break;              /* Ensure we only test once, because each routine
  2631.                                            changes the pointers */
  2632.                     }
  2633.                 }
  2634.             if(pMD->Back!=NULL)
  2635.                 {                       /* It is any item of more than one item and not
  2636.                                            the first one */
  2637.                 if(((pMD->Back)->Submenu==pMD) && (pMD->Submenu==NULL) && (pMD->Next==NULL))
  2638.                 {                       /* If it is the first item of a Submenu not followed
  2639.                                            by any item, set it to empty */
  2640.                                         /* Remove the item from the Popup-Menu */
  2641.                     SetPopupMenu(MM_DELETEITEM, MPFROMP(pMD), MPFROMP(pMD));
  2642.                     free(pMD->PgmTitle);
  2643.                     strcpy(pU=malloc(strlen("")+1), "");
  2644.                     pMD->PgmTitle=pU;
  2645.                     free(pMD->WindowTitle);
  2646.                     strcpy(pU=malloc(strlen("")+1), "");
  2647.                     pMD->WindowTitle=pU;
  2648.                     free(pMD->PgmName);
  2649.                     strcpy(pU=malloc(strlen("")+1), "");
  2650.                     pMD->PgmName=pU;
  2651.                     free(pMD->PgmDirectory);
  2652.                     strcpy(pU=malloc(strlen("")+1), "");
  2653.                     pMD->PgmDirectory=pU;
  2654.                     free(pMD->PgmInputs);
  2655.                     strcpy(pU=malloc(strlen("")+1), "");
  2656.                     pMD->PgmInputs=pU;
  2657.                     free(pMD->PgmDosSettings);
  2658.                     strcpy(pU=malloc(strlen("")+1), "");
  2659.                     pMD->PgmDosSettings=pU;
  2660.                     pMD->Item=ENTRYEMPTY;
  2661.                     break;              /* Ensure we only test once, because each routine
  2662.                                            changes the pointers */
  2663.                     }
  2664.                 if(pMD->Item==ENTRYSUBMENU)
  2665.                 if((pMD->Submenu)->Item==ENTRYEMPTY)
  2666.                     {                   /* If it is an empty Submenu so also remove the
  2667.                                            first item in the Submenu */
  2668.                                         /* Remove the Submenu and the empty first item
  2669.                                            from the Popup-Menu */
  2670.                     SetPopupMenu(MM_DELETEITEM, MPFROMP(pMD), MPFROMP(pMD));
  2671.                     free((pMD->Submenu)->PgmTitle);
  2672.                     free((pMD->Submenu)->WindowTitle);
  2673.                     free((pMD->Submenu)->PgmName);
  2674.                     free((pMD->Submenu)->PgmDirectory);
  2675.                     free((pMD->Submenu)->PgmInputs);
  2676.                     free((pMD->Submenu)->PgmDosSettings);
  2677.                     free(pMD->Submenu);
  2678.                     if(((pMD->Back)->Submenu==pMD) && (pMD->Next==NULL))
  2679.                         {               /* If the previous item is a Submenu, this item is
  2680.                                            the first item of it. If none item follows, set
  2681.                                            this item to empty */
  2682.                         free(pMD->PgmTitle);
  2683.                         strcpy(pU=malloc(strlen("")+1), "");
  2684.                         pMD->PgmTitle=pU;
  2685.                         free(pMD->WindowTitle);
  2686.                         strcpy(pU=malloc(strlen("")+1), "");
  2687.                         pMD->WindowTitle=pU;
  2688.                         free(pMD->PgmName);
  2689.                         strcpy(pU=malloc(strlen("")+1), "");
  2690.                         pMD->PgmName=pU;
  2691.                         free(pMD->PgmDirectory);
  2692.                         strcpy(pU=malloc(strlen("")+1), "");
  2693.                         pMD->PgmDirectory=pU;
  2694.                         free(pMD->PgmInputs);
  2695.                         strcpy(pU=malloc(strlen("")+1), "");
  2696.                         pMD->PgmInputs=pU;
  2697.                         free(pMD->PgmDosSettings);
  2698.                         strcpy(pU=malloc(strlen("")+1), "");
  2699.                         pMD->PgmDosSettings=pU;
  2700.                         pMD->Item=ENTRYEMPTY;
  2701.                         pMD->Submenu=NULL;
  2702.                         pMD->Next=NULL;
  2703.                         break;          /* Ensure we only test once, because each routine
  2704.                                            changes the pointers */
  2705.                         }
  2706.                     if(((pMD->Back)->Submenu==pMD) && (pMD->Next!=NULL))
  2707.                         {               /* If the previous item is a Submenu, this item ist
  2708.                                            the first item of it. If one item follows adjust
  2709.                                            the pointer to the current level of items */
  2710.                         pHP->pMenuData=pMD->Next;
  2711.                         (pMD->Back)->Submenu=pMD->Next;
  2712.                         if(pMD->Next!=NULL) (pMD->Next)->Back=pMD->Back;
  2713.                         free(pMD->PgmTitle);
  2714.                         free(pMD->WindowTitle);
  2715.                         free(pMD->PgmName);
  2716.                         free(pMD->PgmDirectory);
  2717.                         free(pMD->PgmInputs);
  2718.                         free(pMD->PgmDosSettings);
  2719.                         free(pMD);
  2720.                         break;          /* Ensure we only test once, because each routine
  2721.                                            changes the pointers */
  2722.                         }
  2723.                     if((pMD->Back)->Submenu!=pMD)
  2724.                         {               /* If this item isn't the first item of a Submenu */
  2725.                         (pMD->Back)->Next=pMD->Next;
  2726.                         if(pMD->Next!=NULL) (pMD->Next)->Back=pMD->Back;
  2727.                         free(pMD->PgmTitle);
  2728.                         free(pMD->WindowTitle);
  2729.                         free(pMD->PgmName);
  2730.                         free(pMD->PgmDirectory);
  2731.                         free(pMD->PgmInputs);
  2732.                         free(pMD->PgmDosSettings);
  2733.                         free(pMD);
  2734.                         break;          /* Ensure we only test once, because each routine
  2735.                                            changes the pointers */
  2736.                         }
  2737.                     }
  2738.                 if((pMD->Item==ENTRYMENUITEM) || pMD->Item==ENTRYCONTROL)
  2739.                     {                   /* If it is a Menuitem or Control just remove it completly */
  2740.                                         /* Remove the item from the Popup-Menu */
  2741.                     SetPopupMenu(MM_DELETEITEM, MPFROMP(pMD), MPFROMP(pMD));
  2742.                     if(((pMD->Back)->Submenu==pMD) && (pMD->Next==NULL))
  2743.                         {               /* If the previous item is a Submenu, this item is
  2744.                                            the first item of it. If none item follows, set
  2745.                                            this item to empty */
  2746.                         free(pMD->PgmTitle);
  2747.                         strcpy(pU=malloc(strlen("")+1), "");
  2748.                         pMD->PgmTitle=pU;
  2749.                         free(pMD->WindowTitle);
  2750.                         strcpy(pU=malloc(strlen("")+1), "");
  2751.                         pMD->WindowTitle=pU;
  2752.                         free(pMD->PgmName);
  2753.                         strcpy(pU=malloc(strlen("")+1), "");
  2754.                         pMD->PgmName=pU;
  2755.                         free(pMD->PgmDirectory);
  2756.                         strcpy(pU=malloc(strlen("")+1), "");
  2757.                         pMD->PgmDirectory=pU;
  2758.                         free(pMD->PgmInputs);
  2759.                         strcpy(pU=malloc(strlen("")+1), "");
  2760.                         pMD->PgmInputs=pU;
  2761.                         free(pMD->PgmDosSettings);
  2762.                         strcpy(pU=malloc(strlen("")+1), "");
  2763.                         pMD->PgmDosSettings=pU;
  2764.                         pMD->Item=ENTRYEMPTY;
  2765.                         pMD->Submenu=NULL;
  2766.                         pMD->Next=NULL;
  2767.                         break;          /* Ensure we only test once, because each routine
  2768.                                            changes the pointers */
  2769.                         }
  2770.                     if(((pMD->Back)->Submenu==pMD) && (pMD->Next!=NULL))
  2771.                         {               /* If the previous item is a Submenu, this item ist
  2772.                                            the first item of it. If one item follows adjust
  2773.                                            the pointer to the current level of items */
  2774.                         pHP->pMenuData=pMD->Next;
  2775.                         (pMD->Back)->Submenu=pMD->Next;
  2776.                         if(pMD->Next!=NULL) (pMD->Next)->Back=pMD->Back;
  2777.                         free(pMD->PgmTitle);
  2778.                         free(pMD->WindowTitle);
  2779.                         free(pMD->PgmName);
  2780.                         free(pMD->PgmDirectory);
  2781.                         free(pMD->PgmInputs);
  2782.                         free(pMD->PgmDosSettings);
  2783.                         free(pMD);
  2784.                         break;          /* Ensure we only test once, because each routine
  2785.                                            changes the pointers */
  2786.                         }
  2787.                     if((pMD->Back)->Submenu!=pMD)
  2788.                         {               /* If this item isn't the first item of a Submenu */
  2789.                         (pMD->Back)->Next=pMD->Next;
  2790.                         if(pMD->Next!=NULL) (pMD->Next)->Back=pMD->Back;
  2791.                         free(pMD->PgmTitle);
  2792.                         free(pMD->WindowTitle);
  2793.                         free(pMD->PgmName);
  2794.                         free(pMD->PgmDirectory);
  2795.                         free(pMD->PgmInputs);
  2796.                         free(pMD->PgmDosSettings);
  2797.                         free(pMD);
  2798.                         break;          /* Ensure we only test once, because each routine
  2799.                                            changes the pointers */
  2800.                         }
  2801.                     }
  2802.                 }
  2803.             break;                      /* If we come here, we're trying to remove an not
  2804.                                            empty Submenu, but we also must exit the
  2805.                                            endless loop */
  2806.             }
  2807.                                         /* Initialize the listbox */
  2808.         WinSendMsg(hwndDlg, WM_LOADPOPUPMENU, MPFROMP(pHP->pMenuData), NULL);
  2809.         return((MRESULT)FALSE);         /* We have done everything */
  2810.         }
  2811.  
  2812. /*                                                                                      *\
  2813.  * The user selected to resort the current level of the menuentries. Load the dialog    *
  2814.  * and let the user resort the linked list of menues pointed to by pMenuData and to     *
  2815.  * resort the menuentries of the Popup-Menu.                                            *
  2816. \*                                                                                      */
  2817.     case CDID_RESORT:                   /* Load the resort dialog */
  2818.         if(!WinDlgBox(                  /* Start Resort dialog box */
  2819.             HWND_DESKTOP,               /* DESKTOP is parent */
  2820.             hwndDlg,                    /* This dialog is owner */
  2821.             RD_DialogProcedure,         /* Dialog procedure of Program Installation
  2822.                                            dialog */
  2823.             0,                          /* Ressource is .EXE file */
  2824.             RDID_RESORTDIALOG,          /* ID of Program Installation PC/2 dialog */
  2825.             0))                         /* No initialization data */
  2826.             PM_ERR(pHP->habPc2, pHP->hwndFrame, HELP_CREATEDIALOG, MB_ERROR|MB_OK|MB_HELP|MB_MOVEABLE|MB_DEFBUTTON1,
  2827.                 "Creation of a dialog box failed - continuing...");
  2828.                                         /* Initialize the listbox */
  2829.         WinSendMsg(hwndDlg, WM_LOADPOPUPMENU, MPFROMP(pHP->pMenuData), NULL);
  2830.         break;
  2831.  
  2832.     case DID_OK:                        /* Enter key pressed */
  2833.                                         /* Save the changes */
  2834.         WinSendMsg(hwndDlg, WM_SAVEPOPUPMENU, NULL, NULL);
  2835.                                         /* Dialog terminated with DID_OK */
  2836.         WinDismissDlg(hwndDlg, DID_OK);
  2837.         break;
  2838.  
  2839.     case DID_CANCEL:                    /* Escape or Cancel pressed */
  2840.                                         /* Dialog terminated with DID_CANCEL */
  2841.         WinDismissDlg(hwndDlg, DID_CANCEL);
  2842.         break;
  2843.  
  2844.     default:
  2845.         return(WinDefDlgProc(hwndDlg, msg, mp1, mp2));
  2846.     }
  2847.     break;
  2848.  
  2849. default:                                /* Default window procedure must be called */
  2850.     return(WinDefDlgProc(hwndDlg, msg, mp1, mp2));
  2851. }
  2852. return((MRESULT)FALSE);                 /* We have handled the message */
  2853. }
  2854.  
  2855. /*--------------------------------------------------------------------------------------*\
  2856.  * This subclassed window procedure handles the PC/2's setup dialog CDLB_MENUPROGRAM    *
  2857.  * listbox to allow dropping into.                                                      *
  2858.  * Req: none                                                                            *
  2859. \*--------------------------------------------------------------------------------------*/
  2860. MRESULT  EXPENTRY SubclassedListboxWindowProc(HWND hwndListbox, ULONG msg, MPARAM mp1, MPARAM mp2)
  2861. {
  2862. PDRAGINFO       pDraginfo;              /* Pointer to DRAGINFO structure */
  2863. PDRAGITEM       pDragitem;              /* Pointer to DRAGITEM structure */
  2864.  
  2865. switch(msg)
  2866. {
  2867. /*                                                                                      *\
  2868.  * Allow known objects to be dropped.                                                   *
  2869. \*                                                                                      */
  2870. case DM_DRAGOVER:
  2871.     pDraginfo = (PDRAGINFO)mp1;         /* Get the pointer to the DRAGINFO structure */
  2872.                                         /* Access the structure */
  2873.     if(DrgAccessDraginfo(pDraginfo)==FALSE) break;
  2874.                                         /* Get the first itemp of the item(s) dragged
  2875.                                            onto dialog window */
  2876.     pDragitem = DrgQueryDragitemPtr(pDraginfo, 0);
  2877.                                         /* Allow only one object, coming from only WPS 2 PC/2 */
  2878.     if((pDraginfo->cditem!=1) || !(DrgVerifyRMF(pDragitem, "DRM_WPS2PC2", "DRF_WPS2PC2")))
  2879.                                         /* Don't allow dropping of more than one item */
  2880.         return(MPFROM2SHORT(DOR_NODROP, DO_UNKNOWN));
  2881.                                         /* Allow drop of undefined operation onto dialog window */
  2882.     return(MPFROM2SHORT(DOR_DROP, DO_UNKNOWN));
  2883.  
  2884. case DM_DROP:
  2885.     {
  2886.     UCHAR       ucObjectType[256];
  2887.     PBYTE       pbSharedMem;
  2888.  
  2889.     pDraginfo = (PDRAGINFO)mp1;         /* Get the pointer to the DRAGINFO structure */
  2890.                                         /* Access the structure */
  2891.     if(DrgAccessDraginfo(pDraginfo)==FALSE) break;
  2892.                                         /* Get the first itemp of the item(s) dragged
  2893.                                            onto dialog window */
  2894.     pDragitem = DrgQueryDragitemPtr(pDraginfo, 0);
  2895.                                         /* Query the rendering format */
  2896.     DrgQueryStrName(pDragitem->hstrRMF, sizeof(ucObjectType), ucObjectType);
  2897.                                         /* Scan for an file object */
  2898.     if(DrgVerifyRMF(pDragitem, DRM_WPS2PC2, DRF_WPS2PC2))
  2899.         {                               /* It was dragged from WPS 2 PC/2, so we know how to
  2900.                                            access the shared memory we expect */
  2901.         pbSharedMem=(PBYTE)pDragitem->ulItemID;
  2902.         if(DosGetSharedMem(pbSharedMem, PAG_READ | PAG_WRITE)==NO_ERROR)
  2903.             {
  2904.                                         /* Insert the LIST of WPSOBJECTLISTs into
  2905.                                            Popup-Menu */
  2906.             ConvertWPSObjectList2MenuData(pHP->pMenuData, (WPSOBJECTLIST *)pbSharedMem);
  2907.                                         /* Give memory back to OS/2 */
  2908.             DosFreeMem(pbSharedMem);
  2909.                                         /* Initialize the listbox */
  2910.             WinSendMsg(WinQueryWindow(hwndListbox, QW_OWNER), WM_LOADPOPUPMENU,
  2911.                 MPFROMP(pHP->pMenuData), NULL);
  2912.             }
  2913.         break;
  2914.         }
  2915.     }
  2916.     return((MRESULT)FALSE);             /* We have handled the message */
  2917. }
  2918.                                         /* Call default window procedure */
  2919. return(pfnListboxWindowProc(hwndListbox, msg, mp1, mp2));
  2920. }
  2921.  
  2922. /*--------------------------------------------------------------------------------------*\
  2923.  * This subclassed window procedure handles the PC/2's smart icon menu.                 *
  2924.  * Req: none                                                                            *
  2925. \*--------------------------------------------------------------------------------------*/
  2926. MRESULT  EXPENTRY SubclassedMenuWindowProc(HWND hwndMenu, ULONG msg, MPARAM mp1, MPARAM mp2)
  2927. {
  2928. switch(msg)
  2929. {
  2930. case WM_PRESPARAMCHANGED:
  2931.     switch((ULONG)mp1)
  2932.     {
  2933.     case PP_FONTNAMESIZE:
  2934.         {
  2935.         ULONG       ulAttrFound;
  2936.  
  2937.                                         /* Get font selected for PC/2's smart icon menu */
  2938.         if(WinQueryPresParam(hwndMenu, PP_FONTNAMESIZE, 0, &ulAttrFound,
  2939.             sizeof(pHP->ucPopupMenuFont), pHP->ucPopupMenuFont, 0))
  2940.                                         /* Set this font also into the Popup-Menu */
  2941.             WinSetPresParam(pHP->hwndPopupMenu, PP_FONTNAMESIZE,
  2942.                 sizeof(pHP->ucPopupMenuFont), pHP->ucPopupMenuFont);
  2943.         }
  2944.         break;
  2945.     }
  2946. }
  2947.                                         /* Call default window procedure */
  2948. return(pfnMenuWindowProc(hwndMenu, msg, mp1, mp2));
  2949. }
  2950.  
  2951.