home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tolkit45.zip / os2tk45 / samples / pm / graphics / file.c next >
C/C++ Source or Header  |  1999-05-11  |  67KB  |  1,844 lines

  1. /************ FILE C Sample Program Source Code File (.C) *******************
  2.  *
  3.  * PROGRAM NAME: GRAPHIC
  4.  * -------------
  5.  *  Presentation Manager Non-Retained Graphics C Sample Program
  6.  *
  7.  * WHAT THIS PROGRAM DEMONSTRATES:
  8.  * -------------------------------
  9.  * Following are all the functions needed to print "GPI", "METAFILE"
  10.  * in a separate thread.
  11.  *
  12.  *  Copyright (C) 1991, 1993 IBM Corporation
  13.  *
  14.  *      DISCLAIMER OF WARRANTIES.  The following [enclosed] code is
  15.  *      sample code created by IBM Corporation. This sample code is not
  16.  *      part of any standard or IBM product and is provided to you solely
  17.  *      for  the purpose of assisting you in the development of your
  18.  *      applications.  The code is provided "AS IS", without
  19.  *      warranty of any kind.  IBM shall not be liable for any damages
  20.  *      arising out of your use of the sample code, even if they have been
  21.  *      advised of the possibility of such damages.                                                    *
  22.  ******************************************************************************/
  23.  
  24. /* Include the required sections of the PM header file */
  25.  
  26. #include <stdlib.h>
  27. #include <string.h>
  28.  
  29. #define INCL_DOSPROCESS
  30. #define INCL_DOSSEMAPHORES
  31. #define INCL_WINWINDOWMGR
  32. #define INCL_GPICONTROL
  33. #define INCL_WINPOINTERS
  34. #define INCL_GPIMETAFILES
  35. #define INCL_GPICORRELATION
  36. #define INCL_WINMESSAGEMGR
  37. #define INCL_GPIREGIONS
  38. #define INCL_WINRECTANGLES
  39. #define INCL_GPITRANSFORMS
  40. #define INCL_GPIPRIMITIVES
  41. #define INCL_WINERRORS
  42. #define INCL_GPILCIDS
  43. #define INCL_WINDIALOGS
  44. #define INCL_DEV
  45. #define INCL_WINSTDDLGS
  46. #define INCL_DOSERRORS
  47. #define INCL_GPIERRORS
  48. #define INCL_WINSHELLDATA 
  49.  
  50. #include <os2.h>
  51. #include <malloc.h>
  52. #include <memory.h>
  53. #include <sys\types.h>
  54. #include <sys\stat.h>
  55. #include "graphic.h"
  56.  
  57. #define EXPORT_BUFFER_SIZE 1000
  58.  
  59. /* static function definitions */
  60. static   VOID       PreparePrinting(PHPS hpsPrint,
  61.                                     PHDC hdcPrinter,
  62.                                     HAB habPrint,
  63.                                     PSIZEL sizlPS,
  64.                                     PRESOLUTION Resolution,
  65.                                     PPRTTXT_DATA PrintData,
  66.                                     PSIZEL DevCaps);
  67. static   VOID       MetaOutput(PDRAW_PARM PrtParm,
  68.                                PPRTTXT_DATA PrintData,
  69.                                HAB habPrint);
  70. static   int _Optlink SortQueues(const void *Queue1, const void *Queue2);
  71. static   PSZ        GetQueueNamesString(PULONG);
  72. static   CHAR       pm_spooler_queue_dd[] = "PM_SPOOLER_QUEUE_DD";
  73. static   CHAR       Drive[]={0,0,0};
  74.  
  75. /*********************************************************************
  76.  *
  77.  *  Name:   OpenFileDialog
  78.  *
  79.  *  Purpose: open the standard file open dialog as file extention
  80.  *           and return the filename
  81.  *
  82.  *  Usage:   called when the user needs to supply a name for
  83.  *           the file to be opened
  84.  *
  85.  *  Method:  calls the standard file open dialog to get the
  86.  *           file name.
  87.  *
  88.  *  Parameters: HWD hwnd         Handle of the owner window.
  89.  *              PSZ szTitle      Title of open dialog.
  90.  *              PSZ pszFileExt   File extention. (for example : *.txt)
  91.  *              PSZ pszFullPath  PSZ for returning the file name.
  92.  *
  93.  *  Returns: TRUE if successful in getting a file name, FALSE
  94.  *              if not in pushing CANCEL
  95.  *           PSZ pszFullPath pointer to filename (full path)
  96.  *
  97.  **********************************************************************/
  98. BOOL OpenFileDialog(HWND hwndOwner,
  99.                     PSZ szTitle,
  100.                     PSZ szFileExt,
  101.                     PSZ szFullPath)
  102. {
  103.    static   PSZ        ppszdefaultdrivelist[] = {NULL};
  104.  
  105.             FILEDLG    fdg;               /* file dialog structure           */
  106.  
  107.    fdg.cbSize = sizeof(FILEDLG);          /* Size of FILEDLG.                */
  108.    fdg.pszTitle = szTitle;                /* String to display in title bar. */
  109.    fdg.pszOKButton = (PSZ)"Open";         /* String to display in OK button. */
  110.    fdg.ulUser = 0L;                       /* User defined field.             */
  111.    fdg.fl = FDS_HELPBUTTON |              /* FDS flags.                      */
  112.             FDS_CENTER |
  113.             FDS_OPEN_DIALOG;
  114.    fdg.pfnDlgProc = (PFNWP)TemplateOpenFilterProc;/* Entry point to custom*/
  115.                                                   /*         dialog proc. */
  116.    fdg.lReturn = 0L;                      /* Result code from dialog dismissal. */
  117.    fdg.lSRC = 0L;                         /* System return code.          */
  118.    fdg.hMod = 0;                          /* Custom file dialog template. */
  119.    fdg.usDlgId = 0;                       /* Custom file dialog ID.       */
  120.    fdg.x = 0;                             /* X coordinate of the dialog.  */
  121.    fdg.y = 0;                             /* Y coordinate of the dialog.  */
  122.  
  123.    /* set selected fully qualified path */
  124.    strcpy( fdg.szFullFile, szFileExt);
  125.  
  126.    fdg.pszIType = NULL;                   /* Pointer to string containing   */
  127.                                           /*   Initial EA type filter.      */
  128.    fdg.papszITypeList = NULL;             /* Pointer to table of pointers   */
  129.                                           /*  that point to null terminated */
  130.    if (!Drive[0])                         /*  Type strings.                 */
  131.        fdg.pszIDrive = NULL;              /* Pointer to string containing   */
  132.    else                                   /*  initial drive.                */
  133.        fdg.pszIDrive = Drive;
  134.  
  135.    fdg.papszIDriveList = (PAPSZ)ppszdefaultdrivelist;
  136.                                           /* Pointer to table of pointers   */
  137.                                           /*  that point to null terminated */
  138.                                           /*  Drive strings.                */
  139.    fdg.sEAType = 0;                       /* Selected file's EA Type.       */
  140.    fdg.papszFQFilename = NULL;            /* Pointer to table of pointers   */
  141.                                           /*  point to null terminated      */
  142.                                           /*  FQFname strings.              */
  143.    fdg.ulFQFCount = 0L;                   /* Numbers of file selected.      */
  144.  
  145.    /* get the file */
  146.  
  147.    if (!WinFileDlg(HWND_DESKTOP, hwndOwner, (PFILEDLG)&fdg))
  148.       return FALSE;
  149.  
  150.    if (fdg.lReturn == DID_CANCEL)
  151.       return FALSE;
  152.  
  153.    /* copy file name into file name buffer */
  154.  
  155.    strcpy(szFullPath, fdg.szFullFile);
  156.    strcpy(szFileExt, fdg.szFullFile);
  157.    strncpy(Drive,fdg.szFullFile,2);         /* Always Contains Drive letter */
  158.  
  159.    return TRUE;
  160. } /* End of OpenFileDialog */
  161.  
  162. /**************************************************************************
  163.  * Function: DrawThread
  164.  *
  165.  * DrawThread is the asynchronous drawing thread.  It performs all
  166.  * drawing, whether to the screen or to the printer.
  167.  * It obtains an anchor block handle and creates a message queue.
  168.  * It then posts a private message to the main thread's client area
  169.  * if successful. NewThread monitors its own message queue, and
  170.  * processes the commands it receives.
  171.  *
  172.  * Parameters: PMAIN_PARM   main parameter structure
  173.  *
  174.  * Result:  none
  175.  *
  176.  ***************************************************************************/
  177. VOID DrawThread(PMAIN_PARM Parm)
  178. {
  179. static   CHAR       DrawThreadErrorTitle[] = "Draw Thread Error:";
  180. static   CHAR       DrawThreadError1[] = "WinInitialize failed!";
  181. static   CHAR       DrawThreadError2[] = "Cannot Create Message Queue.";
  182.          QMSG       qmsgAsync;      /* Message structure for WinGetMsg  */
  183.          QMSG       qmsgPeek;       /* Message structure for WinPeekMsg */
  184.          BOOL       fFlushQueue;    /* Flush-queue indicator            */
  185.          BOOL       fQueueFlush;
  186.          RECTL      rclClient;      /* Bounding rect for picture xform  */
  187.          BOOL       Loop = TRUE;
  188. static   PSZ        dcdatablk[9] = {0,
  189.                                     "MEMORY", /* display driver      */
  190.                                     0, 0, 0, 0, 0, 0, 0 };
  191.  
  192.   /*
  193.    * Obtain an anchor block handle. If this fails, the thread and
  194.    * the application are terminated.
  195.    */
  196.  
  197.    if ((Parm->DrawParm.hABDraw = WinInitialize((USHORT)0)) == (HAB)NULLHANDLE)
  198.    {
  199.       WinPostMsg(Parm->hWndFrame,
  200.               WM_USER_END_ON_ERROR,
  201.               MPFROMP(DrawThreadErrorTitle),
  202.               MPFROMP(DrawThreadError1));
  203.       DosExit(EXIT_THREAD, 0);
  204.    }
  205.  
  206.   /*
  207.    * Create a message queue. If this fails, the thread and the
  208.    * application are terminated.
  209.    */
  210.  
  211.    if ((Parm->hMQDraw = WinCreateMsgQueue(Parm->DrawParm.hABDraw, 0)) ==
  212.                                           (HMQ)NULLHANDLE)
  213.    {
  214.       WinTerminate(Parm->DrawParm.hABDraw);
  215.       WinPostMsg(Parm->hWndFrame,
  216.               WM_USER_END_ON_ERROR,
  217.               MPFROMP(DrawThreadErrorTitle),
  218.               MPFROMP(DrawThreadError2));
  219.       DosExit(EXIT_THREAD, 0);
  220.    }
  221.  
  222.   /*
  223.    * The SetTransform function is invoked to set up the transformation
  224.    * for the default picture. The main thread's priority is adjusted
  225.    * to be higher than that of the asynchronous thread. (GPI CIRCLES)
  226.    */
  227.  
  228.    WinPostMsg(Parm->hWndFrame,
  229.              WM_COMMAND,
  230.              MPFROMP(IDM_F_GPI),
  231.              MPFROMP(NULL));
  232.    DosSetPriority(PRTYS_THREAD, PRTYC_NOCHANGE, 1L, 0L);
  233.    while (Loop)
  234.    {
  235.  
  236.    /*
  237.     * The semaphore hsemStoppable is obtained so that the flag
  238.     * fWaiting can be set to TRUE. This indicates that the thread is
  239.     * waiting for a message, and prevents the main thread from
  240.     * issuing GpiSetStopDraw.
  241.     * To be sure that it will not be suspended, the asynchronous
  242.     * thread sets the stop-draw condition off. It then reads its
  243.     * message queue.
  244.     */
  245.  
  246.       WinRequestMutexSem(Parm->hmtxsemStoppable, (ULONG)-1);
  247.       Parm->fWaiting = TRUE;
  248.       DosReleaseMutexSem(Parm->hmtxsemStoppable);
  249.  
  250.       if (Parm->hpsClient != (HPS)NULLHANDLE)
  251.          GpiSetStopDraw(Parm->hpsClient, (LONG)FALSE);
  252.  
  253.       WinGetMsg(Parm->DrawParm.hABDraw, &qmsgAsync, (HWND)NULLHANDLE, 0L, 0L);
  254.  
  255.    /*
  256.     * When a message has been read from the queue, the flag fWaiting
  257.     * is set the FALSE. This allows the main thread to issue the
  258.     * GpiSetStopDraw call if necessary.
  259.     */
  260.  
  261.       WinRequestMutexSem(Parm->hmtxsemStoppable, (ULONG)-1);
  262.       Parm->fWaiting = FALSE;
  263.       DosReleaseMutexSem(Parm->hmtxsemStoppable);
  264.  
  265.    /*
  266.     * Before doing any drawing, the thread examines its message queue
  267.     * to see if it contains any other commands that would force
  268.     * redrawing. If there is a flush command in the queue, all
  269.     * commands, up to and including the flush command, are read from
  270.     * the queue. A SET_TRANSFORM message is treated like a flush
  271.     * command, because it also means that a new picture is required.
  272.     */
  273.  
  274.       fFlushQueue = WinPeekMsg(Parm->DrawParm.hABDraw,
  275.                             &qmsgPeek,
  276.                             0L,
  277.                             FLUSH_COMMAND,
  278.                             FLUSH_COMMAND,
  279.                             PM_NOREMOVE);
  280.  
  281.       fQueueFlush = WinPeekMsg(Parm->DrawParm.hABDraw,
  282.                             &qmsgPeek,
  283.                             0L,
  284.                             SET_TRANSFORM,
  285.                             SET_TRANSFORM,
  286.                             PM_NOREMOVE);
  287.  
  288.       if (fFlushQueue|| fQueueFlush)
  289.          do
  290.          {
  291.             WinGetMsg(Parm->DrawParm.hABDraw, &qmsgAsync, NULLHANDLE, 0L, 0L);
  292.          } while (qmsgAsync.msg != FLUSH_COMMAND &&
  293.                   qmsgAsync.msg != SET_TRANSFORM &&
  294.                   qmsgAsync.msg != STOP_DRAWING
  295.                   ); /* enddo */
  296.  
  297.       if (WinPeekMsg(Parm->DrawParm.hABDraw,
  298.                   &qmsgPeek,
  299.                   NULLHANDLE,
  300.                   STOP_THREAD,
  301.                   DUMMY_COMMAND,
  302.                   PM_NOREMOVE))
  303.          Parm->DrawParm.fDrawing = FALSE;
  304.       else
  305.          Parm->DrawParm.fDrawing = TRUE;
  306.  
  307.    /* Process the commands.   */
  308.  
  309.       switch (qmsgAsync.msg)
  310.       {
  311.        case SET_TRANSFORM:
  312.             /*
  313.              * Invoke the SetTransform function to define a default viewing
  314.              * transformation for the picture.
  315.              */
  316.            if (*Parm->Message != '\0')
  317.               break;
  318.            SetTransform(Parm, qmsgAsync.mp1);
  319.            break;
  320.  
  321.        case DRAW_ORDERS:
  322.             /*
  323.              * Invoke the DoDraw function. DoDraw uses the region definition
  324.              * passed to it to repaint the invalidated area of the screen.
  325.              * The region definition is then destroyed.
  326.              */
  327.            if (Parm->CheckedItem == 3)
  328.            {
  329.               break;
  330.            }
  331.            else
  332.            {
  333.               if (*Parm->Message != '\0')
  334.                  break;
  335.               DoDraw(Parm->hpsClient, &Parm->DrawParm, (HRGN)qmsgAsync.mp1,
  336.                      Parm->InfoWnd.hWnd);
  337.               if (qmsgAsync.mp1 != MPVOID)
  338.                  GpiDestroyRegion(Parm->hpsClient, (HRGN)qmsgAsync.mp1);
  339.  
  340.               break;
  341.            }
  342.       case DISPLAY_MESSAGE:
  343.            DisplayMessage(Parm->hWndClient,
  344.                           Parm->Message);
  345.            break;
  346.  
  347.       case SIZING:
  348.            /* Recalculate the picture transformation. */
  349.  
  350.            if (*Parm->Message != '\0')
  351.               break;
  352.            WinQueryWindowRect(Parm->hWndClient, &rclClient);
  353.            Transform(&Parm->DrawParm, Parm->hpsClient, &rclClient);
  354.            break;
  355.  
  356.       case ACTIVATE_DRAW_THREAD:
  357.            WinPostMsg(Parm->hWndFrame, ACTIVATE_DRAW_THREAD, MPVOID, MPVOID);
  358.            break;
  359.  
  360.       case STOP_DRAWING:
  361.            Parm->DrawThreadActivated = FALSE;
  362.            DosPostEventSem(Parm->hevsemStop);
  363.            break;
  364.  
  365.       case STOP_THREAD:
  366.            /* StopThread is invoked to terminate the thread. */
  367.            Loop = FALSE;
  368.            break;
  369.  
  370.       case FLUSH_COMMAND:
  371.            /*
  372.             * Other commands have already been discarded. Set the fAllValid
  373.             * flag to TRUE.
  374.             */
  375.            Parm->DrawParm.fAllValid = TRUE;
  376.            break;
  377.       }
  378.    }
  379.  
  380.    DosSetPriority(PRTYS_THREAD, PRTYC_IDLETIME, 0L, 0L);
  381.  
  382.    WinDestroyMsgQueue(Parm->hMQDraw);  /* Destroy this thread's message queue */
  383.  
  384.    WinTerminate(Parm->DrawParm.hABDraw);
  385.  
  386.    DosPostEventSem(Parm->hevsemTerminate);
  387.  
  388.    DosExit(EXIT_THREAD, 0);
  389. } /* End of DrawThread */
  390.  
  391. /**************************************************************************
  392.  * Function: SendCommand
  393.  *
  394.  * The SendCommand function posts the required command and its
  395.  * parameters to the asynchronous drawing thread's message queue.
  396.  *
  397.  * Parameters: PMAIN_PARM   main parameter structure
  398.  *             usCommand    command to send
  399.  *             ULONG        long value passed to draw thread as parameter1
  400.  *
  401.  * Result:  BOOL            TRUE is ok
  402.  *
  403.  ***************************************************************************/
  404. BOOL SendCommand(PMAIN_PARM Parm, ULONG ulCommand, ULONG ulInfo)
  405. {
  406.    APIRET     Ret;
  407.  
  408.    if (Parm->hMQDraw == (HMQ)NULLHANDLE)
  409.       return FALSE;
  410.  /*
  411.   * Communication between the main thread and the asynchronous thread
  412.   * is controlled by the two flags fWaiting and fStoppable. Access to
  413.   * the two flags is regulated by the semaphore hsemStoppable.
  414.   * The main thread requests access to the semaphore, and waits until
  415.   * it is available. If this command is one that causes drawing, the
  416.   * main thread checks whether the asynchronous thread is waiting for
  417.   * a message. If it is not waiting, the main thread issues
  418.   * GpiSetStopDraw to suspend drawing. It then frees the semaphore,
  419.   * and posts the message to the asynchronous thread's message queue.
  420.   */
  421.    do
  422.    {
  423.       Ret = WinRequestMutexSem(Parm->hmtxsemStoppable, (ULONG)-1);
  424.    } while (Ret == (APIRET)ERROR_INTERRUPT);
  425.  
  426.    if (ulCommand <= DUMMY_COMMAND)
  427.       if (!Parm->fWaiting)
  428.          GpiSetStopDraw(Parm->hpsClient, (ULONG)Parm->fStoppable);
  429.  
  430.    DosReleaseMutexSem(Parm->hmtxsemStoppable);
  431.  
  432.    return WinPostQueueMsg(Parm->hMQDraw,
  433.                        ulCommand,
  434.                        MPFROMLONG(ulInfo),
  435.                        MPVOID);
  436. } /* End of SendCommand */
  437.  
  438. /**************************************************************************
  439.  * Function: SetTransform
  440.  *
  441.  * The SetTransform function calculates the original default viewing
  442.  * transformation for the picture.  This function can be interrupted
  443.  * only by another request to set the default viewing transformation,
  444.  * or by a request to terminate the thread. SetTransform invokes the
  445.  * DoDraw function to draw the picture, and then calculates the
  446.  * bounding rectangle of the picture.
  447.  * It then invokes the Transform function to set the appropriate
  448.  * transformation.
  449.  *
  450.  * Parameters: PMAIN_PARM   main parameter structure
  451.  *             MPARAM       message parameter  passed from SendCommand
  452.  *
  453.  * Result:  none
  454.  *
  455.  ***************************************************************************/
  456. VOID SetTransform(PMAIN_PARM Parm, MPARAM mp1)
  457. {
  458.    LONG       lRc;
  459.    RECTL      rclDest;
  460.    RECTL      rclClient;
  461.    POINTL     ptlWork;
  462.    RECTL      rclWork;
  463.  
  464.    GpiErase(Parm->hpsClient);
  465.    WinInvalidateRect(Parm->InfoWnd.hWnd, (PRECTL)NULL, TRUE);
  466.  
  467.    DisplayMessage(Parm->hWndClient,
  468.                   GetString(Parm->DrawParm.hABDraw,
  469.                             ((HMF)mp1 == (HMF)NULLHANDLE) ?
  470.                                  TEXT_DRAWTHR_SIZEGPI : TEXT_DRAWTHR_SIZEMETA,
  471.                             STATIC_STRING));
  472.  
  473.    Parm->fBusy = TRUE;
  474.    if (WinQueryPointerPos(HWND_DESKTOP, &ptlWork))
  475.        if (WinMapWindowPoints(HWND_DESKTOP, Parm->hWndClient, &ptlWork, 1))
  476.            if (WinQueryWindowRect(Parm->hWndClient, &rclWork))
  477.                if (WinPtInRect(Parm->DrawParm.hABDraw, &rclWork, &ptlWork))
  478.                    WinSetPointer(HWND_DESKTOP, Parm->hptrGlass);
  479.  
  480.    if (Parm->DrawParm.hmfPicture != (HMF)NULLHANDLE)
  481.       GpiDeleteMetaFile(Parm->DrawParm.hmfPicture);
  482.  
  483.    /* The current metafile is deleted before the next one is drawn.     */
  484.    Parm->DrawParm.hmfPicture = (HMF)mp1;
  485.  
  486.    WinRequestMutexSem(Parm->hmtxsemStoppable, (ULONG)-1);
  487.    Parm->fStoppable = FALSE;
  488.    DosReleaseMutexSem(Parm->hmtxsemStoppable);
  489.  
  490.    /*
  491.     * The boundary data is cleared so that the transformation and the
  492.     * presentation space are not reset after the GpiPlayMetaFile call.
  493.     */
  494.    Parm->DrawParm.rclBounds.xRight  = 0xFFFF;
  495.    Parm->DrawParm.rclBounds.xLeft   = 0xFFFF;
  496.    Parm->DrawParm.rclBounds.yTop    = 0xFFFF;
  497.    Parm->DrawParm.rclBounds.yBottom = 0xFFFF;
  498.    GpiResetBoundaryData(Parm->hpsClient);
  499.    /*
  500.     * The presentation space is disassociated from the device context.
  501.     * Boundary-data accumulation is switched on, and the DoDraw
  502.     * function is invoked to draw the picture into the disassociated
  503.     * device context. The picture is drawn, but not displayed. The
  504.     * boundary data is retrieved, and the presentation space is
  505.     * reassociated with the device context. Boundary-data accumulation
  506.     * is switched off.
  507.     */
  508.    GpiAssociate(Parm->hpsClient, (HDC)NULLHANDLE);
  509.    GpiSetDrawControl(Parm->hpsClient, DCTL_BOUNDARY, DCTL_ON);
  510.  
  511.    GpiSetStopDraw(Parm->hpsClient, (ULONG)Parm->fStoppable);
  512.    Parm->DrawParm.fDrawing = TRUE;
  513.    lRc = DoDraw(Parm->hpsClient, &Parm->DrawParm, (HRGN)NULLHANDLE,
  514.                                 Parm->InfoWnd.hWnd);
  515.  
  516.    GpiQueryBoundaryData(Parm->hpsClient, &Parm->DrawParm.rclBounds);
  517.    GpiAssociate(Parm->hpsClient, Parm->hdcClient);
  518.    GpiSetDrawControl(Parm->hpsClient, DCTL_BOUNDARY, DCTL_OFF);
  519.    /*
  520.     * The presentation space is reset so that subsequent
  521.     * GpiPlayMetaFile calls will succeed.
  522.     */
  523.    GpiResetPS(Parm->hpsClient, GRES_ALL);
  524.    /*
  525.     * If the DoDraw function was successful, the Transform function is
  526.     * invoked to set the transformation. WinInvalidateRect invalidates
  527.     * the client area and causes a WM_PAINT message to be issued.
  528.     * The title bar is also updated.
  529.     */
  530.    if (lRc == DRAW_OK)
  531.    {
  532.       WinQueryWindowRect(Parm->hWndClient, &rclClient);
  533.       Transform(&Parm->DrawParm, Parm->hpsClient, &rclClient);
  534.       WinRequestMutexSem(Parm->hmtxsemStoppable, (ULONG)-1);
  535.       Parm->fNotReady = FALSE;
  536.    /*
  537.     * The application can now process paint and erase-background
  538.     * requests.
  539.     */
  540.       DosReleaseMutexSem(Parm->hmtxsemStoppable);
  541.       WinQueryWindowRect(Parm->hWndClient, &rclDest);
  542.       WinInvalidateRect(Parm->hWndClient, &rclDest, FALSE);
  543.    }
  544.    else
  545.    {
  546.       strcpy(Parm->Message,
  547.              GetString(Parm->DrawParm.hABDraw,
  548.                        ERRMSG_DRAWTHR_MSG2,
  549.                        STATIC_STRING));
  550.       strcat(Parm->Message, Parm->MetafilePath);
  551.       DisplayErrorMessage( Parm->hWndClient, Parm->Message);
  552.       Parm->fNotReady = FALSE;
  553.    }
  554.    /*
  555.     * If the DoDraw function failed, the main thread is prompted to
  556.     * display a warning message and issue another SET_TRANSFORM
  557.     * message for the default picture.
  558.     */
  559.    Parm->fBusy = FALSE;
  560.    if (WinQueryPointerPos(HWND_DESKTOP, &ptlWork))
  561.        if (WinMapWindowPoints(HWND_DESKTOP, Parm->hWndClient, &ptlWork, 1))
  562.            if (WinQueryWindowRect(Parm->hWndClient, &rclWork))
  563.                if (WinPtInRect(Parm->DrawParm.hABDraw, &rclWork, &ptlWork))
  564.                    WinSetPointer(HWND_DESKTOP, Parm->hptrArrow);
  565.  
  566.    /* Allow GpiSetStopDraw to interrupt subsequent drawing.             */
  567.    WinRequestMutexSem(Parm->hmtxsemStoppable, (ULONG)-1);
  568.    Parm->fStoppable = TRUE;
  569.    DosReleaseMutexSem(Parm->hmtxsemStoppable);
  570.    return;
  571. } /* End of SetTransform */
  572.  
  573. /**************************************************************************
  574.  * Function: WndProcPaint
  575.  *
  576.  * The WndProcPaint function is invoked from the window procedure
  577.  * ClientWndProc to process the WM_PAINT message. WndProcPaint
  578.  * in turn invokes the asynchronous thread to perform the window
  579.  * painting. If the asynchronous thread has both a drawing and a
  580.  * transformation to process, a region is created that is equal to the
  581.  * update region. This is passed to the asynchronous thread and the
  582.  * region is validated. Otherwise, no painting occurs.
  583.  * If the asynchronous thread does not have time to paint the updated
  584.  * regions, it accumulates them.
  585.  *
  586.  * Parameters: PMAIN_PARM   main parameter structure
  587.  *
  588.  * Result:  MRESULT         message result
  589.  *
  590.  ***************************************************************************/
  591. MRESULT WndProcPaint(PMAIN_PARM Parm)
  592. {
  593.    HRGN      hrgnUpdt;
  594.    LONG      sRgnType;
  595.    APIRET    Ret;
  596.    ULONG     MsgTyp;
  597.  
  598.    do
  599.    {
  600.       Ret = WinRequestMutexSem(Parm->hmtxsemStoppable, (ULONG)-1);
  601.    } while (Ret == (APIRET)ERROR_INTERRUPT);
  602.  
  603.    hrgnUpdt = GpiCreateRegion(Parm->hpsPaint, 0L, (PRECTL)NULL);
  604.    sRgnType = WinQueryUpdateRegion(Parm->hWndClient, hrgnUpdt);
  605.    if (!Parm->fNotReady)
  606.    {
  607.       if (*Parm->Message != '\0')
  608.          MsgTyp = DISPLAY_MESSAGE;
  609.       else
  610.          MsgTyp = DRAW_ORDERS;
  611.  
  612.       DosReleaseMutexSem(Parm->hmtxsemStoppable);
  613.       if ((sRgnType == RGN_RECT) ||
  614.           (sRgnType == RGN_COMPLEX))
  615.       {
  616.          if (!SendCommand(Parm, MsgTyp, 0L))
  617.             GpiDestroyRegion(Parm->hpsPaint, hrgnUpdt);
  618.          WinValidateRegion(Parm->hWndClient, hrgnUpdt, FALSE);
  619.       }
  620.       else
  621.       {
  622.          SendCommand(Parm, MsgTyp, 0L);
  623.          WinValidateRegion(Parm->hWndClient, hrgnUpdt, FALSE);
  624.          GpiDestroyRegion(Parm->hpsPaint, hrgnUpdt);
  625.       } /* endif */
  626.    }
  627.    else
  628.    {
  629.       DosReleaseMutexSem(Parm->hmtxsemStoppable);
  630.       WinValidateRegion(Parm->hWndClient, hrgnUpdt, FALSE);
  631.       GpiDestroyRegion(Parm->hpsPaint, hrgnUpdt);
  632.    } /* endif */
  633.    return(FALSE);
  634. } /* End of WndProcPaint */
  635.  
  636. /**************************************************************************
  637.  * Function: Transform
  638.  *
  639.  * The Transform function recalculates the default viewing
  640.  * transformation when the picture is scrolled or sized, and when the
  641.  * 'Best Fit' option is changed.
  642.  * If the window handle is NULLHANDLE, the default viewing transformation
  643.  * is applied to ensure that the picture is contained within the page
  644.  * and that no graphics clipping occurs for printing.
  645.  *
  646.  * Parameters: PDRAW_PARM   draw parameter structure
  647.  *             HPS          presentation space handle for drawing
  648.  *             PRECTL       rectangle
  649.  *
  650.  * Result:  none
  651.  *
  652.  ***************************************************************************/
  653. VOID Transform(PDRAW_PARM Parm, HPS hpsXForm, PRECTL prclXform)
  654. {
  655.          LONG       lxPictRight, lxPictLeft;
  656.          LONG       lyPictTop, lyPictBottom; /* picture boundary information */
  657.          LONG       clxPict, clyPict;        /* picture extents              */
  658.          POINTL     ptlPictCenter;           /* center of picture            */
  659.          LONG       clxBounds, clyBounds;    /* bounds area extent           */
  660.          LONG       alScale[2];              /* scaling factors              */
  661.          LONG       sxWork, syWork;          /* work areas                   */
  662.          LONG       lxWork, lyWork;          /* work areas                   */
  663. static   MATRIXLF   matlfViewTransform = {65536,0,0,0,65536,0,0,0,1};
  664.  
  665. /* Calculate the center of the bounding rectangle of the picture. */
  666.    lxPictRight = Parm->rclBounds.xRight + 5;
  667.    lxPictLeft = Parm->rclBounds.xLeft - 5;
  668.    lyPictTop = Parm->rclBounds.yTop + 5;
  669.    lyPictBottom = Parm->rclBounds.yBottom - 5;
  670.  
  671.    ptlPictCenter.x = (lxPictRight - lxPictLeft)/2 + lxPictLeft;
  672.    ptlPictCenter.y = (lyPictTop - lyPictBottom)/2 + lyPictBottom;
  673.  
  674.  
  675.    /* Get the picture extents. */
  676.    clxPict = lxPictRight - lxPictLeft;
  677.    clyPict = lyPictTop - lyPictBottom;
  678.  
  679.    /* Get the current bounding rectangle extent. */
  680.    clxBounds = prclXform->xRight - prclXform->xLeft;
  681.    clyBounds = prclXform->yTop - prclXform->yBottom;
  682.  
  683.    /* Calculate the x/y ratio of the current bounding area. */
  684.    sxWork = (LONG)(clyBounds/clxBounds);
  685.    lxWork = clyBounds%clxBounds;
  686.    alScale[0] = MAKEULONG((USHORT)((lxWork * 0x10000)/clxBounds), sxWork);
  687.  
  688.    /* Calculate the x/y ratio of the picture. */
  689.    syWork = clyPict/clxPict;
  690.    lyWork = clyPict%clxPict;
  691.    alScale[1] = MAKEULONG((USHORT)((lyWork * 0x10000)/clxPict), syWork);
  692.    /*
  693.     * Compare the two ratios to determine the dimension along which
  694.     * scaling is to be provided, so that the picture fits into the
  695.     * area and its aspect ratio is preserved.  The same scaling is
  696.     * then applied along both the x and y axes.
  697.     */
  698.    if (alScale[0] > alScale[1])
  699.    {
  700.       sxWork = clxBounds/clxPict;
  701.       lxWork = clxBounds%clxPict;
  702.       alScale[0] = MAKEULONG((USHORT)((lxWork * 0x10000)/clxPict), sxWork);
  703.       alScale[1] = alScale[0];
  704.    }
  705.    else
  706.    {
  707.       syWork = clyBounds/clyPict;
  708.       lyWork = clyBounds%clyPict;
  709.       alScale[1] = MAKEULONG((USHORT)((lyWork * 0x10000)/clyPict), syWork);
  710.       alScale[0] = alScale[1];
  711.    }
  712.  
  713.    GpiScale(hpsXForm,
  714.             &matlfViewTransform,
  715.             TRANSFORM_REPLACE,
  716.             (PFIXED)alScale,
  717.             &ptlPictCenter);
  718.    /*
  719.     * Translate the center of the picture to the center of the
  720.     * bounding area, based on the page origin.
  721.     */
  722.    ptlPictCenter.x = (prclXform->xRight - prclXform->xLeft)/2 - ptlPictCenter.x;
  723.    ptlPictCenter.y = (prclXform->yTop - prclXform->yBottom)/2 - ptlPictCenter.y;
  724.  
  725.    GpiTranslate(hpsXForm,
  726.                 &matlfViewTransform,
  727.                 TRANSFORM_ADD,
  728.                 &ptlPictCenter);
  729.  
  730.    /* Now set the default viewing transform. */
  731.    GpiSetDefaultViewMatrix(hpsXForm,
  732.                            9L,
  733.                            &matlfViewTransform,
  734.                            TRANSFORM_REPLACE);
  735. /*
  736.  * If the picture has been transformed for the screen, we need to
  737.  * reset the scroll bar thumb size, so convert the picture height
  738.  * from world to page coordinates and together with the rect area
  739.  * height send the visible/total sizes to the scroll bar.
  740.  */
  741. } /* End of Transform */
  742.  
  743. /*************************************************************************
  744.  * Function: DoDraw
  745.  *
  746.  * The DoDraw function draws the metafile contents. The supplied
  747.  * update rectangle is used to define a clipping region, so that the
  748.  * amount of drawing required is restricted.
  749.  *
  750.  * Parameters: HPS          presentation space handle for drawing
  751.  *             PDRAW_PARM   draw parameter structure
  752.  *             HRGN         region for  drawing
  753.  *             HWND         window handle of information window
  754.  *
  755.  * Result:  LONG            return code
  756.  *
  757.  ***************************************************************************/
  758. LONG DoDraw(HPS hpsDraw, PDRAW_PARM Parm, HRGN rgn, HWND InfoWnd)
  759. {
  760.          LONG       lRc;
  761.          LONG       lOptionCount;
  762.          LONG       alOptionArray[8];
  763.          LONG       lSegCount;
  764.          LONG       lDescLen;
  765.          CHAR       achDescBuff[251];
  766.          LONG       lSaveMode;
  767.          HRGN       hrgnOld;
  768.          MATRIXLF   matlfSaveTransform;
  769.          FIXED      fxSize;
  770. static   AREABUNDLE PatternAttr =
  771.                     {CLR_BACKGROUND, 0L, 0, 0, 0, 0, 0L};
  772. static   POINTL     ptlCurr = {0, 0};
  773.  
  774.    if (rgn != (HRGN)NULLHANDLE)
  775.    {
  776.   /*
  777.    * A valid region has been passed to DoDraw. Depending on the
  778.    * setting of the flag fAllValid, the region is either copied or
  779.    * is combined with the current region using a logical OR
  780.    * operation.
  781.    */
  782.       if (Parm->fAllValid)
  783.       {
  784.          GpiCombineRegion(hpsDraw,
  785.                        Parm->hrgnInvalid,
  786.                        rgn,
  787.                        Parm->hrgnInvalid,
  788.                        CRGN_COPY);
  789.          Parm->fAllValid = FALSE;
  790.       }
  791.       else
  792.       {
  793.          GpiCombineRegion(hpsDraw,
  794.                        Parm->hrgnInvalid,
  795.                        Parm->hrgnInvalid,
  796.                        rgn,
  797.                        CRGN_OR);
  798.       } /* endif */
  799.    } /* endif */
  800.  
  801.   /* Stop here if the flag fDrawing is set to FALSE. */
  802.    if (!Parm->fDrawing)
  803.       return(FALSE);
  804.  /*
  805.   * If an invalid region exists, define a clipping region and clear
  806.   * the area within the region. If there is no invalid region, the
  807.   * whole client area is cleared.
  808.   */
  809.    if (rgn != (HRGN)NULLHANDLE)
  810.    {
  811.       lSaveMode = GpiQueryAttrMode(hpsDraw);
  812.       GpiSetAttrMode(hpsDraw, AM_PRESERVE);
  813.       GpiSetAttrs(hpsDraw,
  814.                PRIM_AREA,
  815.                ABB_COLOR,
  816.                0L,
  817.                (PBUNDLE)&PatternAttr);
  818.       GpiPaintRegion(hpsDraw, Parm->hrgnInvalid);
  819.       GpiSetAttrMode(hpsDraw, lSaveMode);
  820.       GpiPop(hpsDraw, 1L);
  821.       GpiSetClipRegion(hpsDraw, Parm->hrgnInvalid, &hrgnOld);
  822.    }
  823.    else
  824.    {
  825.       GpiErase(hpsDraw);
  826.       if (InfoWnd != (HWND)NULLHANDLE)
  827.       WinInvalidateRect(InfoWnd, (PRECTL)NULL, TRUE);
  828.    } /* endif */
  829.  /*
  830.   * If a metafile has been loaded, it is played into the presentation
  831.   * space. If GpiPlayMetaFile is successful, the setting of the
  832.   * stop-draw flag is checked. If the flag is set, it is assumed that
  833.   * GpiPlayMetaFile did not complete successfully.
  834.   * If no metafile has been loaded, a simple default picture is drawn.
  835.   */
  836. if (Parm->hmfPicture != (HMF)NULLHANDLE)
  837.    {
  838.    lDescLen = 250;
  839.    lOptionCount = 8L;
  840.    alOptionArray[PMF_SEGBASE] = 0L;
  841.    alOptionArray[PMF_LOADTYPE] = LT_DEFAULT;
  842.    alOptionArray[PMF_RESOLVE] = RS_DEFAULT;
  843.    alOptionArray[PMF_LCIDS] = LC_LOADDISC;
  844.    alOptionArray[PMF_RESET] = RES_DEFAULT;
  845.    alOptionArray[PMF_SUPPRESS] = SUP_DEFAULT;
  846.    alOptionArray[PMF_COLORTABLES] = CTAB_REPLACE;
  847.    alOptionArray[PMF_COLORREALIZABLE] = CREA_DEFAULT;
  848.    lRc = GpiPlayMetaFile(hpsDraw,
  849.                          Parm->hmfPicture,
  850.                          lOptionCount,
  851.                          (PLONG)alOptionArray,
  852.                          &lSegCount,
  853.                          lDescLen,
  854.                          (PSZ)&achDescBuff[0]
  855.                          );
  856.    if (lRc == GPI_OK)
  857.       if (LOUSHORT(WinGetLastError(Parm->hABDraw)) ==
  858.           PMERR_STOP_DRAW_OCCURRED)
  859.          lRc = DRAW_STOPPED;
  860.      /*
  861.       * Reset the presentation space, including the default viewing
  862.       * transformation, and then reapply the required transformation.
  863.       * This is to ensure that the metafile can be replayed without
  864.       * causing errors due to stored segments or new character sets,
  865.       * for example. The presentation space is not reset if called
  866.       * from the SetTransform function, as that function needs the
  867.       * accumulated boundary information.
  868.       */
  869.    if (Parm->rclBounds.xRight != 0xFFFF)
  870.       {
  871.       GpiQueryDefaultViewMatrix(hpsDraw,
  872.                                 9L,
  873.                                 &matlfSaveTransform);
  874.       GpiResetPS(hpsDraw, GRES_ALL);
  875.       GpiSetDefaultViewMatrix(hpsDraw,
  876.                               9L,
  877.                               &matlfSaveTransform,
  878.                               TRANSFORM_REPLACE);
  879.       }
  880.    }
  881. else
  882.    {
  883.    GpiSetColor(hpsDraw, CLR_BLACK);
  884.    fxSize = 0x100000;
  885.    if (Circles(hpsDraw, &ptlCurr, fxSize))
  886.       {
  887.       lRc = DRAW_OK;
  888.       }
  889.    else
  890.       {
  891.       lRc = DRAW_STOPPED;
  892.       }
  893.    } /* endif */
  894.  
  895.   /* Clear any clipping region defined earlier. */
  896.    if (rgn != (HRGN)NULLHANDLE)
  897.       GpiSetClipRegion(hpsDraw, (HRGN)NULLHANDLE, &hrgnOld);
  898.     /*
  899.      * If the drawing failed, the flag fAllValid is not set. This
  900.      * ensures that, the next time through, the region will be combined
  901.      * with the new invalid region.
  902.      */
  903.    if (lRc == DRAW_OK)
  904.       if (rgn != (HRGN)NULLHANDLE)
  905.          Parm->fAllValid = TRUE;
  906.    return(lRc);
  907. } /* End of DoDraw */
  908.  
  909. /*************************************************************************
  910.  * Function: Circles
  911.  *
  912.  *  The Circles function draws a circle and then calls itself to
  913.  *  draw four further circles within the first one. It will
  914.  *  repeat this until the circles reach a certain size or if StopDraw
  915.  *  has been set for the presentation space.
  916.  *
  917.  * Parameters: HPS          presentation space handle for drawing
  918.  *             PPOINTL      point  structure
  919.  *             FIXED        size
  920.  *
  921.  * Result:  BOOL            return code, TRUE if ok
  922.  *
  923.  ***************************************************************************/
  924. BOOL Circles(HPS hpsDraw, PPOINTL pptlCur, FIXED fxSize)
  925. {
  926.          LONG      i;
  927.          POINTL     ptlWork;
  928.          FIXED      fxWork;
  929. static   LONG       xOff[4] = {0, 1, 0, -1};
  930. static   LONG       yOff[4] = {1, 0, -1, 0};
  931. static   LONG       ColorTab[] =
  932.                     {
  933.                     CLR_BLACK,
  934.                     CLR_RED,
  935.                     CLR_GREEN,
  936.                     CLR_BLUE
  937.                     };
  938.  
  939.   /* Don't continue if StopDraw is set */
  940.    if (GpiQueryStopDraw(hpsDraw) == SDW_ON)
  941.       return(FALSE);
  942.   /* Don't continue if the circles are getting too small */
  943.    if (fxSize < 0x10000)
  944.       return(TRUE);
  945.   /* Draw a circle of the required size at the required position. */
  946.    GpiSetCurrentPosition(hpsDraw, pptlCur);
  947.    GpiFullArc(hpsDraw, DRO_OUTLINE, fxSize);
  948.  /*
  949.   * Now draw a further 4 circles, half the current size, within the
  950.   * one just drawn.
  951.   */
  952.    fxWork = fxSize >> 1;
  953.    for (i = 0; i < 4; i++)
  954.    {
  955.       ptlWork.x = pptlCur->x + xOff[i]*(FIXEDINT(fxSize)>>1);
  956.       ptlWork.y = pptlCur->y + yOff[i]*(FIXEDINT(fxSize)>>1);
  957.       GpiSetColor(hpsDraw, ColorTab[i]);
  958.       if (!Circles(hpsDraw, &ptlWork, fxWork))
  959.          return(FALSE);
  960.    }
  961.    return(TRUE);
  962. } /* End of Circles */
  963.  
  964. /*************************************************************************
  965.  * Function: DisplayMessage
  966.  *
  967.  * Display a message in the client area.
  968.  *
  969.  * Parameters: HWND         window handle
  970.  *             PSZ          message to display
  971.  *
  972.  * Result:  none
  973.  *
  974.  ***************************************************************************/
  975. VOID DisplayMessage(HWND hwndDraw, PSZ Message)
  976. {
  977.    RECTL      rDraw;
  978.    PFONTMETRICS FontMetrics;
  979.    POINTL     MessagePos;
  980.    HPS        hpsDraw;
  981.  
  982.    WinQueryWindowRect(hwndDraw, &rDraw);               /* query window size */
  983.  
  984.    hpsDraw = WinGetPS(hwndDraw);
  985.  
  986.    WinFillRect(hpsDraw, &rDraw, CLR_BACKGROUND);          /* clear window   */
  987.  
  988.    FontMetrics = (PFONTMETRICS)malloc(sizeof(FONTMETRICS)); /* query fontsize */
  989.  
  990.    GpiQueryFontMetrics(hpsDraw, (LONG)sizeof(FONTMETRICS), FontMetrics);
  991.  
  992.    MessagePos.x = 4L;                                   /* set message position*/
  993.    MessagePos.y = rDraw.yTop - FontMetrics->lMaxAscender;
  994.  
  995.    GpiCharStringAt(hpsDraw, &MessagePos,                     /* display message*/
  996.                    (LONG)strlen(Message), Message);
  997.  
  998.    WinReleasePS(hpsDraw);
  999.  
  1000.    free((PVOID)FontMetrics);
  1001. } /* End of DisplayMessage */
  1002.  
  1003. /*************************************************************************
  1004.  * Function: DisplayErrorMessage
  1005.  *
  1006.  * Display a error message in a message box.
  1007.  *
  1008.  * Parameters: HWND         window handle
  1009.  *             PSZ          message to display
  1010.  *
  1011.  * Result:  none
  1012.  *
  1013.  ***************************************************************************/
  1014. VOID DisplayErrorMessage( HWND hwndDraw, PSZ Message)
  1015. {
  1016.    RECTL      rDraw;
  1017.    HPS        hpsDraw;
  1018.  
  1019.    /* Display the error message */
  1020.    WinMessageBox( HWND_DESKTOP, hwndDraw, Message,
  1021.                                       "Graphics Sample Application", 0, MB_OK);
  1022.  
  1023.    /* Clear the client area */
  1024.    WinQueryWindowRect( hwndDraw, &rDraw);              /* query window size */
  1025.  
  1026.    hpsDraw = WinGetPS( hwndDraw);
  1027.  
  1028.    WinFillRect( hpsDraw, &rDraw, CLR_BACKGROUND);         /* clear window   */
  1029.  
  1030.    WinReleasePS(hpsDraw);
  1031. }  /* End of DisplayErrorMessage */
  1032.  
  1033. /**************************************************************************
  1034.  * Function: PrintGpiMeta
  1035.  *
  1036.  * Create a structure with the printer specific data and send a message to
  1037.  * the print thread to print GPI or METAFILE.
  1038.  *
  1039.  * Parameters: PMAIN_PARM   main parameter structure
  1040.  *             ULONG        identifier if GPI or METAFILE
  1041.  *
  1042.  * Result:  none
  1043.  *
  1044.  ***************************************************************************/
  1045. VOID PrintGpiMeta(PMAIN_PARM Parm, ULONG Message)
  1046. {
  1047.    PPRTTXT_DATA PrintData;
  1048.    PDRAW_PARM PrtParm;
  1049.    /* definitions for print */
  1050.  
  1051.    /* Allocate Space for PrintData */
  1052.    PrintData = malloc(sizeof(PRTTXT_DATA));
  1053.  
  1054.    /* Allocate Space for PrtParm which is used in Draw-Functions */
  1055.    PrtParm = (PDRAW_PARM)malloc(sizeof(DRAW_PARM));
  1056.  
  1057.    /* now copy all additional information to the print structure */
  1058.    strcpy(PrintData->QueueName, Parm->QueueName);
  1059.    strcpy(PrintData->DriverName, Parm->DriverName);
  1060.  
  1061.    /* Allocate space for DriverData */
  1062.    PrintData->DriverData = malloc(Parm->DriverDataLength);
  1063.  
  1064.    memcpy(PrintData->DriverData, Parm->DriverData, Parm->DriverDataLength);
  1065.    memcpy((PSZ)PrtParm, (PSZ)&Parm->DrawParm, sizeof(DRAW_PARM));
  1066.    PrtParm->fDrawing = TRUE;
  1067.    PrintData->hWndFrame = Parm->hWndFrame;
  1068.  
  1069.    strcpy(PrintData->Jobname, ((Message == DRAW_GPI) ? "GPI" : "METAFILE"));
  1070.  
  1071.    /* now send the print thread a message to print GPI or METAFILE */
  1072.    WinPostQueueMsg(Parm->hMQPrint, Message,
  1073.                 MPFROMP(PrintData), MPFROMP(PrtParm));
  1074. } /* End of PrintGpiMeta */
  1075.  
  1076. /*************************************************************************
  1077.  * Function: PrintThread
  1078.  *
  1079.  * PrintThread is the asynchronous printing thread.  It performs all
  1080.  * printing, text and graphic.
  1081.  * It obtains an anchor block handle and creates a message queue.
  1082.  *
  1083.  * Parameters: PMAIN_PARM   main parameter structure
  1084.  *
  1085.  * Result:  none
  1086.  *
  1087.  ***************************************************************************/
  1088. VOID PrintThread(PMAIN_PARM Parm)
  1089. {
  1090.          HAB        habPrint;
  1091. static   CHAR       PrintThreadErrorTitle[] = "Print Thread Error:";
  1092. static   CHAR       PrintThreadError1[] = "WinInitialize failed!";
  1093. static   CHAR       PrintThreadError2[] = "Cannot Create Message Queue.";
  1094.          BOOL       Loop = TRUE;
  1095.          QMSG       qmsgAsync;      /* Message structure for WinGetMsg  */
  1096.  /*
  1097.   * Obtain an anchor block handle. If this fails, the thread and
  1098.   * the application are terminated.
  1099.   */
  1100.    if ((habPrint = WinInitialize(0L)) == (HAB)NULLHANDLE)
  1101.    {
  1102.       WinPostMsg(Parm->hWndFrame,
  1103.               WM_USER_END_ON_ERROR,
  1104.               MPFROMP(PrintThreadErrorTitle),
  1105.               MPFROMP(PrintThreadError1));
  1106.       DosExit(EXIT_THREAD, 0);
  1107.    }
  1108.  /*
  1109.   *Create a message queue. If this fails, the thread and the
  1110.   * application are terminated.
  1111.   */
  1112.    if ((Parm->hMQPrint = WinCreateMsgQueue(habPrint, 0L)) == (HMQ)NULLHANDLE)
  1113.    {
  1114.       WinTerminate(habPrint);
  1115.       WinPostMsg(Parm->hWndFrame,
  1116.               WM_USER_END_ON_ERROR,
  1117.               MPFROMP(PrintThreadErrorTitle),
  1118.               MPFROMP(PrintThreadError2));
  1119.       DosExit(EXIT_THREAD, 0);
  1120.    }
  1121.  
  1122.    DosSetPriority(PRTYS_THREAD, PRTYC_NOCHANGE, 1L, 0L);
  1123.  
  1124.    while (Loop)
  1125.    {
  1126.       WinGetMsg(habPrint, &qmsgAsync, (HWND)NULLHANDLE, 0L, 0L);
  1127.       switch (qmsgAsync.msg)
  1128.       {
  1129.       case STOP_THREAD:                /* end thread        */
  1130.            Loop = FALSE;
  1131.            break;
  1132.       case DRAW_GPI:                   /* print GPI/METAFILE   */
  1133.       case DRAW_META:
  1134.            MetaOutput((PDRAW_PARM)PVOIDFROMMP(qmsgAsync.mp2),
  1135.                       (PPRTTXT_DATA)PVOIDFROMMP(qmsgAsync.mp1),
  1136.                       habPrint);
  1137.            break;
  1138.       }
  1139.    }
  1140.  
  1141.    DosSetPriority(PRTYS_THREAD, PRTYC_IDLETIME, 0L, 0L);
  1142.  
  1143.    WinDestroyMsgQueue(Parm->hMQPrint); /* Destroy this thread's message queue */
  1144.  
  1145.    WinTerminate(habPrint);
  1146.  
  1147.    DosPostEventSem(Parm->hevsemPrintEnds);
  1148.  
  1149.    DosExit(EXIT_THREAD, 0);
  1150. } /* End of PrintThread */
  1151.  
  1152. /**************************************************************************
  1153.  * Function: PreparePrinting
  1154.  *
  1155.  * Create device context and presentation space for printing
  1156.  *
  1157.  * Parameters: PHPS         presentations space handle for printing(return)
  1158.  *             PHDC         device context handle for printing (return)
  1159.  *             HAB          anchor block handle of the print process
  1160.  *             PSIZEL       size of the presentation space (return)
  1161.  *             PRESOLUTION  resolution of the device (return)
  1162.  *             PPRTTXT_DATA print data structure
  1163.  *             PSIZEL       device capabilities
  1164.  *
  1165.  * Result:  none
  1166.  *
  1167.  ***************************************************************************/
  1168. static VOID PreparePrinting(PHPS hpsPrint,
  1169.                             PHDC hdcPrinter,
  1170.                             HAB habPrint,
  1171.                             PSIZEL sizlPS,
  1172.                             PRESOLUTION Resolution,
  1173.                             PPRTTXT_DATA PrintData,
  1174.                             PSIZEL DevCaps)
  1175. {
  1176.          DEVOPENSTRUC dopPrinter;
  1177.          ULONG     i1;
  1178. static   CHAR       Comment[] = "Graphic";
  1179.          LONG       alDevInfo[DEVINFOSIZE];
  1180.  
  1181.    /* initialize DEVOPENSTRUC */
  1182.    memset((PVOID)&dopPrinter, '\0', sizeof(DEVOPENSTRUC));
  1183.    dopPrinter.pszLogAddress = PrintData->QueueName;
  1184.    i1 = strcspn(PrintData->DriverName, ".");
  1185.    PrintData->DriverName[i1] = '\0';
  1186.    dopPrinter.pszDriverName = PrintData->DriverName;
  1187.    dopPrinter.pdriv = (PDRIVDATA)PrintData->DriverData;
  1188.    dopPrinter.pszDataType = NULL;
  1189.  
  1190.    dopPrinter.pszComment = Comment;
  1191.    /* Open device context for printer */
  1192.    *hdcPrinter = DevOpenDC(habPrint,
  1193.                            OD_QUEUED,
  1194.                            (PSZ)"*",
  1195.                            5L,
  1196.                            (PDEVOPENDATA)&dopPrinter,
  1197.                            (HDC)NULLHANDLE);
  1198.    /* Query Height and Width of the printer */
  1199.  
  1200.    DevQueryCaps(*hdcPrinter,
  1201.                 0L,
  1202.                 (LONG)DEVINFOSIZE,
  1203.                 alDevInfo);
  1204.  
  1205.    sizlPS->cx = (alDevInfo[CAPS_WIDTH] * 10000) /
  1206.                 alDevInfo[CAPS_HORIZONTAL_RESOLUTION];
  1207.  
  1208.    sizlPS->cy = (alDevInfo[CAPS_HEIGHT] * 10000) /
  1209.                 alDevInfo[CAPS_VERTICAL_RESOLUTION];
  1210.  
  1211.    /* return device capabilities if pointer not NULL */
  1212.    if (DevCaps != (PSIZEL)NULL)
  1213.       {
  1214.       DevCaps->cx = alDevInfo[CAPS_WIDTH];
  1215.       DevCaps->cy = alDevInfo[CAPS_HEIGHT];
  1216.       }
  1217.  
  1218.    Resolution->x = (alDevInfo[CAPS_HORIZONTAL_FONT_RES] * 10000) /
  1219.                            alDevInfo[CAPS_HORIZONTAL_RESOLUTION];
  1220.    Resolution->y = (alDevInfo[CAPS_VERTICAL_FONT_RES] * 10000) /
  1221.                            alDevInfo[CAPS_VERTICAL_RESOLUTION];
  1222.  
  1223.    /* create presentation space */
  1224.    *hpsPrint = GpiCreatePS(habPrint,
  1225.                            *hdcPrinter,
  1226.                            sizlPS,
  1227.                            PU_LOMETRIC | GPIA_ASSOC | GPIT_NORMAL);
  1228. } /* End of PreparePrinting */
  1229.  
  1230. /**************************************************************************
  1231.  * Function: MetaOutput
  1232.  *
  1233.  * Create spoolfile for GPI or METAFILE.
  1234.  *
  1235.  * Parameters: PDRAW_PARM   draw parameter structure
  1236.  *             PPRTTXT_DATA print parameter structure
  1237.  *             HAB          anchor block handle of the print process
  1238.  *
  1239.  * Result:  none
  1240.  *
  1241.  ***************************************************************************/
  1242. static VOID MetaOutput(PDRAW_PARM PrtParm,
  1243.                        PPRTTXT_DATA PrintData,
  1244.                        HAB habPrint)
  1245. {
  1246.    HDC        hdcPrinter;
  1247.    RESOLUTION Resolution;
  1248.    SIZEL      sizlPS;
  1249.    HPS        hpsPrint;
  1250.    ULONG      JobNr;
  1251.    ULONG      Dummy;
  1252.    RECTL      rclPrinter;
  1253.  
  1254.    PrtParm->hABDraw = habPrint;
  1255.  
  1256.    /* create device context and presentations space for printing */
  1257.    PreparePrinting(&hpsPrint, &hdcPrinter, habPrint,
  1258.                    &sizlPS, &Resolution, PrintData, (PSIZEL)NULL);
  1259.  
  1260.    /* disable menuitem "Print" */
  1261.    WinPostMsg(PrintData->hWndFrame, SET_PRINT_STATUS, MPFROMSHORT(1),
  1262.               MPFROMLONG(hpsPrint));
  1263.  
  1264.    /* Start Document */
  1265.    DevEscape(hdcPrinter,
  1266.              DEVESC_STARTDOC,
  1267.              (LONG)strlen(PrintData->Jobname),
  1268.              (PSZ)PrintData->Jobname,
  1269.              0L,
  1270.              (PSZ)NULL);
  1271.  
  1272.    rclPrinter.xLeft = rclPrinter.yBottom = 0;
  1273.    rclPrinter.xRight = sizlPS.cx;
  1274.    rclPrinter.yTop = sizlPS.cy;
  1275.  
  1276.    /* draw GPI/METAFILE to the print presentation space */
  1277.    Transform(PrtParm, hpsPrint, &rclPrinter);
  1278.  
  1279.    if (DoDraw(hpsPrint, PrtParm, (HRGN)NULLHANDLE, (HWND)NULLHANDLE) != DRAW_OK)
  1280.       DevEscape(hdcPrinter,
  1281.                 DEVESC_ABORTDOC,
  1282.                 0L,
  1283.                 (PSZ)NULL,
  1284.                 (PLONG)NULL,
  1285.                 (PSZ)NULL);
  1286.    else
  1287.       {
  1288.       DevEscape(hdcPrinter, DEVESC_NEWFRAME,
  1289.                 0L, (PSZ)NULL, (PLONG)NULL, (PSZ)NULL);
  1290.       DevEscape(hdcPrinter,
  1291.                 DEVESC_ENDDOC,
  1292.                 0L,
  1293.                 (PSZ)NULL,
  1294.                 (PLONG)&Dummy,
  1295.                 (PSZ)&JobNr);
  1296.       }
  1297.  
  1298.    GpiAssociate(hpsPrint, (HDC)NULLHANDLE);
  1299.    GpiDestroyPS(hpsPrint);
  1300.    DevCloseDC(hdcPrinter);
  1301.  
  1302.    /* Release Data fields */
  1303.    free((PVOID)PrintData->DriverData);
  1304.  
  1305.    WinPostMsg(PrintData->hWndFrame, SET_PRINT_STATUS, MPVOID, MPVOID);
  1306.  
  1307.    free((PVOID)PrintData);
  1308. } /* End of MetaOutput */
  1309.  
  1310. /*************************************************************************
  1311.  * Function: QueryPrinterQueue
  1312.  *
  1313.  * query all printer queues, display them in a listbox an do a selection.
  1314.  *
  1315.  * Parameters: HAB          anchor block handle of main process
  1316.  *             HWND         window handle of frame window
  1317.  *             PVOID        pointer to driver data structure
  1318.  *             PULONG       length of driver data
  1319.  *             PSZ          (pre)selected queue name
  1320.  *             PSZ          (pre)selected printer driver name
  1321.  *
  1322.  * Result:  BOOL            TRUE if printer changed, otherwise FALSE
  1323.  *
  1324.  ***************************************************************************/
  1325. BOOL QueryPrinterQueue(HAB hAB,
  1326.                        HWND hWnd,
  1327.                        PVOID *DriverData,
  1328.                        PULONG DriverDataLength,
  1329.                        PSZ QueueName,
  1330.                        PSZ DriverName)
  1331. {
  1332.          PSZ        pszQueueNames;
  1333.          PSZ        pszQueueName;
  1334.          CHAR       DefaultQueue[10];
  1335. static   CHAR       pm_spooler_queue_descr[] = "PM_SPOOLER_QUEUE_DESCR";
  1336.          PRINTER_SEL_PARM PrtSelParm;
  1337.          ULONG     i1;
  1338.          BOOL       rc;
  1339.          LONG      SelectedItem;
  1340.  
  1341.    /* initialize printer selection structure */
  1342.    PrtSelParm.ulLen = sizeof(PRINTER_SEL_PARM);
  1343.    PrtSelParm.ulType = 0L;                   /* reserved, must be zero     */
  1344.    /*
  1345.     * First query all printer queues with a default driver name defined on it
  1346.     * If it fails, bring up a Message Box
  1347.     */
  1348.    if ((pszQueueNames = GetQueueNamesString(&PrtSelParm.QueuesCount)) == NULL)
  1349.       {
  1350.       WinMessageBox(HWND_DESKTOP,
  1351.                     hWnd,
  1352.                     (PSZ)"There are no Printer Queues defined, use PrintManager to define at least one Queue and Printer.",
  1353.                     (PSZ)NULL,
  1354.                     0L,
  1355.                     MB_OK | MB_MOVEABLE | MB_CUAWARNING | MB_APPLMODAL);
  1356.       return(FALSE);
  1357.       }
  1358.  
  1359.    /* if no Queue is connected to a Printer Driver, bring up a message */
  1360.    if (PrtSelParm.QueuesCount == 0)
  1361.       {
  1362.       WinMessageBox(HWND_DESKTOP,
  1363.                     hWnd,
  1364.                     (PSZ)"There are no Printer Queues connected to a Printer Driver, use PrintManager to connect a Printer to the Queue.",
  1365.                     (PSZ)NULL,
  1366.                     0L,
  1367.                     MB_OK | MB_MOVEABLE | MB_CUAWARNING | MB_APPLMODAL);
  1368.       free(pszQueueNames);
  1369.       return(FALSE);
  1370.       }
  1371.  
  1372.    /* Allocate Printer Selection Table */
  1373.    PrtSelParm.QueueSelTable = malloc(PrtSelParm.QueuesCount * sizeof(QUEUE_SEL));
  1374.    /*
  1375.     * Fill Printer Selection Table with Queue-Names and connected Printer
  1376.     * Driver Names
  1377.     */
  1378.    for (pszQueueName = pszQueueNames, i1 = 0;
  1379.         *pszQueueName;
  1380.         pszQueueName = &pszQueueName[strlen(pszQueueName) + 1], i1++)
  1381.       {
  1382.       PrfQueryProfileString(HINI_PROFILE,
  1383.                             (PSZ)pm_spooler_queue_dd,
  1384.                             (PSZ)pszQueueName,
  1385.                             (PSZ)NULL,
  1386.                             (PVOID)PrtSelParm.QueueSelTable[i1].DriverName,
  1387.                             (ULONG)DRIVERNAME_LENGTH);
  1388.  
  1389.       strcpy(PrtSelParm.QueueSelTable[i1].QueueName, pszQueueName);
  1390.       /* eliminate ; */
  1391.       PrtSelParm.QueueSelTable[i1].DriverName[strlen(
  1392.              PrtSelParm.QueueSelTable[i1].DriverName) - 1] = '\0';
  1393.  
  1394.       /* Query Queue-Description */
  1395.       PrfQueryProfileString(HINI_PROFILE,
  1396.                             (PSZ)pm_spooler_queue_descr,
  1397.                             (PSZ)pszQueueName,
  1398.                             (PSZ)NULL,
  1399.                             (PVOID)PrtSelParm.QueueSelTable[i1].QueueDescription,
  1400.                             (ULONG)DESCRIPTION_LENGTH);
  1401.       /* eliminate ; */
  1402.       PrtSelParm.QueueSelTable[i1].QueueDescription[strlen(
  1403.              PrtSelParm.QueueSelTable[i1].QueueDescription) - 1] = '\0';
  1404.       PrtSelParm.QueueSelTable[i1].DriverData = NULL;
  1405.       PrtSelParm.QueueSelTable[i1].DriverDataLength = 0;
  1406.       }
  1407.  
  1408.    free(pszQueueNames);                /* free queue names */
  1409.  
  1410.    /* Sort Queue-Descriptions */
  1411.    qsort((void *)PrtSelParm.QueueSelTable,
  1412.          (size_t)PrtSelParm.QueuesCount,
  1413.          (size_t)sizeof(QUEUE_SEL),
  1414.           SortQueues);
  1415.  
  1416.    /* Define preselected queue */
  1417.    PrtSelParm.SelectedItem = -1;
  1418.  
  1419.    strcpy(DefaultQueue, QueueName);
  1420.    /*
  1421.     * if Queue Name not given or not found in table, query Default Queue
  1422.     * if queue name defined, test if the queue is valid
  1423.     */
  1424.    if (QueryDefaultQueue(DefaultQueue, (PSZ)NULL))
  1425.       {
  1426.       /* If default Queue defined and connected to Driver, search table */
  1427.       for (i1 = 0; i1 < PrtSelParm.QueuesCount; i1++)
  1428.          {
  1429.          /* if found in table, set Selected Item to entry */
  1430.          if (!strcmp(DefaultQueue, PrtSelParm.QueueSelTable[i1].QueueName))
  1431.             {
  1432.             PrtSelParm.SelectedItem = SelectedItem = i1;
  1433.             PrtSelParm.QueueSelTable[i1].DriverData = *DriverData;
  1434.             PrtSelParm.QueueSelTable[i1].DriverDataLength = *DriverDataLength;
  1435.             break;
  1436.             }
  1437.          }
  1438.       }
  1439.  
  1440.    /* if no queue defined or queue not valid, free driver data if allocated */
  1441.    if ((SelectedItem = PrtSelParm.SelectedItem) == -1)
  1442.       if (*DriverData != NULL)
  1443.       {
  1444.          free(*DriverData);
  1445.          *DriverDataLength = 0;
  1446.          *DriverData = NULL;
  1447.       }
  1448.  
  1449.    PrtSelParm.hAB = hAB;
  1450.  
  1451.    /* if an other printer is selected, copy the data to the function parameters */
  1452.    if (PrtSelParm.SelectedItem != SelectedItem)
  1453.       {
  1454.       rc = TRUE;
  1455.       strcpy(QueueName,
  1456.              PrtSelParm.QueueSelTable[PrtSelParm.SelectedItem].QueueName);
  1457.       strcpy(DriverName,
  1458.              PrtSelParm.QueueSelTable[PrtSelParm.SelectedItem].DriverName);
  1459.       *DriverData = PrtSelParm.QueueSelTable[PrtSelParm.SelectedItem].DriverData;
  1460.       *DriverDataLength = PrtSelParm.QueueSelTable[PrtSelParm.SelectedItem].DriverDataLength;
  1461.       }
  1462.    else
  1463.       rc = FALSE;
  1464.  
  1465.    /* free fields in queue selection table */
  1466.    for (i1 = 0; i1 < PrtSelParm.QueuesCount; i1++)
  1467.       if ((i1 != (ULONG)PrtSelParm.SelectedItem) &&
  1468.           (PrtSelParm.QueueSelTable[i1].DriverData != NULL))
  1469.       {
  1470.          free(PrtSelParm.QueueSelTable[i1].DriverData);
  1471.       }
  1472.  
  1473.    free(PrtSelParm.QueueSelTable);
  1474.  
  1475.    return(rc);
  1476. } /* End of QueryPrinterQueue */
  1477.  
  1478. /**************************************************************************
  1479.  * Function: SortQueues
  1480.  *
  1481.  * Compare routine for sorting the queue names table.
  1482.  *
  1483.  * Parameters: PQUEUE_SEL   pointer to queue selection structure 1
  1484.  *             PQUEUE_SEL   pointer to queue selection structure 2
  1485.  *
  1486.  * Result:  int             result of comparison
  1487.  *
  1488.  ***************************************************************************/
  1489. static int _Optlink SortQueues(const void *Queue1, const void *Queue2)
  1490. {
  1491.    return(strcmp(((PQUEUE_SEL)Queue1)->QueueDescription,
  1492.               ((PQUEUE_SEL)Queue2)->QueueDescription));
  1493. } /* End of SortQueues */
  1494.  
  1495. /**************************************************************************
  1496.  * Function: QueryDefaultQueue
  1497.  *
  1498.  * if queue name passed, test if queue is valid, otherwise query default
  1499.  * printer queue.
  1500.  *
  1501.  * Parameters: PSZ          queue name or nullstring
  1502.  *             PSZ          queue driver name or nullstring
  1503.  *
  1504.  * Result:  BOOL            TRUE if driver is connected to queue
  1505.  *
  1506.  ***************************************************************************/
  1507. BOOL QueryDefaultQueue(PSZ QueueName, PSZ QueueDriver)
  1508. {
  1509.          PSZ        pszQueueNames;
  1510.          PSZ        pszQueueName;
  1511.          CHAR       QueueNameTemp[DRIVERNAME_LENGTH];
  1512.          BOOL       rc = FALSE;
  1513. static   CHAR       pm_spooler[] = "PM_SPOOLER";
  1514.  
  1515. /* if no queue name passed, query default printer queue */
  1516.    if (!*QueueName)
  1517.    {
  1518.       if (!PrfQueryProfileString(HINI_PROFILE,
  1519.                               (PSZ)pm_spooler,
  1520.                               (PSZ)"QUEUE",
  1521.                               NULL,
  1522.                               (PSZ)QueueNameTemp,
  1523.                               (LONG)sizeof(QueueNameTemp)))
  1524.       {
  1525.          *QueueName = '\0';      /* if no default printer queue found,  */
  1526.          return(FALSE);          /* return FALSE                        */
  1527.       }
  1528.  
  1529.    /* cut ; */
  1530.       QueueNameTemp[strlen(QueueNameTemp) - 1] = '\0';
  1531.  
  1532.    /* return Queue Name */
  1533.       strcpy(QueueName, QueueNameTemp);
  1534.    }
  1535.  
  1536. /* Get String with all Queue Names to which a driver is connected */
  1537.    if ((pszQueueNames = GetQueueNamesString((PULONG)NULL)) != NULL)
  1538.    {
  1539.    /* Compare all Queue Names with default Queue Name */
  1540.       for (pszQueueName = pszQueueNames;
  1541.         *pszQueueName;
  1542.         pszQueueName = &pszQueueName[strlen(pszQueueName) + 1])
  1543.       {
  1544.          if (!strcmp(pszQueueName, QueueName))
  1545.          {
  1546.          /* if Queue found in string, set rc = TRUE */
  1547.             rc = TRUE;
  1548.             break;
  1549.          }
  1550.       }
  1551.    /* Free Queue Names string */
  1552.       free(pszQueueNames);
  1553.    }
  1554.  
  1555. /* if queue driver defined and Queue found, query queue driver name  */
  1556.    if ((QueueDriver != (PSZ)NULL) && rc)
  1557.    {
  1558.       PrfQueryProfileString(HINI_PROFILE,
  1559.                          (PSZ)pm_spooler_queue_dd,
  1560.                          (PSZ)QueueName,
  1561.                          NULL,
  1562.                          (PSZ)QueueNameTemp,
  1563.                          DRIVERNAME_LENGTH);
  1564.       QueueNameTemp[strlen(QueueNameTemp) - 1] = '\0';
  1565.       strcpy(QueueDriver, QueueNameTemp);
  1566.    }
  1567.  
  1568.    return(rc);
  1569. } /* End of QueryDefaultQueue */
  1570.  
  1571. /**************************************************************************
  1572.  * Function: GetQueueNamesString
  1573.  *
  1574.  * Get a string with all Queue Names with a connected Driver Name.
  1575.  *
  1576.  * Parameters: PULONG       pointer to ULONG for returning the queue count
  1577.  *
  1578.  * Result:  PSZ             string with all queuenames
  1579.  *
  1580.  ***************************************************************************/
  1581. static PSZ GetQueueNamesString(PULONG QCount)
  1582. {
  1583.    ULONG      QueueNamesLength;
  1584.    PSZ        pszQueueNames;
  1585.    PSZ        pszQueueNamesWithDriver;
  1586.    PSZ        pszQueueName;
  1587.    PSZ        pszQueueStringPtr;
  1588.    PSZ        pszQueueDriver;
  1589.    ULONG     QueuesCt;
  1590.  
  1591.    /* Query size of Queues-String, if not defined return NULL */
  1592.    if (!PrfQueryProfileSize(HINI_PROFILE,
  1593.                             (PSZ)pm_spooler_queue_dd,
  1594.                             (PSZ)NULL,
  1595.                             (PULONG)&QueueNamesLength))
  1596.       return((PSZ)NULL);
  1597.  
  1598.    /* Allocate Space for Queue Names, if it fails return NULL */
  1599.    if ((pszQueueNames = malloc(QueueNamesLength)) == NULL)
  1600.       return((PSZ)NULL);
  1601.  
  1602.    /* Allocate Space for Queue Names with driver, if it fails return NULL */
  1603.    if ((pszQueueNamesWithDriver = malloc(QueueNamesLength)) == NULL)
  1604.       {
  1605.       free(pszQueueNames);
  1606.       return((PSZ)NULL);
  1607.       }
  1608.  
  1609.    /* Get Queue Names */
  1610.    PrfQueryProfileString(HINI_PROFILE,
  1611.                          (PSZ)pm_spooler_queue_dd,
  1612.                          (PSZ)NULL,
  1613.                          (PSZ)NULL,
  1614.                          (PVOID)pszQueueNames,
  1615.                          QueueNamesLength);
  1616.    /*
  1617.     * Query Count of Queues to which a Printer Driver is connected and
  1618.     * copy all Queue Names to pszQueueNamesWithDriver
  1619.     * Allocate space for Queue Driver Name, if it fails return NULL
  1620.     */
  1621.    if ((pszQueueDriver = malloc(DRIVERNAME_LENGTH)) == NULL)
  1622.       {
  1623.       free(pszQueueNames);
  1624.       free(pszQueueNamesWithDriver);
  1625.       return((PSZ)NULL);
  1626.       }
  1627.  
  1628.    pszQueueStringPtr = pszQueueNamesWithDriver;
  1629.  
  1630.    for (pszQueueName = pszQueueNames, QueuesCt = 0;
  1631.         *pszQueueName;
  1632.         pszQueueName = &pszQueueName[strlen(pszQueueName) + 1])
  1633.    {
  1634.       /* Query Queue Driver */
  1635.       PrfQueryProfileString(HINI_PROFILE,
  1636.                             (PSZ)pm_spooler_queue_dd,
  1637.                             (PSZ)pszQueueName,
  1638.                             (PSZ)NULL,
  1639.                             (PVOID)pszQueueDriver,
  1640.                             DRIVERNAME_LENGTH);
  1641.       /*
  1642.        * if Queue Driver isn't a null-string, increment Queues Count
  1643.        * and copy queue name to string
  1644.        */
  1645.       if ((*pszQueueDriver != ';') && (*pszQueueDriver != '\0'))
  1646.       {
  1647.          strcpy(pszQueueStringPtr, pszQueueName);
  1648.          pszQueueStringPtr = &pszQueueStringPtr[strlen(pszQueueStringPtr) + 1];
  1649.          QueuesCt++;
  1650.       }
  1651.    }
  1652.  
  1653.    /* add a x'00' to the end of the string */
  1654.    *pszQueueStringPtr = '\0';
  1655.  
  1656.    /* free temporary fields */
  1657.    free(pszQueueDriver);
  1658.    free(pszQueueNames);
  1659.  
  1660.    /* Return Queues Count, if Pointer != NULL */
  1661.    if (QCount != (PULONG)NULL)
  1662.       *QCount = QueuesCt;
  1663.  
  1664.    /* return string with Queue Names */
  1665.    return(pszQueueNamesWithDriver);
  1666. } /* End of GetQueueNamesString */
  1667.  
  1668. /*************************************************************************
  1669.  * Function: QueryJobProperties
  1670.  *
  1671.  * Query job properties (print properties) of the printer driver.
  1672.  *
  1673.  * Parameters: HAB          anchor block handle of the process
  1674.  *             PVOID        pointer to driver data
  1675.  *             PULONG       driver data length
  1676.  *             PSZ          queue driver name
  1677.  *             PSZ          printer queue name
  1678.  *             PSZ          printer driver name
  1679.  *             BOOL         flag, if FALSE don't display the dialog
  1680.  *
  1681.  * Result:  BOOL            TRUE if ok
  1682.  *
  1683.  ***************************************************************************/
  1684. BOOL QueryJobProperties(HAB hAB,
  1685.                         PVOID *DriverData,
  1686.                         PULONG DriverDataLength,
  1687.                         PSZ QueueDriverName,
  1688.                         PSZ QueueName,
  1689.                         PSZ PrtName,
  1690.                         BOOL Flag)
  1691. {
  1692.          CHAR       DriverName[10];
  1693.          CHAR       DeviceName[64];
  1694.          LONG       rc;
  1695.          LONG      i1;
  1696.          ULONG      Length;
  1697.          PSZ        PrinterNames = NULL;
  1698.          PSZ        PrinterName;
  1699.          PSZ        PrinterInfo;
  1700.          PSZ        QueueInfo;
  1701.          PSZ        DriverInfo;
  1702.          PSZ        Queue;
  1703.          PSZ        Driver;
  1704.          BOOL       found;
  1705.          ULONG      flOptions;
  1706. static   CHAR       pm_spooler_printer[] = "PM_SPOOLER_PRINTER";
  1707. static   CHAR       separator1[] = ";";
  1708. static   CHAR       separator2[] = ",";
  1709.  
  1710. /* if flag=TRUE display the dialog, otherwise only query default properties */
  1711.    if (Flag)
  1712.       flOptions = DPDM_POSTJOBPROP;
  1713.    else
  1714.       flOptions = DPDM_QUERYJOBPROP;
  1715.  
  1716.    /* separate  driver name at the dot (e.g PLOTTERS.IBM6180) */
  1717.    if ((i1 = (LONG)strcspn(QueueDriverName, ".")) != 0L)
  1718.       {
  1719.       strncpy(DriverName, QueueDriverName, i1);
  1720.       DriverName[i1] = '\0';
  1721.       strcpy(DeviceName, &QueueDriverName[i1 + 1]);
  1722.       }
  1723.    else
  1724.       {
  1725.       strcpy(DriverName, QueueDriverName);
  1726.       *DeviceName = '\0';
  1727.       }
  1728.    /*
  1729.     * Query device name because some printer drivers need this information
  1730.     * in the DevPostDeviceModes call
  1731.     */
  1732.  
  1733.    /* Query the size needed to get the printer names */
  1734.    PrfQueryProfileSize(HINI_PROFILE,
  1735.                        (PSZ)pm_spooler_printer,
  1736.                        (PSZ)NULL,
  1737.                        (PULONG)&Length);
  1738.  
  1739.    /* Allocate space for printer names */
  1740.    PrinterNames = malloc((LONG)Length);
  1741.  
  1742.    /* Get the printer names */
  1743.    PrfQueryProfileString(HINI_PROFILE,
  1744.                          (PSZ)pm_spooler_printer,
  1745.                          (PSZ)NULL,
  1746.                          (PSZ)NULL,
  1747.                          (PVOID)PrinterNames,
  1748.                          Length);
  1749.  
  1750.    /* get Printer information and search for the specific queue */
  1751.    for (PrinterName = PrinterNames, found = FALSE;
  1752.         *PrinterName;
  1753.         PrinterName = &PrinterName[strlen(PrinterName) + 1])
  1754.       {
  1755.       /* Query the size needed to get the printer information */
  1756.       PrfQueryProfileSize(HINI_PROFILE,
  1757.                           (PSZ)pm_spooler_printer,
  1758.                           (PSZ)PrinterName,
  1759.                           (PULONG)&Length);
  1760.  
  1761.       PrinterInfo = malloc((ULONG)Length);
  1762.  
  1763.       PrfQueryProfileString(HINI_PROFILE,
  1764.                             (PSZ)pm_spooler_printer,
  1765.                             (PSZ)PrinterName,
  1766.                             (PSZ)NULL,
  1767.                             (PVOID)PrinterInfo,
  1768.                             Length);
  1769.  
  1770.       /* Get Driver Info and Queue Info of the string */
  1771.       DriverInfo = &PrinterInfo[strcspn(PrinterInfo, separator1) + 1];
  1772.       QueueInfo = &DriverInfo[strcspn(DriverInfo, separator1) + 1];
  1773.       QueueInfo[strcspn(QueueInfo, separator1)] = '\0';
  1774.       QueueInfo[-1] = '\0';
  1775.  
  1776.       /* Verify Queues with specific Queue Name */
  1777.       Queue = strchr(QueueInfo, separator2[0]);
  1778.  
  1779.       while (Queue)
  1780.          {
  1781.          /* if Queue found, compare Driver Names */
  1782.          if (!strcmp(Queue, QueueName))
  1783.             {
  1784.             Driver = strchr(DriverInfo, separator2[0]);
  1785.             while (Driver)
  1786.                {
  1787.                /* if Driver found, stop loop */
  1788.                if (!strcmp(Driver, QueueDriverName))
  1789.                   {
  1790.                   found = TRUE;
  1791.                   break;
  1792.                   }
  1793.                Driver = strchr(NULL, separator2[0]);
  1794.                }
  1795.             break;
  1796.             }
  1797.          Queue = strchr(NULL,separator2[0]);
  1798.          }
  1799.  
  1800.       free(PrinterInfo);
  1801.  
  1802.       if (found)
  1803.          break;
  1804.       }
  1805.  
  1806.    if (*DriverData == NULL)
  1807.       {
  1808.       /* Query length for Job-Properties */
  1809.       rc = DevPostDeviceModes(hAB,
  1810.                               NULL,
  1811.                               DriverName,
  1812.                               DeviceName,
  1813.                               PrinterName,
  1814.                               flOptions);
  1815.  
  1816.       if (rc <= 0L)
  1817.          {
  1818.          free(PrinterNames);
  1819.          return(FALSE);
  1820.          }
  1821.  
  1822.       *DriverData = malloc((LONG)rc);
  1823.  
  1824.       *DriverDataLength = (LONG)rc;
  1825.       }
  1826.  
  1827.    /* call DevPostDeviceModes */
  1828.    rc = DevPostDeviceModes(hAB,
  1829.                            *DriverData,
  1830.                            DriverName,
  1831.                            DeviceName,
  1832.                            PrinterName,
  1833.                            flOptions);
  1834.  
  1835.    if ((PrtName != (PSZ)NULL) && (PrinterName != NULL))
  1836.       strcpy(PrtName, PrinterName);
  1837.  
  1838.    if (PrinterNames != NULL)
  1839.       free(PrinterNames);
  1840.  
  1841.    return TRUE;
  1842. } /* End of QueryJobProperties */
  1843. /************************** End of FILE.C *********************************/
  1844.