$Id: AbiSource_DialogFramework.abw,v 1.4 2002/12/04 01:37:31 dom Exp $

AbiSource Dialog Framework

Version 1.0

Copyright (C) 1999 AbiSource, Inc., All Rights Reserved.

Jeff Hostetler

jeff@abisource.com

AbiSource, Inc.

$Date: 2002/12/04 01:37:31 $

1. Introduction

AbiWord makes use of a Dialog Framework which simplifies the complicated task of managing and raising modal dialogs in a application-neutral, platform-neutral fashion. It also manages the dialog-specific persistence information in an automatic and consistent manner without bothering the application.

2. Types of Dialogs

Before adding a dialog, the following questions must be answered:

[1] Is the dialog application-specific (ie. AbiWord vs AbiCalc) or is it application-independent (theoretically usable by any application). The File-Open dialog on most systems, for example.

[2] What is the persistence nature of the information in the dialog? That is, what information (either user-entered or system-supplied) that is shown in one invocation of the dialog should be remembered and shown the next time that the dialog is raised (assuming nothing in the application needs to override it).

The Dialog Framework defines 3 levels of persistence:

[a] no-persistence (think of a yes/no message box)

[b] frame (window) persistence

[c] application (global) persistence

3. Class Hierarchy

The file abi/src/xap/xp/xap_Dialog.h gives the class hierarchy of the Dialog Framework:

Dialog

NonPersistent [*]

Persistent

FramePersistent [*]

AppPersistent [*]

From the ones marked with [*] we then derive a dialog-specific subclass (for FileOpenSaveAs, MessageBox, FontChooser, etc.). From these, we then derive platform-specific versions. For example:

Dialog

NonPersistent [*]

MessageBox [**]

UnixMessageBox [***]

WinMessageBox [***]

Persistent

FramePersistent [*]

AppPersistent [*]

FileOpenSaveAs [**]

UnixFileOpenSaveAs [***]

WinFileOpenSaveAs [***]

The business logic in the application (in abi/src/wp/ap/xp/ap_EditMethods.cpp for example) requests dialogs from the Dialog Framework by type (the ones marked [**]). Platform-specific code in the Application Framework takes care of the [***] portion.

4. API to a Dialog

Needless to say, the [**] classes define a MINI-API for each type of dialog using absolute and virtual methods, so that the business logic can just work with the [**] classes. For example, the MessageBox dialog defines the following methods and data types (in abi/src/xap/xp/xap_Dlg_MessageBox.h):

typedef enum { b_O, b_OC, b_YN, b_YNC } tButtons;

typedef enum { a_OK, a_CANCEL, a_YES, a_NO } tAnswer;

void setMessage(const char * szMessage);

void setMessage(const char * szMessage, const char * sz1);

void setButtons(AP_Dialog_MessageBox::tButtons buttons);

void setDefaultAnswer(AP_Dialog_MessageBox::tAnswer answer);

AP_Dialog_MessageBox::tAnswer getAnswer(void) const;

virtual void runModal(XAP_Frame * pFrame) = 0;

After requesting an instance of the MessageBox dialog from the Dialog Factory, application logic can simply use the dialog by doing something like the following:

...

pDialog->setMessage("question...");

pDialog->setButtons(b_OC);

pDialog->setDefaultAnswer(a_OK);

pDialog->runModal(pFrame);

answer = pDialog->getAnswer();

...

In a similar fashion each type of dialog defines (in cross-platform code) the set of verbs that it requires.

5. The Dialog Factory

The Dialog Framework contains a Dialog Factory that is contained in the {xap,ap}_{Frame,App} code The Dialog Factory deals with the specific class construction and persistence, such that application logic can just request a dialog by ID and let the Dialog Factory take care of the details.

The following code shows an example of how to request a MessageBox dialog from the Dialog Factory:

AP_DialogFactory * pDialogFactory

= (AP_DialogFactory *)(pFrame->getDialogFactory());

AP_Dialog_MessageBox * pDialog = (AP_Dialog_MessageBox *)

(pDialogFactory->requestDialog(XAP_DIALOG_ID_MESSAGE_BOX));

... use dialog as shown in previous section ...

pDialogFactory->releaseDialog(pDialog);

return (answer);

This code snippet was taken from code in abi/src/wp/ap/xp/ap_EditMethods.cpp.

6. Creating a New Dialog

To add a new type of dialog, we need to do the following:

If you are creating an application-independent dialog:

[1] Define XAP_DIALOG_ID_foo for it in abi/src/xap/xp/xap_Dialog_Id.h.

[2] Create a [**] class for it in abi/src/xap/xp/xap_Dialog_Foo.{h,cpp} -- completely define the MINI-API for this dialog.

[3] Create [***] classes for each platform (abi/src/xap/{unix,win}/xap_{Unix,Win}Dialog_Foo.{h,cpp} -- create both sets even if you only "speak" one platform (stub in the other platform so it will at least compile).

[4] Update the Makefiles in abi/src/xap/{Makefile,{unix,win}/Makefile}. Make sure that you get the .../xap/Makefile -- most link failures are due to adding it to the .../xap/unix/Makefile or .../xap/win/Makefile but forgetting to add it to the upper-level one.

If an application-dependent dialog, do steps [1-4] above but in abi/src/wp/ap/ instead of abi/src/xap/.

For either type of dialog:

[5] Add declarations for the dialog in abi/src/wp/ap/{unix,win}/ap_{Unix,Win}Dialog_All.h -- there are 2 sections in this file, add something to both.

[6] On win32, if not using a common dialog, add a resource ID to abi/src/wp/ap/win/ap_Win32Resources.rc2 and create a dialog template in abi/src/wp/ap/win/ap_Win32Resources_DialogFoo.rc2. (You can use the MSVC dialog designer if you want, but I usually edit the output by hand before putting it in the tree.)

With some luck (and hopefully I haven't left anything out) you should have it.

7. Supplemental Notes

[a] For some related dialogs (like file-open and save-as) we use the same code for both -- but with 2 unique ID's -- the association is made in the DeclareDialog() statements in the .../...Dialog_All.h files.

[b] All dialogs run as modal dialogs (hence the runModal call in the base class :-) We do not (at present time) have need to modeless dialogs.

[c] All persistent dialogs are instantiated once (per-frame or per-application) and re-used between screen invocations. The Dialog Factory takes care of instantiation and final destruction. This allows you to store anything in the class that you want to persist between appearances on screen. The useStart() and useEnd() methods are called by the Dialog Factory before and after your application logic uses the dialog (during the call to requestDialog() and during the releaseDialog() call, respectively), so your dialog can do any per-use initialization/cleanup.

[d] At this point we haven't dealt with localization issues, so (for now) we've just hard-coded English text in the dialogs. We'll deal with that later.