home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c082_122 / 10.ddi / OWLPRINT.ZIP / PRINTER.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-10  |  22.2 KB  |  812 lines

  1. // ObjectWindows - (C) Copyright 1992 by Borland International
  2.  
  3. #include "printer.h"
  4. #include "mycombo.h"
  5. #include "ids.h"
  6.  
  7. #include <static.h>
  8. #include <safepool.h>
  9.  
  10. // A few private constants
  11.  
  12. #define SR_ON 32512
  13. #define SR_ERRORTEMPLATE 32513
  14. #define SR_OUTOFMEMORY 32514
  15. #define SR_OUTOFDISK 32515
  16. #define SR_PRNCANCEL 32516
  17. #define SR_PRNMGRABORT 32517
  18. #define SR_GENERROR 32518
  19. #define SR_ERRORCAPTION 32519
  20.  
  21. static BOOL UserAbort = FALSE;
  22.  
  23.  
  24. /*
  25. ** FetchStr
  26. ** Returns a pointer to the first comma delimited field pointed to
  27. ** by Str. It replaces the comma with a a null character and moves the Str
  28. ** to the beginning of the next string (skipping white space). Str will
  29. ** will point to a null character if no more strings are left. This
  30. ** routine is used to fetch strings out of text retrieved from WIN.INI.
  31. */
  32.  
  33. static Pchar FetchStr(Pchar& Str)
  34. {
  35.   Pchar result = Str;
  36.   LPSTR tempStr = Str;  // Used because AnsiNext returns an LPSTR;
  37.                         // Note that we could simply use Str instead
  38.                         // if we assumed a large data model.
  39.  
  40.   if (Str == NULL)
  41.         return result;
  42.   while ((*tempStr != '\0') && (*tempStr != ','))
  43.     tempStr = AnsiNext(tempStr);
  44.   if (*tempStr != '\0')
  45.   {
  46.         *tempStr = '\0';
  47.         tempStr++;
  48.         while (*tempStr == ' ')
  49.                 tempStr = AnsiNext(tempStr);
  50.   }
  51.   // Change Str to point to next field.
  52.   Str += int(tempStr - (LPSTR) Str);
  53.   return result;
  54. }
  55.  
  56.  
  57. /*
  58. ** newstrdup
  59. ** Allocate space for and copy a string; allocation is performed
  60. ** using 'new' so 'delete' should be used for de-allocation.
  61. */
  62.  
  63. static Pchar newstrdup(Pchar S)
  64. {
  65.   Pchar P = new char[strlen(S)+1];
  66.   strcpy(P, S);
  67.   return P;
  68. }
  69.  
  70.  
  71. // TPrintout
  72.  
  73.  
  74. TPrintout::TPrintout(Pchar ATitle)
  75. {
  76.   Title = newstrdup(ATitle);
  77.   Banding = FALSE;
  78.   ForceAllBands = TRUE;
  79. }
  80.  
  81. TPrintout::~TPrintout()
  82. {
  83.   delete Title;
  84. }
  85.  
  86. BOOL TPrintout::IsNextPage()
  87. {
  88.   return FALSE;
  89. }
  90.  
  91. // TReplaceStatic
  92.  
  93. _CLASSDEF(TReplaceStatic)
  94. class TReplaceStatic: public TStatic
  95. {
  96. private:
  97.   Pchar Text;
  98. public:
  99.   TReplaceStatic(PTWindowsObject AParent, int ResourceId, Pchar AText);
  100.   virtual ~TReplaceStatic();
  101.   virtual void SetupWindow();
  102. };
  103.  
  104. TReplaceStatic::TReplaceStatic(PTWindowsObject AParent, int ResourceId,
  105.         Pchar AText) : TStatic(AParent, ResourceId, 0)
  106. {
  107.   Text = newstrdup(AText);
  108. }
  109.  
  110. TReplaceStatic::~TReplaceStatic()
  111. {
  112.   delete Text;
  113. }
  114.  
  115. void TReplaceStatic::SetupWindow()
  116. {
  117.   char A[80];
  118.   char B[80];
  119.  
  120.   TStatic::SetupWindow();
  121.   GetText(A, sizeof(A) - 1);
  122.   LPSTR C[1];
  123.   C[0] = Text;
  124.   wvsprintf((LPSTR) B, (LPSTR) A, (LPSTR) C);
  125.   SetText(B);
  126. }
  127.  
  128.  
  129. // TPrinterAbortDlg
  130.  
  131. TPrinterAbortDlg::TPrinterAbortDlg(PTWindowsObject AParent, Pchar Template,
  132.   Pchar Title, Pchar Device, Pchar Port)
  133.   : TDialog(AParent, Template)
  134. {
  135.   new TReplaceStatic(this, ID_TITLE, Title);
  136.   new TReplaceStatic(this, ID_DEVICE, Device);
  137.   new TReplaceStatic(this, ID_PORT, Port);
  138. }
  139.  
  140. void TPrinterAbortDlg::SetupWindow()
  141. {
  142.   TDialog::SetupWindow();
  143.   EnableMenuItem(GetSystemMenu(HWindow, FALSE), SC_CLOSE, MF_GRAYED);
  144. }
  145.  
  146. void TPrinterAbortDlg::WMCommand(RTMessage)
  147. {
  148.   UserAbort = TRUE;
  149. }
  150.  
  151.  
  152. // TPrinter
  153.  
  154. /*
  155. **  This object type is an encapsulation around the Windows printer
  156. **  device interface.  After the object is initialized the Status
  157. **  field must be checked to see if the object was created correctly.
  158. **
  159. **  Examples:
  160. **   Creating a default device printing object:
  161. **
  162. **     DefaultPrinter = new TPrinter();
  163. **
  164. **   Creating a device for a specific printer:
  165. **
  166. **     PostScriptPrinter = new TPrinter();
  167. **     PostScriptPrinter->SetDevice("PostScript Printer",
  168. **       "PSCRIPT", "LPT2:");
  169. **
  170. **   Allowing the user to configure the printer:
  171. **
  172. **     DefaultPrinter->Configure(MyWindow);
  173. */
  174.  
  175. // Initialize the TPrinter object assigned to the default printer
  176.  
  177. TPrinter::TPrinter()
  178. {
  179.   Device = NULL;
  180.   Driver = NULL;
  181.   Port = NULL;
  182.   DeviceModule = 0;
  183.   DevSettings = NULL;
  184.   Error = 0;
  185.   SetDevice(NULL, NULL, NULL);  // Associate with default printer
  186. }
  187.  
  188. // Deallocate allocated resources
  189.  
  190. TPrinter::~TPrinter()
  191. {
  192.   ClearDevice();
  193. }
  194.  
  195. // Clears the association of this object with the current device
  196.  
  197. void TPrinter::ClearDevice()
  198. {
  199.   delete Device;
  200.   Device = NULL;
  201.  
  202.   delete Driver;
  203.   Driver = NULL;
  204.  
  205.   delete Port;
  206.   Port = NULL;
  207.  
  208.         if ((int)DeviceModule >= 32) {
  209.                 FreeLibrary(DeviceModule);
  210.                 DeviceModule = 0;
  211.   }
  212.   if (DevSettings)
  213.         delete [] (Pchar) DevSettings;
  214.   Status = PS_UNASSOCIATED;
  215. }
  216.  
  217.  
  218. // GetDefaultPrinter and Equal are helper functions used by
  219. // TPrinter::SetDevice
  220.  
  221. void TPrinter::GetDefaultPrinter()
  222. {
  223.   char Printer[80];
  224.   Pchar Cur;
  225.  
  226.   GetProfileString("windows", "device", "", Printer,
  227.     sizeof(Printer) - 1);
  228.   Cur = Printer;
  229.   Device = newstrdup(FetchStr(Cur));
  230.   Driver = newstrdup(FetchStr(Cur));
  231.   Port = newstrdup(FetchStr(Cur));
  232. }
  233.  
  234. static BOOL Equal(Pchar S1, Pchar S2)
  235. {
  236.   return ((S1 != NULL) && (S2 != NULL) && (strcmp(S1, S2) == 0));
  237. }
  238.  
  239. /*
  240. Associates the printer object with a new device. If the ADevice
  241. parameter is NULL the Windows default printer is used, otherwise,
  242. the parameters must be ones contained in the [devices] section
  243. of the WIN.INI file.
  244. */
  245.  
  246. void TPrinter::SetDevice(Pchar ADevice, Pchar ADriver, Pchar APort)
  247. {
  248.   char DriverName[80];
  249.   DEVMODE StubDevMode;
  250.  
  251.   if (Equal(Device, ADevice) && Equal(Driver, ADriver) &&
  252.     Equal(Port, APort))
  253.         return;
  254.   ClearDevice();
  255.   if (ADevice == NULL)
  256.     GetDefaultPrinter();
  257.   else {
  258.     Device = newstrdup(ADevice);
  259.     Driver = newstrdup(ADriver);
  260.     Port = newstrdup(APort);
  261.   }
  262.   Status = PS_OK;
  263.   strncpy(DriverName, Driver, sizeof(DriverName) - 1);
  264.   strncat(DriverName, ".DRV", sizeof(DriverName) - strlen(DriverName) - 1);
  265.   DeviceModule = LoadLibrary(DriverName);
  266.         if ((int)DeviceModule < 32)
  267.         Status = PS_INVALIDDEVICE;
  268.   else {
  269.     // Grab the DevMode procedures
  270.     ExtDeviceMode = (LPFNDEVMODE) GetProcAddress(DeviceModule, "ExtDeviceMode");
  271.     DeviceMode = (PTDeviceModeFcn) GetProcAddress(DeviceModule, "DeviceMode");
  272.     if ((DeviceMode == NULL) && (ExtDeviceMode == NULL))
  273.       Status = PS_INVALIDDEVICE;
  274.     if (ExtDeviceMode != NULL) {
  275.       // Get default printer settings
  276.       DevSettingSize = ExtDeviceMode(0, DeviceModule, &StubDevMode,
  277.         Device, Port, &StubDevMode, NULL, 0);
  278.       DevSettings = (PDEVMODE) new char[DevSettingSize];
  279.       ExtDeviceMode(0, DeviceModule, DevSettings, Device, Port,
  280.         DevSettings, NULL, DM_OUT_BUFFER);
  281.     }
  282.     else
  283.       DevSettings = NULL;  // Cannot use local settings
  284.   }
  285. }
  286.  
  287. // Configure brings up a dialog as a child of the given window
  288. // to configure the associated printer driver.
  289.  
  290. void TPrinter::Configure(PTWindowsObject Window)
  291. {
  292.   if (Status == PS_OK)
  293.     if (ExtDeviceMode == NULL) // driver only supports DevMode
  294.       // if DeviceMode == NULL, Status will != PS_OK
  295.       DeviceMode(Window->HWindow, DeviceModule, Device, Port);
  296.     else
  297.       // Request driver to modify local copy of printer settings
  298.       ExtDeviceMode(Window->HWindow, DeviceModule, DevSettings, Device,
  299.         Port, DevSettings, NULL, DM_IN_BUFFER | DM_PROMPT | DM_OUT_BUFFER);
  300. }
  301.  
  302. // Returns a device context for the associated printer, 0 if an
  303. // error occurs or Status is != PS_OK
  304.  
  305. HDC TPrinter::GetDC()
  306. {
  307.   if (Status == PS_OK)
  308.     return CreateDC(Driver, Device, Port, (LPSTR) (LPDEVMODE) DevSettings);
  309.   else
  310.     return 0;
  311. }
  312.  
  313. static BOOL ProcessDlgMsg(LPMSG PMessage, PTApplication Application)
  314. {
  315.         if (Application->KBHandlerWnd && Application->KBHandlerWnd->HWindow)
  316.            return IsDialogMessage(Application->KBHandlerWnd->HWindow, PMessage);
  317.         else
  318.            return FALSE;
  319. }
  320.  
  321. static BOOL ProcessAccels(LPMSG PMessage, PTApplication Application)
  322. {
  323.         return Application->HAccTable &&
  324.                  TranslateAccelerator(
  325.                          Application->MainWindow->HWindow, Application->HAccTable, PMessage);
  326. }
  327.  
  328. static BOOL ProcessMDIAccels(LPMSG PMessage, PTApplication Application)
  329. {
  330.         return (PTWindowsObject)(Application->MainWindow->GetClient()) &&
  331.            TranslateMDISysAccel(
  332.             ((PTWindowsObject)(Application->MainWindow->GetClient()))->HWindow, PMessage);
  333. }
  334.  
  335. static BOOL ProcessAppMsg(LPMSG PMessage)
  336. {
  337.         PTApplication Application = GetApplicationObject();
  338.  
  339.         if ( Application->KBHandlerWnd )
  340.            if ( Application->KBHandlerWnd->IsFlagSet(WB_MDICHILD) )
  341.              return ProcessMDIAccels(PMessage, Application) ||
  342.                     ProcessDlgMsg(PMessage, Application) ||
  343.                     ProcessAccels(PMessage, Application);
  344.            else
  345.             return ProcessDlgMsg(PMessage, Application) ||
  346.                    ProcessMDIAccels(PMessage, Application) ||
  347.                    ProcessAccels(PMessage, Application);
  348.         else
  349.            return ProcessMDIAccels(PMessage, Application) ||
  350.                   ProcessAccels(PMessage, Application);
  351. }
  352.  
  353. // Abort procedure used for printing
  354. BOOL FAR PASCAL _export AbortProc(HDC, short)
  355. {
  356.   MSG Msg;
  357.  
  358.   while (!UserAbort && PeekMessage(&Msg, NULL, NULL, NULL, PM_REMOVE))
  359.     if (!ProcessAppMsg(&Msg)) {
  360.       TranslateMessage(&Msg);
  361.       DispatchMessage(&Msg);
  362.     }
  363.   return (!UserAbort);
  364. }
  365.  
  366.  
  367. // Static variables used by CalcBandingFlags and Print
  368. static BOOL UseBandInfo;
  369. static HDC PrnDC;
  370. static WORD Flags;
  371. static BOOL FirstBand;
  372. static RECT BandRect;
  373. static POINT PageSize;
  374.  
  375. void TPrinter::CalcBandingFlags()
  376. {
  377.   struct TBandInfoStruct {
  378.     BOOL fGraphicsFlag;
  379.     BOOL fTextFlag;
  380.     RECT GraphicsRect;
  381.   };
  382.  
  383.   TBandInfoStruct BandInfoRec;
  384.   WORD pFlags = 0;
  385.  
  386.   // Calculate text verses graphics banding
  387.   if (UseBandInfo) {
  388.     Escape(PrnDC, BANDINFO, sizeof(TBandInfoStruct), NULL,
  389.         (LPSTR) &BandInfoRec);
  390.     if (BandInfoRec.fGraphicsFlag)
  391.         pFlags = PF_GRAPHICS;
  392.     if (BandInfoRec.fTextFlag)
  393.         pFlags = pFlags | PF_TEXT;
  394.     Flags = (Flags & !PF_BOTH) | pFlags;
  395.   }
  396.   else {
  397.     // If a driver does not support BANDINFO the Microsoft
  398.     //  Recommended way of determining text only bands is if
  399.     //  the first band is the full page, all others are
  400.     //  graphics only.  Otherwise it handles both.
  401.     if ( (FirstBand) && (BandRect.left == 0)
  402.        && (BandRect.top == 0)
  403.        && (BandRect.right == PageSize.x)
  404.        && (BandRect.bottom == PageSize.y) )
  405.       Flags = PF_TEXT;
  406.     else
  407.       if ((Flags & PF_BOTH) == PF_TEXT)
  408.         // All other bands are graphics only
  409.         Flags = ((Flags & !PF_BOTH) | PF_GRAPHICS);
  410.       else
  411.         Flags = Flags | PF_BOTH;
  412.   }
  413.   FirstBand = FALSE;
  414. }
  415.  
  416. BOOL TPrinter::Print(PTWindowsObject ParentWin, PTPrintout Printout)
  417. {
  418.   typedef BOOL (FAR PASCAL *PTAbortProc)( HDC Prn, short Code );
  419.  
  420.  
  421.   BOOL Banding;
  422.   PTAbortProc AbortProcInst;
  423.   WORD PageNumber;
  424.  
  425.   BOOL result = FALSE; // Assume error occurred
  426.  
  427.   Error = 0;
  428.  
  429.   if ( Printout == NULL )
  430.         return result;
  431.   if ( ParentWin == NULL )
  432.         return result;
  433.   if ( Status != PS_OK )
  434.   {
  435.         Error = SP_ERROR;
  436.         ReportError(Printout);
  437.         return result;
  438.   }
  439.  
  440.   PrnDC = GetDC();
  441.   if ( PrnDC == 0 )
  442.         return result;
  443.  
  444.   PTWindowsObject Dlg = GetApplicationObject()->MakeWindow(
  445.                 new TPrinterAbortDlg(ParentWin, "AbortDialog", Printout->Title, Device, Port) );
  446.  
  447.   if ( Dlg == NULL )
  448.   {
  449.     DeleteDC(PrnDC);
  450.     return result;
  451.   }
  452.  
  453.   EnableWindow(ParentWin->HWindow, FALSE);
  454.  
  455.   AbortProcInst = (PTAbortProc) MakeProcInstance( (FARPROC) AbortProc,
  456.         GetApplicationObject()->hInstance);
  457.   Escape(PrnDC, SETABORTPROC, 0, LPSTR(AbortProcInst), NULL);
  458.  
  459.   // Get the page size
  460.   PageSize.x = GetDeviceCaps(PrnDC, HORZRES);
  461.   PageSize.y = GetDeviceCaps(PrnDC, VERTRES);
  462.  
  463.   // Only band if the user requests banding and the printer
  464.   //  supports banding
  465.   Banding = ( Printout->Banding ) &&
  466.         (GetDeviceCaps(PrnDC, RASTERCAPS) & RC_BANDING);
  467.   if ( !Banding )
  468.   {
  469.     // Set the banding rectangle to full page
  470.     BandRect.left = 0;
  471.     BandRect.top = 0;
  472.     BandRect.right = PageSize.x;
  473.     BandRect.bottom = PageSize.y;
  474.   }
  475.   else
  476.   {
  477.     // Only use BANDINFO if supported (note: using Flags as a temporary)
  478.     Flags = BANDINFO;
  479.     // Escape(QUERYESCSUPPORT) returns nonzero for implemented
  480.     // escape function, and zero otherwise.
  481.     UseBandInfo =
  482.       Escape(PrnDC, QUERYESCSUPPORT, sizeof(Flags), (LPSTR) &Flags, NULL);
  483.   }
  484.  
  485.   Flags = PF_BOTH;
  486.  
  487.   Error = Escape(PrnDC, STARTDOC, strlen(Printout->Title),
  488.     Printout->Title, NULL);
  489.   PageNumber = 1;
  490.   if ( Error > 0 )
  491.   {
  492.     do
  493.     {
  494.       if ( Banding )
  495.       {
  496.         FirstBand = TRUE;
  497.         Error = Escape(PrnDC, NEXTBAND, 0, NULL, (LPSTR) &BandRect);
  498.       }
  499.       do
  500.       {
  501.         // Call the abort proc between bands or pages
  502.         (*AbortProcInst)(PrnDC, 0);
  503.  
  504.         if ( Banding )
  505.         {
  506.           CalcBandingFlags();
  507.           if ( (Printout->ForceAllBands) &&
  508.              ( ( Flags & PF_BOTH ) == PF_TEXT ) )
  509.                 SetPixel(PrnDC, 0, 0, 0);
  510.         }
  511.  
  512.         if ( Error > 0 )
  513.         {
  514.           Printout->PrintPage(PrnDC, PageNumber, PageSize, &BandRect, Flags);
  515.           if ( Banding )
  516.             Error = Escape(PrnDC, NEXTBAND, 0, NULL, (LPSTR) &BandRect);
  517.         }
  518.       } while ( (Error > 0) && (Banding) && (!IsRectEmpty(&BandRect)) );
  519.  
  520.       // NewFrame should only be called if not banding
  521.       if ( (Error > 0) && (!Banding) )
  522.         Error = Escape(PrnDC, NEWFRAME, 0, NULL, NULL);
  523.  
  524.       PageNumber++;
  525.     } while ( (Error > 0) && (Printout->IsNextPage()) );
  526.  
  527.     // Tell GDI the document is finished
  528.     if ( Error > 0 ) {
  529.       if ( Banding && UserAbort )
  530.         Escape(PrnDC, ABORTDOC, 0, NULL, NULL);
  531.       else
  532.         Escape(PrnDC, ENDDOC, 0, NULL, NULL);
  533.     }
  534.   }
  535.  
  536.   // Free allocated resources
  537.   FreeProcInstance((FARPROC) AbortProcInst);
  538.   EnableWindow(ParentWin->HWindow, TRUE);
  539.   delete Dlg;
  540.   DeleteDC(PrnDC);
  541.  
  542.   if ( Error & SP_NOTREPORTED )
  543.     ReportError(Printout);
  544.  
  545.   result = (Error > 0) && (!UserAbort);
  546.  
  547.   UserAbort = FALSE;
  548.  
  549.   return result;
  550. }
  551.  
  552. void TPrinter::Setup(PTWindowsObject Parent)
  553. {
  554.   GetApplicationObject()->ExecDialog(
  555.                 new TPrinterSetupDlg(Parent, "PrinterSetup", this));
  556. }
  557.  
  558. void TPrinter::ReportError(PTPrintout Printout)
  559. {
  560.   char ErrorMsg[80];
  561.   char ErrorCaption[80];
  562.   char ErrorTemplate[40];
  563.   char ErrorStr[40];
  564.   WORD ErrorId;
  565.   Pchar Title;
  566.  
  567.   switch (Error) {
  568.     case SP_APPABORT:
  569.       ErrorId = SR_PRNCANCEL;
  570.       break;
  571.     case SP_ERROR:
  572.       ErrorId = SR_GENERROR;
  573.       break;
  574.     case SP_OUTOFDISK:
  575.       ErrorId = SR_OUTOFDISK;
  576.       break;
  577.     case SP_OUTOFMEMORY:
  578.       ErrorId = SR_OUTOFMEMORY;
  579.       break;
  580.     case SP_USERABORT:
  581.       ErrorId = SR_PRNMGRABORT;
  582.       break;
  583.     default:
  584.       return;
  585.   }
  586.  
  587.         HINSTANCE hInstance = GetApplicationObject()->hInstance;
  588.  
  589.   LoadString(hInstance, SR_ERRORTEMPLATE, ErrorTemplate,
  590.     sizeof(ErrorTemplate));
  591.   LoadString(hInstance, ErrorId, ErrorStr, sizeof(ErrorStr));
  592.   Title = Printout->Title;
  593.   LPSTR C[1];
  594.   C[0] = Title;
  595.   wvsprintf(ErrorMsg, ErrorTemplate, (LPSTR) C);
  596.   LoadString(hInstance, SR_ERRORCAPTION, ErrorCaption,
  597.     sizeof(ErrorCaption));
  598.   MessageBox(0, ErrorMsg, ErrorCaption, MB_OK | MB_ICONSTOP);
  599. }
  600.  
  601.  
  602. // TPrinterSetupDlg
  603.  
  604. // TPrinterSetupDlg assumes the template passed has a ComboBox with
  605. // the control ID of 100 and a "Setup" button with id 101
  606.  
  607. const pdStrWidth = 80;
  608.  
  609. _CLASSDEF(TTransferBuffer)
  610. struct TTransferBuffer {
  611.     PTMyComboBoxData ComboBoxData;
  612.  
  613.     TTransferBuffer()
  614.     {
  615.         ComboBoxData = new TMyComboBoxData;
  616.     }
  617.     ~TTransferBuffer()
  618.     {
  619.         delete ComboBoxData;
  620.     }
  621.     int insert( Pchar ComboBoxString )
  622.     {
  623.         return ComboBoxData->Strings->insert( ComboBoxString );
  624.     }
  625.     // indexOf returns the index of the entry in ComboBoxData
  626.     // that has a an identical copy of string; note that this
  627.     // is different from TNSCollection::indexOf which looks
  628.     // for the actual pointer, not a copy.
  629.     static Boolean isCopy( Pvoid s1, Pvoid s2 )
  630.     {
  631.         return (strcmp( (Pchar) s1, (Pchar) s2 ) ) ?
  632.                 False :
  633.                 True;
  634.     }
  635.     int indexOf( Pchar string )
  636.     {
  637.         PTNSCollection Collection = ComboBoxData->Strings;
  638.         Pchar string2 = (Pchar) Collection->firstThat( isCopy, string );
  639.         return Collection->indexOf( string2 );
  640.     }
  641. };
  642.  
  643. struct TDeviceRec
  644. {
  645.     Pchar Driver, Device, Port;
  646. };
  647. typedef TDeviceRec *PTDeviceRec;
  648.  
  649. _CLASSDEF(TDeviceCollection)
  650. class TDeviceCollection : public TNSCollection
  651. {
  652. public:
  653.         TDeviceCollection( ccIndex aLimit, ccIndex aDelta )
  654.                 : TNSCollection( aLimit, aDelta ) {}
  655.         virtual void freeItem( Pvoid item );
  656. };
  657.  
  658. void TDeviceCollection::freeItem( Pvoid item )
  659. {
  660.   PTDeviceRec p = (PTDeviceRec) item;
  661.  
  662.   delete p->Driver;
  663.   delete p->Device;
  664.   delete p->Port;
  665.   delete p;
  666. }
  667.  
  668. static void FormDriverStr( Pchar DriverStr, int MaxLen, Pchar Device,
  669.         Pchar Port)
  670. {
  671.     strncpy(DriverStr, Device, MaxLen);
  672.     LoadString(GetApplicationObject()->hInstance, SR_ON,
  673.         (DriverStr + strlen(DriverStr)), MaxLen - strlen(DriverStr) - 1);
  674.     strncat(DriverStr, Port, MaxLen - strlen(DriverStr));
  675. }
  676.  
  677. TPrinterSetupDlg::TPrinterSetupDlg( PTWindowsObject AParent,
  678.   LPSTR TemplateName, PTPrinter APrinter)
  679.   : TDialog(AParent, TemplateName)
  680. {
  681.   Pchar Devices;                            // List of devices from the
  682.                                             //  WIN.INI
  683.   Pchar Device;                             // Current device
  684.   int DevicesSize;                          // Amount of bytes allocated
  685.                                             //  to store 'devices'
  686.   Pchar Driver;                             // Name of the driver for the
  687.                                             //  device
  688.   Pchar Port;                               // Name of the port for the
  689.                                             //  device
  690.   char DriverLine[pdStrWidth];              // Device line from WIN.INI
  691.   Pchar LineCur;                            // FetchStr pointer into
  692.                                             // DriverLine
  693.   char DriverStr[pdStrWidth];               // Text being built for display
  694.   Pchar StrCur;                             // Temp pointer used for copying
  695.                                             //  Port into the line
  696.   int StrCurSize;                           // Room left in DriverStr to
  697.                                             //  copy Port
  698.   PTDeviceRec DevRec;                       // Record pointer built to
  699.                                             //  store in DeviceCollection
  700.  
  701.   new TMyComboBox(this, ID_COMBO, pdStrWidth);
  702.  
  703.   PTTransferBuffer TransBuff = new TTransferBuffer;
  704.   TransferBuffer = TransBuff;
  705.  
  706.   Printer = APrinter;
  707.   DeviceCollection = new TDeviceCollection(5, 5);
  708.  
  709.   DevicesSize = DEFAULT_SAFETY_POOL_SIZE / 2;
  710.   Devices = new char[DevicesSize];
  711.  
  712.   // Save initial values of printer for Cancel
  713.   OldDevice = newstrdup(Printer->Device);
  714.   OldDriver = newstrdup(Printer->Driver);
  715.   OldPort = newstrdup(Printer->Port);
  716.  
  717.   // Get a list of devices from WIN.INI.  Stored in the form of
  718.   //  <device 1>\0<device 2>\0...<driver n>\0\0
  719.   GetProfileString("devices", NULL, "", Devices, DevicesSize);
  720.  
  721.   Device = Devices;
  722.   while ( *Device != '\0' )
  723.   {
  724.       GetProfileString("devices", Device, "", DriverLine,
  725.         sizeof(DriverLine) - 1);
  726.  
  727.       FormDriverStr(DriverStr, sizeof(DriverStr) - 1,Device, "");
  728.  
  729.       // Get driver portion of DeviceLine
  730.       LineCur = DriverLine;
  731.       Driver = FetchStr(LineCur);
  732.  
  733.       // Copy the port information from the line
  734.       /*   This code is complicated because the device line is of
  735.           the form:
  736.            <device name> = <driver name> , <port> { , <port> }
  737.           where port (in {}) can be repeated. */
  738.  
  739.       StrCur = ( DriverStr + strlen(DriverStr) );
  740.       StrCurSize = sizeof(DriverStr) - strlen(DriverStr) - 1;
  741.       Port = FetchStr(LineCur);
  742.       while ( *Port != '\0' )
  743.       {
  744.         strncpy(StrCur, Port, StrCurSize);
  745.         Pchar tempStr = newstrdup(DriverStr);
  746.         TransBuff->insert(tempStr);
  747.         DevRec = new TDeviceRec;
  748.         DevRec->Device = newstrdup(Device);
  749.         DevRec->Driver = newstrdup(Driver);
  750.         DevRec->Port = newstrdup(Port);
  751.         DeviceCollection->atInsert(TransBuff->indexOf(tempStr), DevRec);
  752.         Port = FetchStr(LineCur);
  753.       }
  754.       Device += (strlen(Device) + 1);
  755.     }
  756.     delete [] Devices;
  757.  
  758.     // Set the current selection to Printer's current device
  759.     TransBuff->ComboBoxData->Selection = new char[pdStrWidth];
  760.     if ( *(Printer->Device) != '\0' )
  761.         FormDriverStr(TransBuff->ComboBoxData->Selection, pdStrWidth, Printer->Device, Printer->Port);
  762.     else
  763.         TransBuff->ComboBoxData->Selection = NULL;
  764. }
  765.  
  766. TPrinterSetupDlg::~TPrinterSetupDlg( void )
  767. {
  768.   delete OldDevice;
  769.   delete OldDriver;
  770.   delete OldPort;
  771.   delete DeviceCollection;
  772.   delete (PTTransferBuffer) TransferBuffer;
  773.   TransferBuffer = NULL;
  774. }
  775.  
  776. void TPrinterSetupDlg::TransferData(WORD TransferFlag)
  777. {
  778.   TDialog::TransferData(TransferFlag);
  779.   if ( TransferFlag == TF_GETDATA )
  780.   {
  781.     PTTransferBuffer TransBuff = (PTTransferBuffer) TransferBuffer;
  782.     if ( ( TransBuff->ComboBoxData->Selection ) &&
  783.          ( *(TransBuff->ComboBoxData->Selection) != '\0' ) )
  784.     {
  785.         // Use the current selection to set Printer
  786.         PTDeviceRec DevRec = (PTDeviceRec)(DeviceCollection->
  787.                 at(TransBuff->indexOf(
  788.                         TransBuff->ComboBoxData->Selection) ) );
  789.         // Set the printer to the new device
  790.         Printer->SetDevice(DevRec->Device,
  791.                 DevRec->Driver,
  792.                 DevRec->Port);
  793.     }
  794.   }
  795. }
  796.  
  797. void TPrinterSetupDlg::IDSetup(RTMessage)
  798. {
  799.   TransferData(TF_GETDATA);
  800.   Printer->Configure(this);
  801. }
  802.  
  803. void TPrinterSetupDlg::Cancel(RTMessage Msg)
  804. {
  805.   TDialog::Cancel(Msg);
  806.   // Restore old settings, just in case the user pressed the Setup button
  807.   if ( OldDriver == NULL )
  808.         Printer->ClearDevice();
  809.   else
  810.         Printer->SetDevice(OldDevice, OldDriver, OldPort);
  811. }
  812.