				An OWL Printing Example and a Tip-of-the-Day Class


Printing Overview:

This is a simple example of using a TPrintout object to handle a file
or database listing over multiple pages.  This is a departure from the
usual Borland printing examples which show you how the Paint DC for
a visible screen can be quickly mapped to a hardcopy printout. The
latter is very slick, but not of much help if the material you need to
print has little or nothing to do with the screen being displayed.

The sample TPrintout object works together with a FORM class I wrote.
The FORM class has helper member functions which draw boxes, lines, place
text, and draws bitmaps.  The base program is derived from the printing
example provided by Borland.  The menu has been enhanced to provide a
multipage printout of your WIN.INI file in landscape, using a boxed form
with a logo.

The Files:

	  PRINTING.CPP  -- A slightly modified version of that provided by Borland
	  TPOFILE.CPP   -- The derived TPrintout object which prints the WIN.INI file
	  FORM.CPP      -- The form class.


The FORM Class

This class changes the units to the DC to thousands of an inch (MM_HIENGLISH)
and moves the coordinate origin to the top left of the page.  I find this
more useful when "transcribing" hardcopy forms to a printout object.

FORM has member to print text at a given position, justify text against
a right margin, and center text.  There are box and line drawing members,
some of which are "overloaded" to have differing specifing formats.

A setfont member allows you to succinctly select a font and size.


The Sample TPrintout Object: TPOFILE.CPP

This is a derived TPrintout object.  Some things to note:

a)  The printer orientation is preset in GetDialogInfo().  The user
will then see this orientation (along with selected pages, etc) when
you show them the printer dialog box.  There are always a lot of questions
on the forum about this!

b)  TPrinter resets the DC after each page, so you have to instantiate
the form class (i.e. all drawing objects must be reselected) each time you
pass through the PrintPage member.

c)  A TPrintout object knows when it's done when HasPage() returns false.
Sometimes, you know the total number of pages in your printout so you can
do this:

	BOOL MyPrintout::HasPage(int page)
	{
		return page <= TotalPages;
	}

But suppose you don't know in advance how many pages there will be?  That's
what this example strives to show.  We use a boolean member variable
MoreRecords instead:

	BOOL MyPrintout::HasPage(int page)
	{
		return(MoreRecords);
	}

MoreRecords is set to TRUE in the constructor and set to FALSE in PrintPage
if either the end of file is reached of the user hits the print cancel
button.

d) Note how the GetTextExtent() member is used to obtain the height of
the currrent font, which is in turn used to figure out the number of lines
which can be accomodated per page.

e) It's a good idea to periodically review the OWL source code

		 C:\BC45\SOURCE\OWL\PRINTER.CPP

to see how TPrinter drives your TPrintout object.   After calling the
various setup members,  TPrinter calls your TPrintout::PrintPage() member
over and over again (advancing the page number for you each time) until
your HasPage() member returns a FALSE.

f)  If you're like me, it will take a while to get into the proper mindset
to fully appreciate the TPrintout Class and how to code PrintPage to meet
your needs.  However, it can be done and, after you learn it, it's actually
a pretty slick way to handle your printing!   I have about 20 TPrintout
objects in my application now, and can add new ones in a snap.

g)  You can do lots of preparatory stuff in GetDialogInfo() and BeginPrinting().
While this example was designed to show how to print a report of unknown
length, you could take this specific case (printing the WIN.INI file) and
get fancy:

		1.  Open WIN.INI in GetDialogInfo() and count the total number of
			 lines in the file.  Then close the file or seek to the beginning.

		2.  Instantiate a temporary font of the size you will print the lines
			 with and figure out the number of lines that can fit on a single
			 page.

		3.  Use these values to compute the total number of pages in the job.

Let's say you compute that 6 pages will be required.  You would then
modify your TPrintout object like this:

void PrintoutList1::GetDialogInfo(int& minPage,
											int& maxPage,
											int& selFromPage,
											int& selToPage)
{

	.. blah blah
	Totalpages =   // Compute the total number of pages as per above
	...blah blah
	minPage = 1;
	maxPage = TotalPages;

	selFromPage = selToPage = 0;
	TMyHotPrinter->GetSetup().Copies   = 1;
	TMyHotPrinter->GetSetup().FromPage = 1;
	TMyHotPrinter->GetSetup().ToPage   = TotalPages;
	..blah blah
}

BOOL MyPrintout::HasPage(int page)
{
		return page <= TotalPages;
}

In this way, your print dialog would come up with precisely the correct
number of pages and selected range.  You could modify your PrintPage()
member knowing that the HasPage() member will shut you down when you
have printed the 6th page.

TIP-OF-THE-DAY Class

Admittedly, this is quite a departure from printing, but this app also
illustrates a Tip-of-the-Day dialog I put together.

TIP.CPP, TIP.H, WEMTIPS.MSG and a the dialog tipdlg in PRINTING.RC combine
to form a fairly standard Tip-of-the-Day system.  Note that I have modified
the Borland printing example to override:

	void TRulerWin::SetupWindow()

and there, depending upon an INI file setting, I launch the tip of the day
dialog when the app launches:

	TipIni->GetString("Tips",message,sizeof(message),"Yes");
	if(strstr(message,"Yes"))
		 PostMessage(WM_COMMAND,CM_TIP);

Note that TipIin is an OWL TProfile class which encapsulates INI file reading
and writing.

The look of the tip dialog is somewhat unique.  With Cntrl3D enabled, we have
do some special stuff to get the text background to be white.  We also have to
impose a slight delay in the drawing of the light bulb icon on the white back
ground (thanks to Kent Reisdorph for his help here!).  The white TStatic will
overwrite the icon when the dialog is covered and then uncovered, losing our
bulb and the "Did you know" phrase.  The short timer logic solves this.

The example uses a simple TIP.INI file in the working directory.  In your
app, you will probably want to simply put two tip-related entries in your
existing INI file.

Where do we get the text for the tip dialog?  I've supplied my file WEMTIPS.MSG
which is from my mailing application.  You may want to revise how you store
and maintain the tip list.  I have a separate program which takes a flat
text file (each line is one tip -- some are pretty long lines!!) and places
them in a MSG file which has the following construct:

						First 4000 bytes:  2000 ints containing the file offset
												 of the tip text for tips 1-2000.
						Beyond 4000 bytes: The actual tip text, each separated by
												 a NULL character.

When the TIP class fires up, I read the entire directory of 2000 addresses
into an array.  When any given tip number is requested, I simply read the
file at the appropriate file offset:

void TipDlg::DisplayCurrentTip()
{
	fseek(fhelp,helpoffset[currenttipno],SEEK_SET);
	fgets(tipmessage,SIZE_OF_TIP-1,fhelp);
	TipStatic3->SetText(tipmessage);
}

E-Mail me if you want a copy of the short program which creates this MSG
file from the flat text file.

Setting Dialog Fonts

A frequent question on the CIS forum is how to set font sizes and types in
a dialog.  TIP.CPP contains several cases of this.  Here are the basic
rules...

	  1.  Make your TFonts part of the class so they have scope throughout
			the life of the class.

			HeaderFont = new TFont("MS Sans Serif",16,0,0,0,FW_BOLD,FF_DONTCARE,TRUE);

			This italic font is used for the "Did you know..." header.

	  2.  Anytime in or after SetupWindow(), you can apply this font to any
			TStatic, TEdit, TListBox, etc control.

			TipStatic2->SetWindowFont(*HeaderFont, TRUE);

	  3.  Don't forget to cleanup in the destructor.  The constructor for
			TFont doesn't include a "this" as a lead parameter, so OWL won't
			automatically delete this object.  You must do so!

			delete HeaderFont;


Acknowledgements:

Many thanks to the TeamB folks who patient guide me, especially Bob
Arnson and Kent Reisdorph.  If you don't already have the OWL Texts:

	"Object Windows Programing 2.0"  by Swan, Cantu Arnson  (GO RANDOM)
	"Teach Yourself Owl Programming in 21 Days" by Ian Spencer (SAMS Publishing)

go buy them right away!  You can buy both books online at CIS.


Good Programming!


Harry Whitehouse
The OWL Village Idiot
CIS 71611,1122

