home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Frameworks / Hsoi's App Shell 1.0a4 / Hsoi's App Shell Source / HASPrinting.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-28  |  43.8 KB  |  1,364 lines  |  [TEXT/CWIE]

  1. /*  
  2.     HASPrinting.c from Hsoi's App Shell © 1995-1997 John C. Daub.  All rights reserved.
  3.  
  4.     This file contains the various routines to deal with printing text (and other
  5.     stuff like embedded objects).  There are a few routines called elsewhere in
  6.     the code to deal with printing (like PrOpen, etc), but otherwise, we're
  7.     here!
  8.     
  9.     This code isn't final, and is going through a lot of changes to find the optimal
  10.     way to print.  right now, it's a combination of code based upon my own writing,
  11.     Tom Bender's Tex-Edit+ 1.6.3 printing routines, and Inside Macintosh: Imaging with
  12.     QuickDraw's sample code pp. 9-20 to 9-22.
  13.     
  14.     How it's set will print and work for you, but just informing you that it's in
  15.     a state of flux.
  16. */
  17.  
  18. #pragma mark ••• #includes •••
  19.  
  20. #ifndef _WASTE_
  21. #include "WASTE.h"
  22. #endif
  23. #include "HASGlobals.h"
  24. #ifndef __HSOIS_APP_SHELL__
  25. #include "HASMain.h"
  26. #endif
  27. #include "HASMiscEvents.h"
  28. #include "HASPrinting.h"
  29. #include "HASUtilPrint.h"
  30. #include "HASUtilities.h"
  31. #include "HASUtilPStrings.h"
  32. #include "WETabs.h"
  33.  
  34. #ifndef __PROCESSES__
  35. #include <Processes.h>
  36. #endif
  37.  
  38. #ifndef _STDIO
  39. #include <stdio.h>
  40. #endif
  41.  
  42. #include "WASTE_Objects.h"
  43.  
  44. #pragma mark -
  45. #pragma mark ••• Globals •••
  46.  
  47. // local globals
  48.  
  49. static DialogRef       myPrintStatusDialog;
  50. static Str255            sDocumentName;            // name of the document being printed (used in the idleProc)
  51. static Str255            sMsgStr1;                // message displayed in the idle proc dialog
  52. static Str255            sMsgStr2;                // ditto
  53. static short            sNumOfPages;            // number of pages required to print the document
  54. static short            sNumOfCopies;            // number of copies to print
  55. static short            sFirstPage;                // first page number
  56. static short            sLastPage;                // last page number
  57. PrIdleUPP                printIdleUPP = nil;            // our print idle procedure
  58.  
  59.  
  60. #pragma mark -
  61. #pragma mark ••• Initialization •••
  62.  
  63. /*
  64.     when this function is called, there was an error in calling PrOpen during our
  65.     HsoiInitPrintingStuff routine.  Here's what I'm trying to combat:
  66.  
  67.     I remember once I hit upon a strange problem.  I've had window size based upon
  68.     the paper/page size from the gPrinterRecord structure.  well, what if there is
  69.     some bogus values in the gPrinterRecord?
  70.     
  71.     here's what happened to me.
  72.     
  73.     I was testing the app shell on a brand new PowerMac that I got into my office.
  74.     when i fired the app up, there was no window, or at least, no window that i
  75.     could see. there technically was a window there (due to how the rest of the
  76.     app was behaving) but i think the coordinates of the window (as obtained from/based on
  77.     the gPrinterRecord page/paper sizes) were so fubar that you didn't see any window.
  78.     
  79.     I found the problem for this:  the computer had never had a printer selected
  80.     in it; it was a brand new computer...the Chooser had never been visited.  once
  81.     i went to the Chooser and selected a printer, the app worked just fine.
  82.     
  83.     So, this is a problem that must be dealt with (albeit a rare problem), else
  84.     we risk confusing the user cause their windows will seem to not be there, or
  85.     at least be some strange (and probably unusable) size.
  86.     
  87.     Of course, what would be best would be that when we obtain the paper/page size
  88.     from the gPrinterRecord, that we could do some sort of integrity check on those
  89.     values...but how can we?  I can't think of any sort of way considering the
  90.     vast number of printer drivers out there, and the lack of standardization with them.
  91.     
  92.     So, here's what we do...we attempt to find out if a printer has ever been selected.
  93.     
  94.     I went screwing around in the System file one day and discovered this.  In 'STR '
  95.     -8192, it contains the name of the current printer.  That could be enough to look
  96.     for, but it's not.  I did an installation of a clean pure System 7.1 and found something
  97.     already in there (as a default value I assume...fyi, it was the Imagewriter).
  98.     
  99.     But, i did find the lack of something in the clean System file:  there's a resource,
  100.     'alis' (for alias I'm sure), and lo and behold, it had only one resource in it,
  101.     ID number -8192.  I checked my normal System file and in this, I found an alias
  102.     record pointing to my current selected printer!
  103.     
  104.     after doing some toying around, I found this was the key to solving my problem.
  105.     just check for an 'alis' resource in the System file, and (just to be safe)
  106.     check for an ID of -8192.  We might even go a little further and get the 'STR '
  107.     -8192 and compare these 2 for similarity, but I think that's unneeded.
  108.     
  109.     So that is what we do.
  110.     
  111.     Now, I must put in this note of caution and note of disclaimer.
  112.     
  113.     I scoured NIM:Imaging with QuickDraw's chapter on printing.  almost no where in
  114.     there did i see anything talking about what to do in this sort of situation.  I
  115.     did see one thing on page 9-75 saying the most common error encountered (when
  116.     calling PrError()) is PAPNoPrinter (-4101): the printer is not found, is closed,
  117.     or is not selected.  when this error is encountered, you should display an alert
  118.     box asking the user to select a printer from the Chooser.
  119.     
  120.     Now, I did a Find search on this error code throughout all the Universal Header
  121.     files.  no where did i find this error code.  Plus, in NIM:QD, it says this is
  122.     a LaserWriter error code (so who knows what other printer drivers, especially
  123.     non-Apple drivers) would return in this sort of instance.  I have Deskwriter
  124.     drivers, and when I was testing all this, my call to PrOpen then PrError returned
  125.     an error of -43, fnfErr (file not found).
  126.     
  127.     So, I'm sorta taking an "undocumented" approach to discovering this information.
  128.     Sure, I'll look for PAPNoPrinter errors, but I can't count on them showing up.  So,
  129.     my searching for the 'alis' resource in the System file might be considered a
  130.     "no no" by Apple, and they might not like that I am doing this sort of technique.
  131.     
  132.     Furthermore, since this seems to be undocumented, who knows if it will continue
  133.     to be Apple's way of doing things (all things considered, I"m sure this won't
  134.     change in future system software releases, but you never know).
  135.     
  136.     So, this technique might break in the future, Apple might ask me (and/or you) to
  137.     remove this kind of code, who knows....basically, USE THIS TECHNIQUE AT YOUR
  138.     OWN RISK!!!  If Apple asks you not to do this, ask them then just how TO do
  139.     it properly (cause apparently PAPNoPrinter isn't always the error code in this
  140.     instance).  And I ask that to Apple also....if you don't want me to do this sort
  141.     of thing, then just how am I TO properly do it?
  142.     
  143.     So follow along...hopefully this will work....
  144.     
  145.     the jist is this:  first of all, HsoiCheckPrinterDriver (and it's associated
  146.     routines) are ONLY called during the HsoiInitPrintingStuff() routine, and then
  147.     only if we receive an error from PrError immediately after calling PrOpen (to
  148.     validate/printdefault the gPrinterRecord).  If there is an error, we'll try
  149.     to see what that error might be and deal with it properly.  We'll give the
  150.     user an alert box letting them know what's up, and a few options:
  151.     
  152.     to quit the program immediately and launch the Chooser (a nice "touch" allowing
  153.     them to immediately select a printer...note, we'll have no way of really knowing
  154.     if they select a printer or not...we just have to hope the user will).
  155.     
  156.     to just quit and forget about it.
  157.     
  158.     those are the options....i could impliment something that we just skip it
  159.     and let the user play, but don't give the ability to print (and make windows
  160.     some arbitrary size), but i think that's too much of a pain.
  161.     
  162.     if you want to see this in action, you could step through with a debugger and
  163.     just change variable values to "appropriate" values to force the alert to come
  164.     up, or to REALLY try this, do this: (and you do this at your own risk...it will
  165.     involve modifying your System file, so if you mess something up, please don't
  166.     blame me)
  167.     
  168.     make a copy of your System file and open it up in ResEdit.  delete the 'alis'
  169.     resource.  remove the original System file from the system folder and put your
  170.     copy (renamed to "System") in your System folder.  restart your computer.
  171.     launch the shell demo and watch what happens.  if you want to see how bad things
  172.     could be if this alert didn't come up, comment out the call to HsoiCheckPrinterDriver
  173.     in HsoiInitPrintingStuff, recompile and try the above again...you'll see how small
  174.     and mooshed your window gets.
  175.  
  176. */
  177.  
  178. void    HsoiCheckPrinterDriver( OSErr prErr )
  179. {
  180.     Handle        h;
  181.     
  182.     // first, let's check what that error is.  if the error is greater than
  183.     // 0, there could be some problems, but  they seem to only deal with the PrGeneral
  184.     // procedure (or it was the iPrAbort error, which shouldn't be here in this
  185.     // situation).  there could also be a simple memFullErr (-108), but we're gonna
  186.     // be a bit lazy and not bother with that right now.
  187.     
  188.     // first, just in case we got a noErr passed here, we'll just return
  189.     
  190.     if ( prErr == noErr )
  191.         return;
  192.     
  193.     // if the error is the PAPNoPrinter error, we know we'll have to do the
  194.     // alert box
  195.     
  196.     else if ( prErr == -4101 ) // -4101 is PAPNoPrinter
  197.         HsoiDoChooserAlert( prErr );
  198.     else
  199.     {
  200.         // ok..not that error, so, to simplify things (i.e. be lazy *grin*), we'll
  201.         // just take all other errors as a problem....
  202.         
  203.         // make the System file the current resource file
  204.         
  205.         UseResFile( 0 );
  206.         
  207.         // try to get the 'alis' resource.  we'll use Get1Resource instead of GetResource
  208.         // cause we only want to search the current resource file (the system file) and
  209.         // not every open resource fork (like our application's)
  210.         
  211.         h = Get1Resource( 'alis', -8192 );
  212.         
  213.         // just to be good, we'll make sure we switch the curr res file to our
  214.         // application's before we continue
  215.         
  216.         UseResFile( gAppResourceFork );
  217.         
  218.         // if h is nil, we'll assume it doesn't exist (tho h being nil might be because
  219.         // we don't have enough memory...we should call ResError to check, but again, we're
  220.         // being simple).
  221.                 
  222.         if ( h == nil )
  223.         {        
  224.             HsoiDoChooserAlert( prErr );
  225.         }
  226.         else
  227.             // well, we'll dump the resource, but there's some other problem....
  228.             
  229.             HsoiForgetResource( &h );
  230.     }
  231.  
  232.     // and that should be all she wrote...we should (technically) never reach
  233.     // this far...
  234.     
  235.     return;
  236.  
  237. }
  238.  
  239. // this procedure is called by the above function.  if we ran into some error,
  240. // we put up a dialog saying there was a problem and to select a printer in the
  241. // Chooser and we even give the user the option of us auto-launching the
  242. // Chooser for them.  no matter what happens tho, the app will quit.
  243.  
  244. // originally, i wrote this function using LaunchApplication.  however, since the
  245. // Chooser is technically a desk accessory (and how might this change in Copland?),
  246. // we cannot use LaunchApplication, we must use LaunchDeskAccessory (which kinda sucks
  247. // cause it's not as robust as LaunchApplication).  However, to demonstrate how to use
  248. // LaunchApplication (and in case the Chooser changes in kind/type in the future), i'll
  249. // leave in all the relevant code to launch the Chooser if it was an application. it'll
  250. // be commented out by C style comments instead of C++ comments (i.e. the /* */ instead
  251. // of the //)
  252.  
  253. void    HsoiDoChooserAlert( OSErr prErr )
  254. {
  255.     short                    itemHit;
  256. /*    LaunchParamBlockRec        launchParams;    */
  257.     FSSpec                    fileSpec;
  258.     Str255                    errStr;
  259.     OSErr                    err;
  260.     FInfo                    info;
  261.     Boolean                    isFolder, wasAlias;
  262.     
  263.     
  264.     // first, we must display our alert to determine what to do
  265.     
  266.     // make the error into a string for display in the alert
  267.     
  268.     NumToString( (long)prErr, errStr );
  269.     
  270.     ParamText( errStr, NIL_STRING, NIL_STRING, NIL_STRING );
  271.     
  272.     // do the alert
  273.     
  274.     itemHit = StopAlert( rChooserAlert, nil );
  275.     
  276.     // now, based upon itemHit, what to do
  277.     
  278.     // if itemHit == -1, the alert wasn't able to be displayed, so we'll just bail out.
  279.     
  280.     if ( itemHit == -1 )
  281.     {
  282.         SysBeep(1);
  283.         ExitToShell();
  284.     }
  285.     
  286.     // if they choose to quit
  287.     
  288.     if ( itemHit == cancel ) // "Quit" is item 2, same as cancel
  289.         ExitToShell(); // bail out
  290.     
  291.     // else itemHit == ok (the Quit and Auto-Launch Chooser stuff)
  292.  
  293.     // since we know already that we're running under system 7 (at least), we know
  294.     // the Process Manager exists, so we can use LaunchApplication to launch the
  295.     // Chooser.
  296.     
  297.     // we need to initialize the launchParams variable before we use it.  one thing
  298.     // we need is the FSSpec to the Chooser, so let's get it
  299.     
  300.     // first, let's find the Apple Menu items folder on the current system disk.
  301.     // there might be a chance the Chooser isn't in the Apple Menu Items folder, but
  302.     // i'd think that'd be about a 1 in a million chance (and hell, maybe we should
  303.     // check for such a case, but eh....we're being simple and just assuming a lot)
  304.     
  305.     err = FindFolder(kOnSystemDisk, kAppleMenuFolderType, kDontCreateFolder,
  306.                                     &fileSpec.vRefNum, &fileSpec.parID);
  307.                                     
  308.     // put the name of the Chooser in the file spec
  309.     
  310.     HsoipStringCopy( "\pChooser", fileSpec.name );
  311.     
  312.     // now we'll make sure the Chooser exists
  313.     
  314.     err = FSpGetFInfo( &fileSpec, &info );
  315.     
  316.     // deal with a few error situations
  317.     
  318.     if ( err != noErr )
  319.         ExitToShell(); // didn't find it?  skip it
  320.         
  321.     // now, check to make sure this is is the real Chooser and NOT an alias. if it is
  322.     // an alias to the Chooser, we'll resolve the alias before we proceed
  323.  
  324.     err = ResolveAliasFile( &fileSpec, true, &isFolder, &wasAlias );
  325.  
  326. /*    
  327.     // and now we can initialize the launchParams struct
  328.     
  329.     launchParams.launchBlockID = extendedBlock;
  330.     launchParams.launchEPBLength = extendedBlockLen;
  331.     launchParams.launchFileFlags = 0;
  332.     launchParams.launchControlFlags = launchNoFileFlags;
  333.     launchParams.launchAppSpec = &fileSpec;
  334.     launchParams.launchAppParameters = nil;
  335.     
  336.     // and finally, launch the Chooser!  (note, because of our launchControlFlags, our
  337.     // app will automatically quit).  but just in case some sort of error occurs, we'll
  338.     // call ExitToShell()
  339.     
  340.     if ( LaunchApplication( &launchParams ) )
  341.         ExitToShell();
  342. */
  343.  
  344.     // now we'll launch the Chooser.  the way LaunchDeskAccessory works can greatly vary
  345.     // and I recommend you read NIM:Processes chapter 2 (esp. 2-30) to get more information.
  346.     // I'll just explain what I'm doing.
  347.     
  348.     // LaunchDA searches the resource fork of the file specified in the fileSpec for
  349.     // a 'DRVR' resource with the given name (a StringPtr passed as the second argument).
  350.     // if it's found, it's launched.  if nil is passed as the given name argument (as I
  351.     // am doing), then the first 'DRVR' resource found is the one that is launched.  In
  352.     // this case here, there should only be one resource (one named: Chooser), but this
  353.     // sort of thing allows flexibility if in the given fileSpec there are more than one
  354.     // 'DRVR' resources.
  355.     
  356.     // so, launch it...
  357.     
  358.     err = LaunchDeskAccessory( &fileSpec, nil );
  359.     
  360.     // and then immediately can the app....if we could use LaunchApplication, this wouldn't
  361.     // be necessary cause in the launchControlFlags of the LaunchParamBlockRec, we have
  362.     // not specified to continue our app (by passing launchContinue), therefore it'd
  363.     // autoterminate.  but, this is how DA's work...and it'd be really nice if in the
  364.     // future, Apple could put some sort of similar functionality in LaunchDA.
  365.     
  366.     ExitToShell();
  367.     
  368.     return;
  369. }
  370.  
  371.  
  372. // let's get everything initialized...called at app startup time
  373.  
  374. OSErr    HsoiInitPrintingStuff( void )
  375. {
  376.     OSErr        err = noErr;
  377.     
  378.     // get a handle allocated for the print record
  379.     
  380.     gPrinterRecord = (THPrint)NewHandleClear( sizeof(TPrint) );
  381.  
  382.     if ( gPrinterRecord != nil )
  383.     {
  384.         // ok..we got a printer record!
  385.         
  386.         // it's gonna hang around for the life of the program, so let's move it high
  387.         // in the heap so to minimize memory fragmentation.  And let's lock it down
  388.         // too just to be safe.
  389.         
  390.         HLockHi( (Handle)gPrinterRecord );
  391.                 
  392.         // now that we got it, let's initialize it to some default values
  393.         
  394.         PrOpen();
  395.         
  396.         err = PrError();
  397.         
  398.         if ( err )
  399.         {
  400.             // we got an error, and this error could affect the entire operation
  401.             // of the application, therefore we ought to check a few more things...
  402.             
  403.             HsoiCheckPrinterDriver( err );
  404.             
  405.             // and just in case something goes wrong, we know there is a problem
  406.             // and possibly one worthy of quitting out anyways...but it's probably
  407.             // better to display some sort of error alert to catch odd situations
  408.             
  409.             ExitToShell();
  410.         }
  411.         
  412.         PrintDefault( gPrinterRecord );
  413.         PrClose(); // don't need to leave it open :)
  414.     }
  415.     else
  416.         err = memFullErr;
  417.  
  418.     return err;
  419. }
  420.  
  421.  
  422. #pragma mark -
  423. #pragma mark ••• Page Setup •••
  424.  
  425. // this handles the "File:Page Setup" menu item
  426.  
  427. void    HsoiDoPageSetup( void )
  428. {
  429.     WindowRef    window = FrontWindow();
  430.     
  431.     // if there's a sound playing, stop it
  432.     
  433.     if ( SoundIsPlaying() )
  434.         StopCurrentSound();
  435.         
  436.     // open up the printer driver
  437.     
  438.     PrOpen();
  439.  
  440.     // fix the cursor to the arrow
  441.     
  442.     InitCursor();
  443.     
  444.     // present the user with the Printer Style (Page Setup) dialog
  445.     
  446.     if ( PrError() == noErr )
  447.     {
  448.         // make sure the front window is deactivated
  449.         
  450.         HsoiDoActivate( 0, window );
  451.         PrStlDialog( gPrinterRecord );
  452.     }
  453.     
  454.     // we're done...close up the printer driver
  455.     
  456.     PrClose();
  457.  
  458.     // and we need to redraw our windows...
  459.  
  460.     // what we really ought to do in here is check to see if the user selected some
  461.     // new paper size...if so, we should redraw the window in the new size to reflect
  462.     // the changes.  I think the easiest way to deal with this would simply be to
  463.     // make a copy of the gPrinterRecord before the call to PrStlDialog so we can
  464.     // preserve the "original" settings.  then, just call something like:
  465.     
  466.     // if ( !EqualRect( &(*gPrinterRecord)->prInfo.rPage, &(*printRecCopy)->prInfo.rPage ) )
  467.     //        HsoiDoReadjustWindowSize();
  468.     
  469.     // or something....however, before I impliment something like that, we have to
  470.     // take into account all the various printer drivers and how they manage things.
  471.     // with my HP Deskwriter (driver version 6.0), if i change the paper size, the
  472.     // page orrientation, or reduce/enlarge the page, it affects the rPage size.
  473.     // but, to keep WYSIWYG, just how will that affect the screen window?  and
  474.     // how to take variations in all the different printer drivers into account?
  475.     
  476.     // this is something that needs to be done, but hopefully someone will standardize
  477.     // the way printer drivers work to make our lives easier.
  478.     
  479.     HsoiDoActivate( 1, window );
  480.     
  481.     return;
  482. }
  483.  
  484. #pragma mark -
  485. #pragma mark ••• Printing Utils •••
  486.  
  487. Boolean    HsoiOpenPrintManager( WindowRef window )
  488. {
  489.     OSErr        err = noErr;
  490.     Boolean        retval = false;
  491.     
  492.     // make sure we have a cursor
  493.     
  494.     InitCursor();
  495.     
  496.     // only proceed if we have a properly initialized print record
  497.     
  498.     if ( gPrinterRecord != nil )
  499.     {
  500.         // open the printer and check for errors
  501.         
  502.         PrOpen();
  503.         err = PrError();
  504.         
  505.         // if no errors, bring up the Print dialog
  506.         
  507.         if ( err == noErr )
  508.         {
  509.  
  510.             // stop any currently playing sounds
  511.             
  512.             if ( SoundIsPlaying() )
  513.                 StopCurrentSound();
  514.                 
  515.             // determine the number of pages required to print the document
  516.             
  517.             sNumOfPages = HsoiDetermineNumOfPages( HsoiGetWindowWE( window ));
  518.                 
  519.             // deactivate the front window
  520.             
  521.             HsoiDoActivate( 0, window );
  522.             
  523.             // throw up the print dialog
  524.             
  525.             if ( PrJobDialog( gPrinterRecord ) )
  526.             {
  527.             
  528.                 // let's install our print idle proc.  you have to insert the
  529.                 // idle proc before calling PrOpenDoc() (but after such calls as
  530.                 // PrJobDialog(), which can reset the value of the pIdleProc field
  531.                 // to nil
  532.      
  533.                 if ( printIdleUPP == nil )
  534.                     printIdleUPP = NewPrIdleProc( hsoiMyPrintIdleProc );
  535.                 (*gPrinterRecord)->prJob.pIdleProc = printIdleUPP;
  536.         
  537.                 // initialize the GrafPort before printing
  538.                 
  539.                 gThePrinterPort = PrOpenDoc( gPrinterRecord, nil, nil );
  540.                 
  541.                 // check for errors
  542.                 
  543.                 err = PrError();
  544.                 
  545.                 // if the printing GrafPort is active, return as such.
  546.                 
  547.                 if ( err == noErr )
  548.                     retval = true;
  549.                 else
  550.                     // else, no active one, so just close the printer port
  551.                     PrClose();
  552.             }
  553.             else // they selected cancel so just close
  554.             
  555.                 PrClose();
  556.         }
  557.         else // there was an error, handle it
  558.             goto exit;
  559.     }
  560.     
  561.     // if there was an error, display it as such
  562.  
  563. exit:    
  564.     if ( err != noErr )
  565.         HsoiDoError( rPrintingStrings, strPrintError, err, kErrStop );
  566.     
  567.     return retval;
  568. }
  569.  
  570. // this function figures out the dimensions of the actual printed text on
  571. // the page.  takes the margin settings and an "empty" printRect as arguments, and
  572. // the dimensions of the printRect will be set within it (it's a pointer...no need
  573. // to return it)
  574.  
  575. // incidentally, the figurePageNumbers Boolean is used as an programmer controled
  576. // check as to whether or not the page number adjustment should be made or not.
  577. // in the printing loop, this should be set to true (and then prefs setting can
  578. // determine if this adjustment should be made or not).  this should be set to
  579. // false for example when called by HsoiCalcIdealWindowSize() in which we don't
  580. // want to bother with this calculation (otherwise in HsoiCalcIdealWindowSize()
  581. // i'd have to reverse this calculation, if it even occured, and that's too much
  582. // trouble...this is much easier)
  583.  
  584. void    HsoiGetPrintRect( Rect *margins, LongRect *printRect, Boolean figurePageNumbers )
  585. {
  586.     LongRect        paperRect, maxPrintableRect;
  587.     
  588.     // get the size of the paper
  589.     
  590.     WERectToLongRect( &(*gPrinterRecord)->rPaper, &paperRect );
  591.     
  592.     // now get the printer's maximum print rectangle
  593.     
  594.     WERectToLongRect( &(*gPrinterRecord)->prInfo.rPage, &maxPrintableRect );
  595.     
  596.     // and inset the actual print rectangle by the size of the margins, if the
  597.     // margins are big enough.
  598.     // procedure:  set the printRect.xxx to paperRect.xxx with margins.xxx, and
  599.     // check to make sure it doesn't (over) extend things
  600.     
  601.     printRect->left = paperRect.left + margins->left;
  602.     if ( (printRect->left < maxPrintableRect.left) || (printRect->left > maxPrintableRect.right) )
  603.         printRect->left = maxPrintableRect.left;
  604.     
  605.     printRect->top = paperRect.top + margins->top;
  606.     if ( (printRect->top < maxPrintableRect.top) || (printRect->top > maxPrintableRect.bottom) )
  607.         printRect->top = maxPrintableRect.top;
  608.     
  609.     printRect->right = paperRect.right - margins->right;
  610.     if ( (printRect->right > maxPrintableRect.right) || (printRect->right < printRect->left) )
  611.         printRect->right = maxPrintableRect.right;
  612.     
  613.     printRect->bottom = paperRect.bottom - margins->bottom;
  614.     if ( (printRect->bottom > maxPrintableRect.bottom) || (printRect->bottom < printRect->top) )
  615.         printRect->bottom = maxPrintableRect.bottom;
  616.  
  617.     // leave some room for the page number at the bottom, if needed
  618.     
  619.     if ( figurePageNumbers && (gMyPrefs.printPageNumbers) &&
  620.                 ( (maxPrintableRect.bottom - printRect->bottom) < kSpaceForPageNumber) )
  621.         printRect->bottom = maxPrintableRect.bottom - kSpaceForPageNumber;
  622.         
  623.     return;
  624. }
  625.  
  626. WEReference    HsoiCloneTextHandle( LongRect *printRect, WEReference we )
  627. {
  628.     LongRect        emptyViewRect = { 0,0,0,0 };
  629.     long            selLength;
  630.     Handle            hText;
  631.     StScrpHandle    hStyl;
  632.     WESoupHandle    hSoup;
  633.     SignedByte        currentJust;
  634.     OSErr            err;
  635.     WEReference        scratchWE;
  636.     
  637.     // create a new WEReference using the printRect.  viewRect is closed cause
  638.     // GX doesn't recognize ClipRect()
  639.     
  640.     err = WENew( printRect, &emptyViewRect, weDoUseTempMem, &scratchWE );
  641.     
  642.     // use same tab/align settings as the window being printed
  643.     
  644.     if ( err == noErr )
  645.     {
  646.         currentJust = WEGetAlignment( we );
  647.         WESetAlignment( currentJust, scratchWE );
  648.         if ( WEIsTabHooks( we ) )
  649.             err = WEInstallTabHooks( scratchWE );
  650.     }
  651.     
  652.     // make a copy of the current selection's text/styles
  653.     
  654.     if ( err == noErr )
  655.     {
  656.         // allocate some memory for the handles to the text, style, and soup
  657.         
  658.         HsoiNewHandleTemp( 0, &hText );
  659.         HsoiNewHandleTemp( 0, (Handle *)&hStyl );
  660.         HsoiNewHandleTemp( 0, (Handle *)&hSoup );
  661.         
  662.         // get the range that we want to copy (in this case, the whole thing)
  663.         
  664.         selLength = WEGetTextLength( we );
  665.         
  666.         err = WECopyRange( 0, selLength, hText, hStyl, hSoup, we );
  667.     }
  668.     
  669.     // insert the text into our scratch area, re-open the viewRect
  670.     
  671.     if ( err == noErr )
  672.     {
  673.         HLock( hText );
  674.         err = WEInsert( *hText, selLength, hStyl, hSoup, scratchWE );
  675.         HUnlock( hText );
  676.         WESetViewRect( printRect, scratchWE );
  677.     }
  678.     
  679.     // and free up memory
  680.     
  681.     if ( hStyl != nil )
  682.         HsoiForgetHandle( (Handle *)&hStyl );
  683.     if ( hSoup != nil )
  684.         HsoiForgetHandle( (Handle *)&hSoup );
  685.     if ( hText != nil )
  686.         HsoiForgetHandle( (Handle *)&hText );
  687.     
  688.     if ( err == noErr )
  689.         return scratchWE;
  690.     else
  691.         return nil;
  692. }
  693.  
  694. // HsoiInchesToDots takes a string containing a number (like our prefs margin settings)
  695. // and converts it to pixels (dpi) based upon the printer's resolution.  since we're
  696. // dealing with longs (and not doubles) most of the time, upon a return, the casting
  697. // of the double to a long basically discards the decimal point.  all in all, a fraction
  698. // of a pixel shouldn't matter.
  699.  
  700. long    HsoiInchesToDots( Str255 inches, short direction )
  701. {    
  702. #pragma unused (direction)
  703.  
  704.     long            whole;
  705.     
  706.     double            value;
  707.     
  708.     if ( pos( "\p.", inches ) > 0 )
  709.     {
  710.         
  711.         sscanf( (char *)inches + 1, "%lf", &value );
  712.         
  713. //        if ( direction == kHortz )
  714. //            return (long)(value * (*gPrinterRecord)->prInfo.iHRes );
  715. //        else if ( direction == kVert )
  716. //            return (long)(value * (*gPrinterRecord)->prInfo.iVRes );
  717. //        else if ( direction == kForce72dpi )
  718. //            return (long)(value * kDotsPerInch );
  719. //        else  // a "kludgy" default just in case a strange value is passed for direction
  720.             return (long)(value * kDotsPerInch );
  721.  
  722.     }
  723.     else // no decimal portion
  724.     {
  725.         StringToNum( inches, &whole );
  726.         
  727. //        if ( direction == kHortz )
  728. //            return ( whole * (*gPrinterRecord)->prInfo.iHRes );
  729. //        else if ( direction == kVert )
  730. //            return ( whole * (*gPrinterRecord)->prInfo.iVRes );
  731. //        else if ( direction == kForce72dpi )
  732. //            return ( whole * kDotsPerInch );
  733. //        else
  734.             return ( whole * kDotsPerInch );
  735.     }
  736.     
  737. }
  738.         
  739.  
  740.  
  741.  
  742. void    HsoiPrintFooter( long *currentPage, WindowRef window )
  743. {
  744.     Str255        pageStr = "\p";
  745.     short        oldFont, oldSize, fontNum;
  746.     
  747.     // get the old port settings
  748.     
  749.     oldFont = GetWindowPort(window)->txFont;
  750.     oldSize = GetWindowPort(window)->txSize;
  751.     
  752.     // center on printer's maximum print area
  753.     
  754.     MoveTo( (*gPrinterRecord)->prInfo.rPage.right / 2,
  755.             (*gPrinterRecord)->prInfo.rPage.bottom - 3 );
  756.     
  757.     // print using the default font (set in the prefs)
  758.     
  759.     GetFNum( gMyPrefs.defFont, &fontNum );
  760.     TextFont( fontNum );
  761.     TextSize( kDefaultSize );
  762.     
  763.     // print the page number
  764.     
  765.     NumToString( *currentPage, pageStr );
  766.     DrawString( pageStr );
  767.     
  768.     // restore the port and increment the page counter
  769.     
  770.     TextFont( oldFont );
  771.     TextSize( oldSize );
  772.     
  773.     *currentPage += 1;
  774.     
  775.     return;    
  776. }
  777.  
  778.  
  779. OSErr    HsoiPrintUntilDone( Rect *margins, LongRect *printRect, WEReference we, WindowRef window )
  780. {
  781.     LongRect        paperRect, r, destRect;
  782.     Rect            shortRect;
  783.     long            totalTextLines, totalHeight, currentLine = 1, currentPage = 1,
  784.                     scrollAmount, viewHeight;
  785.     Boolean            abortedPrinting = false;
  786.     OSErr            err = noErr;
  787.     RgnHandle        printRgn;
  788.     
  789.     // get the number of lines in our text
  790.     
  791.     totalTextLines = WECountLines( we );
  792.     
  793.     // adjust the bottom of our text's destRect
  794.     
  795.     totalHeight = WEGetHeight( 0, totalTextLines, we );
  796.     WEGetDestRect( &r, we );
  797.     r.bottom = r.top + totalHeight;
  798.     WESetDestRect( &r, we );
  799.     
  800.     // now print page by page until aborted or out of text...one page per loop
  801.     
  802.     while ( (!abortedPrinting) && ( currentLine <= totalTextLines ) )
  803.     {
  804.         // prepare to print a page
  805.         
  806.         PrOpenPage( gThePrinterPort, nil );
  807.         scrollAmount = 0;
  808.         
  809.         // open the clipping rect so text will draw
  810.         
  811.         shortRect = (*gPrinterRecord)->prInfo.rPage;
  812.         ClipRect( &shortRect );
  813.         
  814.         // calculate the height of the viewRect
  815.         
  816.         WEGetViewRect( &r, we );
  817.         viewHeight = r.bottom - r.top + 1;
  818.         
  819.         // figure out how many lines per page
  820.         
  821.         while ( ((scrollAmount + WEGetHeight( currentLine - 1, currentLine, we )) <= viewHeight )
  822.                 && ( currentLine <= totalTextLines ) )
  823.         {
  824.             scrollAmount += WEGetHeight( currentLine - 1, currentLine, we );
  825.             currentLine++;
  826.         }
  827.         
  828.         // adjust the bottom of the printed page based on t he number of lines printed
  829.         
  830.         WEGetViewRect( &r, we );
  831.         WERectToLongRect( &(*gPrinterRecord)->rPaper, &paperRect );
  832.         if ( (paperRect.top + margins->top) < printRect->top )
  833.             r.bottom = printRect->top + scrollAmount;
  834.         else
  835.             r.bottom = paperRect.top + margins->top + scrollAmount;
  836.         WESetViewRect( &r, we );
  837.         
  838.         // insert the page number
  839.         
  840.         if ( gMyPrefs.printPageNumbers )
  841.             HsoiPrintFooter( ¤tPage, window );
  842.         
  843.         // and finally actually print the text!
  844.         
  845.         WEGetViewRect( &r, we );
  846.         WELongRectToRect( &r, &shortRect );
  847.         printRgn = NewRgn();
  848.         RectRgn( printRgn, &shortRect );
  849.         WEUpdate( printRgn, we );
  850.         if ( printRgn != nil )
  851.             DisposeRgn( printRgn );
  852.         
  853. /*    NOW!!  you must pay attention to this!!!
  854.  
  855.     Usually in a print routine, after calling WEUpdate (or TEUpdate or whatever
  856.     you do to actually print the thing), you'd do something like this:
  857.     
  858.         ClipRect( &zeroRect );  // where zeroRect == { 0, 0, 0, 0 }
  859.     
  860.         WEScroll( 0, -scrollAmount, we );
  861.     
  862.     now, what that does is scroll the text up a page so we can print the next
  863.     page.  You call ClipRect() with a zeroRect to prevent WEScroll from redrawing
  864.     the text as it's being scrolled.  This is a technique gleaned from TextEdit
  865.     (from what I understand...I've always used WASTE for my text stuff).
  866.     
  867.     BUT!!!  this is a problem!  Why?  well, it makes some assumptions about the
  868.     inner workings of WEScroll...and with the 1.2a4 version of the WASTE API,
  869.     WEScroll changed how it works...with versions of WASTE prior to 1.2a4, you
  870.     were able to get away (somewhat) with the ClipRect-WEScroll method, but no
  871.     longer can you.  Besides, from what I understand, calling WEScroll() in a
  872.     printing loop can cause problems with certain printer drivers (according to
  873.     Marco Piovanelli, this problem arises with the HP Deskwriter 6.0 driver, but
  874.     I use that driver at home, and I've not had a problem *shrug*  I'll take Marco's
  875.     word for it).
  876.     
  877.     Let me include here some snippets of an email message between Marco and me
  878.     regarding this subject (this is Marco "talking"):
  879.     
  880.     ----
  881.     
  882.     Believe me, you don't want to use WEScroll in a printing loop.
  883.     WEScroll calls the Toolbox routine ScrollRect(), which I think goes
  884.     through the QuickDraw bottlenecks set up by the printer driver.
  885.     Okay, you tell me you cleverly work around the ScrollRect() call
  886.     by setting the clip region to the empty region right before calling
  887.     WEScroll.  That might seem a good move, but it makes assumptions
  888.     on the inner workings of WEScroll.  In fact, starting from WASTE 1.2a4
  889.     WEScroll sometimes calls ScrollRect() and sometimes calls WEUpdate with a
  890.     NULL updateRgn, depending on the extent of hOffset/vOffset (ScrollRect
  891.     is used only if there's some overlap).  Now, WEUpdate(NULL, we) overrides
  892.     your clip region and you promptly get the second page superimposed onto
  893.     the first one.
  894.     
  895.     Fix: never *ever* call WEScroll from within a  print loop!
  896.     WEScroll doesn't just update the destination rectangle: it also *draws*
  897.     into your port because that's exactly what it's meant to do.
  898.     If what you need is really only changing the destination rectangle
  899.     without causing anything to be drawn, replace WEScroll with something
  900.     like:
  901.     
  902.       WEGetDestRect(&destRect, we);
  903.       WEOffsetLongRect(&destRect, hOffset, vOffset);
  904.       WESetDestRect(&destRect, we);
  905.     
  906.     Easy enough, isn't it?
  907.  
  908.     ----
  909.     
  910.     So, what do you do?  Well, this technique is a better overall technique anyways
  911.     for printing, and it's pretty simple.  I also think it's really nice cause
  912.     there's a lot less overhead in calling these 3 functions instead of WEScroll
  913.     and ClipRect.
  914.  
  915. */
  916.         // first, we get the destRect of the WE instance
  917.         
  918.         WEGetDestRect( &destRect, we );
  919.         
  920.         // then we "scroll" to the next page by just offsetting the
  921.         // rect's coordinates
  922.         
  923.         WEOffsetLongRect( &destRect, 0, -scrollAmount );
  924.         
  925.         // then we reinsert this new offsetted destRect back into the WE
  926.         // instance.  now we're on the next page!
  927.         
  928.         WESetDestRect( &destRect, we );
  929.         
  930.         // reset the bottom of the text's view rect to the full page
  931.         
  932.         WEGetViewRect( &r, we );
  933.         r.bottom = printRect->bottom;
  934.         WESetViewRect( &r, we );
  935.         
  936.         // look for a cancelation
  937.         
  938.         err = PrError();
  939. //        if ( err != noErr )
  940. //        {
  941. //            abortedPrinting = true;
  942. //            if ( err == iPrAbort )
  943. //                err = noErr;
  944. //        }
  945.  
  946.         if ( err == iPrAbort )
  947.         {
  948.             abortedPrinting = true;
  949.             err = noErr;
  950.         }
  951.         
  952.         
  953.         // done with that page
  954.         
  955.         PrClosePage( gThePrinterPort );
  956.     
  957.     } // end: while ( (!abortedPrinting) && (currentLine <= totalTextLines) )
  958.     
  959.     return err;
  960. }
  961.  
  962.  
  963. // based upon the size of the printed page (the rPage field of the TPrint record),
  964. // determine how many pages will be required to print the document
  965.  
  966. short    HsoiDetermineNumOfPages( WEReference we )
  967. {
  968.     short            numPages = 0;
  969.     Rect            margins;
  970.     LongRect        r;
  971.     LongRect        printRect, paperRect, destRect;
  972.     WEReference        scratchWE;
  973.     long            totalHeight, totalTextLines, currentLine = 1;
  974.     long            viewHeight;
  975.     long            scrollAmount;
  976.     
  977.     // this is a bit complex and will probably seem a bit unnecessary/overkill
  978.     // considering how this is all used in the end, but this is how we must
  979.     // do things to calc the number of pages.
  980.     
  981.     // first, we have to get some measurements....
  982.     
  983.     // set up the margins
  984.         
  985.     margins.left = HsoiInchesToDots( gMyPrefs.printLeftMargin, kHortz );
  986.     margins.right = HsoiInchesToDots( gMyPrefs.printRightMargin, kHortz );
  987.     margins.top = HsoiInchesToDots( gMyPrefs.printTopMargin, kVert );
  988.     margins.bottom = HsoiInchesToDots( gMyPrefs.printBottomMargin, kVert );
  989.     
  990.     // get the dimensions of the actual printed rect
  991.     
  992.     HsoiGetPrintRect( &margins, &printRect, gMyPrefs.printPageNumbers );
  993.     
  994.     // create and work with a copy of the text
  995.     
  996.     scratchWE = HsoiCloneTextHandle( &printRect, we );
  997.     
  998.     // set the port in the scratchWE to the printer port
  999.     
  1000.     WESetInfo( wePort, (Ptr)&gThePrinterPort, scratchWE );
  1001.         
  1002.     // if the user wants to print double spaced text, adjust as such
  1003.     
  1004.     if ( gMyPrefs.printDoubleSpaced )
  1005.         HsoiSetSpacing( scratchWE );
  1006.  
  1007.     // get the number of lines in our text
  1008.     
  1009.     totalTextLines = WECountLines( scratchWE );
  1010.     
  1011.     // adjust the bottom of our text's destRect
  1012.     
  1013.     totalHeight = WEGetHeight( 0, totalTextLines, scratchWE );
  1014.     WEGetDestRect( &r, scratchWE );
  1015.     r.bottom = r.top + totalHeight;
  1016.     WESetDestRect( &r, scratchWE );
  1017.     
  1018.     // loop through, counting up the number of pages
  1019.     
  1020.     while ( currentLine <= totalTextLines )
  1021.     {
  1022.         scrollAmount = 0;
  1023.                 
  1024.         // calculate the height of the viewRect
  1025.         
  1026.         WEGetViewRect( &r, scratchWE );
  1027.         viewHeight = r.bottom - r.top + 1;
  1028.         
  1029.         // figure out how many lines per page
  1030.         
  1031.         while ( ((scrollAmount + WEGetHeight( currentLine - 1, currentLine, scratchWE )) <= viewHeight )
  1032.                 && ( currentLine <= totalTextLines ) )
  1033.         {
  1034.             scrollAmount += WEGetHeight( currentLine - 1, currentLine, scratchWE );
  1035.             currentLine++;
  1036.         }
  1037.         
  1038.         // adjust the bottom of the printed page based on t he number of lines printed
  1039.         
  1040.         WEGetViewRect( &r, scratchWE );
  1041.         WERectToLongRect( &(*gPrinterRecord)->rPaper, &paperRect );
  1042.         if ( (paperRect.top + margins.top) < printRect.top )
  1043.             r.bottom = printRect.top + scrollAmount;
  1044.         else
  1045.             r.bottom = paperRect.top + margins.top + scrollAmount;
  1046.         WESetViewRect( &r, scratchWE );
  1047.         
  1048.         // get the destRect of the WE instance
  1049.         
  1050.         WEGetDestRect( &destRect, scratchWE );
  1051.         
  1052.         // then we "scroll" to the next page by just offsetting the
  1053.         // rect's coordinates
  1054.         
  1055.         WEOffsetLongRect( &destRect, 0, -scrollAmount );
  1056.         
  1057.         // then we reinsert this new offsetted destRect back into the WE
  1058.         // instance.  now we're on the next page!
  1059.         
  1060.         WESetDestRect( &destRect, scratchWE );
  1061.         
  1062.         // reset the bottom of the text's view rect to the full page
  1063.         
  1064.         WEGetViewRect( &r, scratchWE );
  1065.         r.bottom = printRect.bottom;
  1066.         WESetViewRect( &r, scratchWE );
  1067.         
  1068.         // and finally, increment the page counter
  1069.         
  1070.         numPages++;
  1071.     
  1072.     } // end: while ( currentLine <= totalTextLines )
  1073.  
  1074.     if ( scratchWE != nil )
  1075.         WEDispose( scratchWE );
  1076.  
  1077.     return numPages;
  1078. }
  1079.  
  1080. #pragma mark -
  1081. #pragma mark ••• Print •••
  1082.  
  1083. void    HsoiDoPrint( WindowRef    windowToPrint )
  1084. {
  1085.     WEReference        we, scratchWE;
  1086.     LongRect        printRect;
  1087.     TPrStatus        thePrinterStatus;
  1088.     Rect            margins;
  1089.     OSErr            err;
  1090.     register short i;
  1091.     Str255            tempStr;
  1092.     
  1093.     
  1094.     // since these are global variables (at least to this file, hence static), we need
  1095.     // to reinitialize them to hold nil values so we don't end up with weird strings
  1096.     // everywhere...
  1097.     
  1098.     for ( i = 0; i <= 255; i++ )
  1099.     {
  1100.         sDocumentName[i] = nil;
  1101.         sMsgStr1[i] = nil;
  1102.         sMsgStr2[i] = nil;
  1103.     }
  1104.     
  1105.     myPrintStatusDialog = nil;
  1106.       
  1107.     // now, get the WE reference
  1108.     
  1109.     if ( windowToPrint != nil )
  1110.         we = HsoiGetWindowWE( windowToPrint );
  1111.     else
  1112.         we = nil;
  1113.         
  1114.     // put up the Print dialog and get the printer port set up.
  1115.     // if the user selected "OK", let's print!
  1116.     
  1117.     if ( HsoiOpenPrintManager( windowToPrint ) )
  1118.     {
  1119.         // set the cursor to the watch
  1120.         
  1121.         SetCursor( *gWaitCursor );
  1122.         
  1123.         // get the number of copies
  1124.         
  1125.         sNumOfCopies = (*gPrinterRecord)->prJob.iCopies;
  1126.         
  1127.         // get the first and last pages
  1128.         
  1129.         sFirstPage = (*gPrinterRecord)->prJob.iFstPage;
  1130.         sLastPage = (*gPrinterRecord)->prJob.iLstPage;
  1131.         
  1132.         // reset to 1
  1133.         
  1134.         (*gPrinterRecord)->prJob.iFstPage = 1;
  1135.         
  1136.         // reset to the max
  1137.         
  1138.         (*gPrinterRecord)->prJob.iLstPage = iPrPgMax;
  1139.         
  1140.         // make sure we don't print page the last page
  1141.         
  1142.         if ( sNumOfPages < sLastPage )
  1143.             sLastPage = sNumOfPages;
  1144.           
  1145.           // make sure further output goes to the printer
  1146.     
  1147.         SetPort( (GrafPtr)gThePrinterPort );
  1148.         
  1149.         // allow fractional character widths for colums to line up properly
  1150.         
  1151.         SetFractEnable( true );
  1152.         
  1153.         // allow for the desired margins
  1154.         
  1155.         margins.left = HsoiInchesToDots( gMyPrefs.printLeftMargin, kHortz );
  1156.         margins.right = HsoiInchesToDots( gMyPrefs.printRightMargin, kHortz );
  1157.         margins.top = HsoiInchesToDots( gMyPrefs.printTopMargin, kVert );
  1158.         margins.bottom = HsoiInchesToDots( gMyPrefs.printBottomMargin, kVert );
  1159.         
  1160.         // get the dimensions of the actual printed rect
  1161.         
  1162.         HsoiGetPrintRect( &margins, &printRect, gMyPrefs.printPageNumbers );
  1163.         
  1164.         // create and work with a copy of the text
  1165.         
  1166.         scratchWE = HsoiCloneTextHandle( &printRect, we );
  1167.         
  1168.         // set the port in the scratchWE to the printer port
  1169.         
  1170.         err = WESetInfo( wePort, (Ptr)&gThePrinterPort, scratchWE );
  1171.             
  1172.         // if the user wants to print double spaced text, adjust as such
  1173.         
  1174.         if ( gMyPrefs.printDoubleSpaced )
  1175.             HsoiSetSpacing( scratchWE );
  1176.         
  1177.         // remove any selection range and deactivate the text
  1178.         
  1179.         WESetSelection( 0, 0, scratchWE );
  1180.         WEDeactivate( scratchWE );
  1181.         
  1182.         // and just before we really get into things, get our "printing in progress"
  1183.         // dialog set up (we installed the address of the proc earlier in
  1184.         // HsoiOpenPrintManager(), now we're just getting the stuff for it all
  1185.         // ready)
  1186.         
  1187.         // get the relevant text for the dialog...
  1188.         
  1189.         GetWTitle( windowToPrint, sDocumentName );
  1190.         GetIndString( tempStr, rPrintingStrings, strPrintBeginning );
  1191.         HsoiConcatString( sMsgStr1, tempStr );
  1192.         HsoiConcatString( sMsgStr1, sDocumentName );
  1193.         GetIndString( tempStr, rPrintingStrings, strPrintMiddle );
  1194.         HsoiConcatString( sMsgStr1, tempStr );            
  1195.         
  1196.         GetIndString( sMsgStr2, rPrintingStrings, strPrintEnd );
  1197.           
  1198.           // let's now show the printing dialog
  1199.  
  1200.         myPrintStatusDialog = GetNewDialog( rPrintStatusDialog, nil, MOVE_TO_FRONT );
  1201.         ShowWindow( GetDialogWindow(myPrintStatusDialog) );
  1202.         
  1203.         // now, i'm not sure if this is a good thing to do or not (writing printing
  1204.         // code for Macintosh can be such an adventure!), but since not all
  1205.         // printer drivers support custom idle procs (and in which case, i'd fear
  1206.         // that since we overroad the default idle proc by installing our own, that
  1207.         // the user will have no way to cancel, not even cmd-.) i want to call
  1208.         // the idle proc at least once...this is cause it's the idle proc that
  1209.         // draws all our text in the dialog....if it's not called at least once,
  1210.         // the user will just see a blank dialog box, and that's no fun!
  1211.         
  1212.         hsoiMyPrintIdleProc();
  1213.         
  1214.         // and now, print until we're done!
  1215.         
  1216.         err = HsoiPrintUntilDone( &margins, &printRect, scratchWE, windowToPrint );
  1217.         
  1218.         // close the print port and print spooled data, if needed
  1219.         
  1220.         PrCloseDoc( gThePrinterPort );
  1221.         
  1222.         if ( ((*gPrinterRecord)->prJob.bJDocLoop == bSpoolLoop) && (PrError() == noErr) )
  1223.             PrPicFile( gPrinterRecord, nil, nil, nil, &thePrinterStatus );
  1224.             
  1225.         PrClose();
  1226.         
  1227.         // and now clean up and restore things as they should be
  1228.         
  1229.         if ( printIdleUPP != nil )
  1230.             DisposeRoutineDescriptor( printIdleUPP );
  1231.         DisposeDialog( myPrintStatusDialog );
  1232.         SetPortWindowPort( windowToPrint );
  1233.         SetFractEnable( false );
  1234.         if ( scratchWE != nil )
  1235.             WEDispose( scratchWE );
  1236.         InitCursor();
  1237.         
  1238.     } // end: if ( HsoiOpenPrintManager() )
  1239.     
  1240.     return;
  1241. }
  1242.  
  1243.  
  1244.  
  1245. // hmm..wonder if i'd want to put a rotating cursor in here...*shrug*  it'd be easy,
  1246. // but do i want to do it?  sure...why not :-)  but do it later
  1247.  
  1248. // Don't give the dialog box a static text and ^0 cause those sorts of things
  1249. // make the screen flicker.
  1250.  
  1251. // For some reason, the cursor won't animate....maybe this is a place where I'll need
  1252. // to put in a VBL task....
  1253.  
  1254. pascal void hsoiMyPrintIdleProc( void )
  1255. {
  1256.     GrafPtr         oldPort;
  1257.     EventRecord     event;
  1258.     char            testChar;
  1259.     short            dialogWidth;
  1260.     short            dialogCenter;
  1261.     short            strWidth;
  1262.     short            halfStrWidth;
  1263.     short            offset;
  1264.     Point            currentPosition;
  1265.     FontInfo        theFontInfo;
  1266.     register short    lineHeight;
  1267.     short            driverResFile;
  1268.         
  1269.     driverResFile = CurResFile();
  1270.     UseResFile( gAppResourceFork );
  1271.     
  1272.     // animate that cursor
  1273.     
  1274. //    RotateCursor( TickCount() );
  1275.         
  1276.     GetPort( &oldPort );
  1277.     SetGrafPortOfDialog( myPrintStatusDialog );
  1278.     
  1279.     // let's center the text in the status dialog
  1280.     
  1281.     // since we never know just how long the text could be (variable sizes/lengths
  1282.     // of the document's name), we'll have to do a little bit of centering stuff
  1283.     
  1284.     dialogWidth = GetWindowPort((WindowRef)myPrintStatusDialog)->portRect.right -
  1285.                     GetWindowPort((WindowRef)myPrintStatusDialog)->portRect.left;
  1286.     dialogCenter = dialogWidth / 2;
  1287.     
  1288.     strWidth = StringWidth( sMsgStr1 );
  1289.     halfStrWidth = strWidth / 2;
  1290.     
  1291.     offset = dialogCenter - halfStrWidth;
  1292.     
  1293.     // the choice to move to 35 pixels vertically is arbitrary.  it'd be nice to figure
  1294.     // out stuff to totally center both lines, now wouldn't it?
  1295.     
  1296.     MoveTo( offset, 35 );
  1297.     DrawString( sMsgStr1 );
  1298.  
  1299.     // now center the other string...
  1300.     
  1301.     // let's move down a line
  1302.     
  1303.     GetPen( ¤tPosition );
  1304.     
  1305.     GetFontInfo( &theFontInfo );
  1306.     lineHeight = theFontInfo.ascent + theFontInfo.descent + theFontInfo.leading;
  1307.     
  1308.     // and now center it horizontally
  1309.     
  1310.     strWidth = StringWidth( sMsgStr2 );
  1311.     halfStrWidth = strWidth / 2;
  1312.     
  1313.     offset = dialogCenter - halfStrWidth;
  1314.  
  1315.     // move the pen
  1316.     
  1317.     MoveTo( offset, currentPosition.v + lineHeight );
  1318.     
  1319.     // draw the string
  1320.     
  1321.     DrawString( sMsgStr2 );
  1322.        
  1323.     // now check for relevent events.  in our limited case, we just want to
  1324.     // see if the user hit cmd-. to cancel (you could do more stuff in here, but do
  1325.     // be careful what you do...not everything is kosher to do in a print idle
  1326.     // procedure...for more information, see Inside Macintosh: Imaging with QuickDraw
  1327.     // and the chapter in there on Printing).  As a note, if you do supply your own
  1328.     // custom print idle proc (like this), at the minimum you MUST check for cmd-. to
  1329.     // occur to cancel.  other things to cancel might be that you put a cancel button
  1330.     // in your dialog, and you check for keyDown's on that button...
  1331.     
  1332.     // one BIG thing you'll notice is that we NEVER call PrError() in an idle proc.
  1333.     // this is a BIG NO NO!!!!  there are many times that an error can and does occur
  1334.     // during printing, but this can be stuff meant solely to facilitate communication
  1335.     // between the computer and the printer...it is not meant for you nor the user!!
  1336.     // that is why what we do is sit around looking for a keyDown event...and if it's
  1337.     // a keyDown event, if there is the cmd key, and if the character is a period, we
  1338.     // call  PrSetError( iPrAbort ) to put a cancel into the print error queue and then
  1339.     // we check for this error in HsoiPrintUntilDone().
  1340.     
  1341.     // if you decide to write/use an idle proc in your application, i highly recommend
  1342.     // reading about them in Inside Macintosh: Imaging with QuickDraw: Printing, or at
  1343.     // least read about them in THINK Reference (look up "TPrJob" and read the stuff at
  1344.     // the bottom of the entry)
  1345.     
  1346.     if ( GetNextEvent(keyDownMask, &event ) )
  1347.     {
  1348.         if ( (event.modifiers & cmdKey) != 0 )
  1349.         {
  1350.             testChar = event.message & charCodeMask;
  1351.             if ( testChar == '.' )
  1352.                 PrSetError( iPrAbort );
  1353.         }
  1354.     }
  1355.    
  1356.     
  1357.     SetPort( oldPort );
  1358.     
  1359.     UseResFile( driverResFile );
  1360.     
  1361.     return;
  1362. }
  1363.     
  1364.