CHICAGO STYLE PROPERTY SHEETS (TAB DIALOGS) IN OWL 2.0
======================================================

Introduction
------------

With the complexity of modern applications, it is sometimes necessary to
group dialog boxes together.  Traditionally this was done by having
dialog boxes with buttons that brought up yet more dialog boxes, but this
approach is extremely cumbersome and not intuitive for users.

More recently, applications like Quattro Pro, and subsequently Microsoft's
Office applications (Word, Excel etc.) have used "property dialogs" - a
dialog box which contains a series of "tabs" that allow the user to
quickly switch from one screen to another like flipping pages in a
notebook.

Such is the usefullness of the "property dialog" approach, Chicago (or
Windows 4 if you prefer) will make widespread use of them.  Now you
don't have to wait, as I present here a set of C++ classes that allow
you to implement Chicago style property dialogs today!


How to use property dialogs
---------------------------

My property dialog classes are built around two simple classes, both of
which derive from the OWL TDialog class:

  TPropertyDialog       a holder for the pages
  TPropertyPage         an individual page

Generally you will not need to override TPropertyDialog, as all of the
functionality of your application will be built around the actual pages.

To create a property dialog you need to follow these steps:

1. Create an instance of TPropertyDialog (or your derived class)
2. Call the Add function in TPropertyDialog to add each of the pages
3. Execute the dialog

The following code fragment (taken from the supplied test program)
shows these three steps being performed.

	void TMainWindow::TestDialog ()
	{
		TPropertyDialog Property (this, IDD_TEST, Tab::DoubleHeight);

		Property.Add ("First page",     new TMyPropertyPage (CHILD_1));
		Property.Add ("Second page",    new TMyPropertyPage (CHILD_2), FALSE);
		Property.Add ("Third page",     new TMyPropertyPage (CHILD_3));
		Property.Add ("Fourth page",    new TMyPropertyPage (CHILD_4));

		Property.Execute ();
	}

The property dialog will handle all of the drawing of the tabs and
switching between pages.

The property pages themselves are implemented as child dialogs (ie. they
have the WS_CHILD style).  When TPropertyDialog is executed is creates
all of the property page dialog and shows the first of them.  To prevent
undue flicker you should make sure you page dialogs do NOT have the
WS_VISIBLE style, as it is up to TPropertyDialog to show them at the
right time.


So what do I need to do?
------------------------

As stated earlier, you will want to override the TPropertyPage class to
add your own functionality.  Your derived class will generally need to
override the following:

	void    SetupWindow ()  - to set up the fields on the page
	void    SaveData ()     - to save away the data before closing

The SaveData takes the place of the CmOk function that you would generally
override.  Although you will need to put Ok (and Cancel if necessary) on
your page, these are handled by TPropertyPage and should NOT be overriden,
as they need to tell the TPropertyDialog to close (not the page).


Things to try
-------------

You might like to play around with the following:

- various styles of tab style are available to the programmer.  The are
  specified via the third parameter to TPropertyDialog, eg.

		TPropertyDialog Property (this, IDD_TEST, Tab::DoubleHeight);

  The following styles are available (all must be prefixed with "Tab::")

  - Tab::SingleHeight       - single height (one row of text) tabs
  - Tab::DoubleHeight       - double height (two rows of text) tabs
  - Tab::VariableWidth      - variable width tabs
  - Tab::FixedWidth         - fixed width tabs
  - Tab::ColorActive        - color the active tab (SetActiveColor sets color)
  - Tab::Collapsing         - "collapse" (hide) disabled tabs
  - Tab::Justified          - expand tab widths to make them fit neatly
  - Tab::Stacked            - allow multiple rows of tabs.  If not specified
							  tabs will be scrollable if too wide.  Not that
							  this style automatically disables Collapsing
							  and enables Justified
  - Tab::AllowDupPages      - allow one dialog to be shared by several pages
  - Tab::CreateOnDemand     - see below

- if you want to add static fields to the property dialog (eg. for an
  informative message), override the TPropertyDialog::AdjustMargin ()
  function.  This will allow you to change the area occupied by the tabs.


NEW! Tab::CreateOnDemand style
------------------------------

Several people have asked me whether or not it is possible to only create
the physical pages when the tab is selected, as they are running into
resource problems.  Well now you can -- just add the Tab::CreateOnDemand
style when creating your TPropertyDialog and the system will do the rest.

Well, not quite.  Unfortunately it is still up to the programmer to
supply a way to store the data away (before destroying the old page your
SaveData function will be called).  The simplest way is to define an OWL
transfer buffer.  TPropertyTab::SaveData has now been changed to save away
to a transfer buffer by default (of course this has no affect if you haven't
defined a transfer buffer)


NEW! TFontPropertyPage class
----------------------------

This is a new class which provides common-dialog style font selection
functionality on a property page, much as found in the latest MS-apps.

To use this class you need to first of all create an instance of TFontData
(or TPrinterFontData for use with printer fonts) and set up certain fields
there (if necessary), then just call TPropertyDialog::Add passing a pointer
to a "new"ed TFontPropertyPage.

NB. TFontData is derived from TChooseFontDialog::TData, so it should be rel-
atively straightforward to modify any existing app to use TFontPropertyPage.
TFontData differs in that it initialises the members to default values

	TPropertyDialog     Format (this, IDD_FORMATDLG, Tab::SingleHeight);
	TFontData           fontdata;

	Format.Add ("Font", new TFontPropertyPage (IDD_FONTDLG, fontdata));
	:

	Format.Execute ();


Creating other dialogs from a property-page
-------------------------------------------

One occasion you may want to display a popup dialog when the user presses a
button on a property-page.  Because the pages are child windows, doing this
using the page as the parent to the popup causes the active window problems
when the popup dialog is closed.

To get around this you simply you can call the function GetParentWindow() to
return a pointer to the first parent window in the hierarchy which is not a
child window.  Just using the TPropertyDialog is OK too, but only when it is
modal (and therefore can be made active), and not when it is being used as
the client to another window (eg. in an MDI child window)

	TMyDialog (GetParentWindow (), IDD_MYDIALOG).Execute ();

This behaviour does not affect messageboxes, as TPropertyPage contains its
own MessageBox function which will automatically use the window returned by
GetParentWindow as its parent.


Using transfer buffers/arrays with property dialogs
---------------------------------------------------

Using transfer buffers (or my own transfer arrays) with the property dialog
classes is sometimes a necessary evil, particularly if you are using the new
CreateOnDemand style, as the page dialogs are only created when you select
that page and are destroyed when you move to another.  As a result you need to
specify some way to store the information away, and transfer mechanisms are
one such way.

To use the either transfer mechanism you need to define the structures at the
same time as you define the property dialog.  Then after creating each page
attach the relevant transfer buffer/array to that page:

	 TPropertyDialog dlg (this, IDD_MYPROPDLG);
	 TPropertyPage *page;
	 TRANSFERPAGE1 tf1; // transfer structure of page 1
	 TRANSFERPAGE1 tf2; // transfer structure of page 2 etc.

	 page = new TYourPage1 (IDD_PAGE1);
	 page->SetTransferBuffer (&tf1);
	 dlg.Add ("First page", *page);

	 page = new TYourPage2 (IDD_PAGE2);
	 page->SetTransferBuffer (&tf2);
	 dlg.Add ("Second page", *page);

	 // here you will want to initialise the buffers prior to showing the dialog
	 :

	 dlg.Execute ();

For information on how my transfer arrays work and how to use them in your
application see the separate file TFARRY.TXT.  The supplied test program also
has an example of the use of transfer arrays with property dialogs which may
be a useful reference

NB. To preserve compatibility property dialogs won't use transfer arrays by
	default, but will use the old transfer buffers instead.  To use transfer
	arrays you need to add 'USETRANSFERARRAYS=TRUE' to the Compiler Defines


Limitations in this version
---------------------------

- it is necessary to place at least one control in the main property
  dialog box for it to work with ctl3d.  The simplest way around this
  problem is to place a single static control in your TPropertyDialog and
  make it invisible (actually this is a ctl3d limitation, not mine)


Corrections/Amendments in this version
--------------------------------------

as at 8th July 1994 (with thanks to Bill Zink of TeamB):

- WS_TABSTOP style removed from the (private) tbProperty control
- now overrides DoExecute to create the TPropertyDialog modelessly, while
  still appearing modal to the caller (doesn't use BeginModal at present..)
- CmOk corrected to not GP fault if you haven't added any tabs to a
  TPropertyDialog (if you don't add tabs, TPropertyDialog should look
  and work EXACTLY like TDialog, making it useful as a common base class)
- double height tabs can now be variable width (uses an iteration mechanism
  increasing the width until two lines of text will fit on the tab)

as at 25th August 1994:

- tabs can be "stacked" to occupy more than one row (as they do in Word's
  Tools|Options dialog)
- tabs can now be scrolled horizontally if there is insufficient space for
  them all to be displayed.  Left and right scroll buttons appear which can
  be used to move between pages beyond the visible area
- New "CloseAction" action function added to all the Ok and Close action of
  TPropertyDialog to be overriden (for use by client dialogs in TMDIChild
  dialogs
- ForEachPage and FirstPageThat functions added to TPropertyDialog
- TPropertyDialog::Add is now virtual to allow extra functionality to be
  added (eg. to allow common attributes to be set up on every page)
- fixed a bug which caused tab widths to be calculated incorrectly when
  using double height tabs and long single words
- tab widths now calculated in advance to improve performance on slower
  machines
- AfterSelected functions added to allow actions to be carried out after a
  particular page becomes active (eg. setting up of a description field)
- tabs can now be selected using "hot-key" combinations much like buttons
- enum now used to enable tab styles for:
	- fixed/variable width
	- single/double height tabs
	- stacked/scrolling tabs
	- justified and collapsed tabs
	- coloring of active tab
	- reuse of one dialog across several pages
	- create-on-demand to only create the physical pages when selected
- TFontPropertyPage class also supplied.  This may not be fully debugged
  yet -- please report any bugs or problems you have using this class

as at 17th September 1994:

- there is a no longer a need to put the OK and Cancel buttons on each page.
  If you wish to put them on the property dialog you may do so, but you
  should put the OK button at the bottom of the dialog because the default
  TPropertyDialog::AdjustMargin will automatically adjust to take account of
  it at that position.  Both tabbing and hot-keying can be used to jump to a
  control either on the page or in the property dialog itself
- tabs now use same color scheme as the main dialog (based on Control Panel)
- support for transfer arrays now 'built in' via the USETRANSFERARRAYS macro
- WideMargins tab style added.  This adds a little extra space to the text on
  the tab making it a little wider.  The 'SetWideMarginWidth' function allows
  the amount of extra space to be changed


Getting in touch with the author
--------------------------------

If you have any experiences good or bad using this code, I would
appreciate hearing from you.  In the past it is the great feedback that
have led to a lot of the new features being added, which benefits
everybody.

I am distributing this as CharityWare.  If you find this code of great
use and you feel you'd like to make a financial contribution you can
send your contributions to:

	Steve Saxon
	Flat 2, 4a Grange Park
	Ealing
	London W5 3PL
	England

I'm afraid you'll have to send cash (preferably not coins!) as it isn't
worth my paying 15 pounds to have cheques cleared (unless you are
sending hundreds of dollars!).  Please can you use registered post to reduce
the chance of your contribution getting 'lost' in the mail

I have intentionally kept this code as simple as possible because I
know that Chicago will be out soon(ish).  My intention is to port these
classes to Chicago hopefully without too many interface changes, (I'm
not a masochist!).  Again, let me know if this would be of interest to
you.

Sadly the addition of such features as scrolling and multiple rows
mean that the classes are now, by necessity, getting complex -- but hey,
thats progress!

Steve Saxon
London, England.
CIS: 100321,2355
