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