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