Text Box objects
Volume Number: | | 7
|
Issue Number: | | 8
|
Column Tag: | | Jörg's Folder
|
Text Box View Objects
By Jörg Langowski, MacTutor Editorial Board
Text box view objects
We are proceeding quite a bit on our way to a full drawing program (with objects of different shape and text boxes); this month well finally see how to create text boxes in a window as independent views. Next month, then, well put it all together and maybe even start adding the disk I/O code.
Some Forth news first
First of all, though, Id like to let you know that Palo Alto Shipping still exists. I had them on the phone just a couple of days ago, the old number (415) 688-1111 still works. They are still supporting Mach2, and even working on a version that is System 7 compatible. But unfortunately, Forth has become their side activity. As I understood it, they are mainly working on hardware developments now, such as modems, and are doing the Mach2 support only as a service to faithful Forthers. Anyway, Rick Wiley assured me that the System 7-compatible version should be out at the end of the summer. Otherwise, he stated, there is just no market for Forth on the Macintosh. Too bad; people still ask me every now and then about whats happening with Mach2, and some quite complex programs seem to have been developed with this system. The Mach2 roundtable on GEnie has been inactive for a long time; in fact, it is supposed to be taken down soon.
Still, there are quite a number of Forth systems around for the Macintosh. Mach2, if PASC keeps its promise to get the System 7 upgrade out at the end of the summer (however, if you compare Bay Area summer temperatures to those we have here in France this year, that might well be in November); MacForth Plus by Creative Solutions - but we rarely hear anything from MacForth people here at MacTutor, although there seems to be quite a big crowd out there using MacForth; then the public domain systems, starting with PocketForth by Chris Heilman, and the two object oriented systems, Yerk (ex-NEON) and Mops, which youve read about in this column. Thus, if you need a Forth for the Macintosh, there are still quite a few possibilities.
When (if ever) I receive a System 7-compatible version of Mach2, Ill devote a column to it. Until then, lets continue our C++/MacApp explorations.
C++ text boxes
Last months example showed you how to create two text views as subviews of a scroller, and how to make either one of them the applications target, i.e., the place where key down events are sent. The application was very simple, it contained just one window which was open at startup, and had no means to save the views data in a document.
Of course, the drawing application that we are going to create is useless if we cant save our drawings. Therefore well have to restructure our example for being able to deal with documents. Also, at the end wed like to add as many text boxes as we like to our drawing; therefore well have to put them in a list which is part of the document, where all the other shapes (rectangles, lines, polygons...) are also kept.
A MacApp application that is supposed to deal with documents needs to define the method DoMakeDocument, which is called when a new document is created (by selecting New from the File menu) or an existing one opened. In our case (see example) this method will just create the new document through the new operator. We have only one class of documents. For applications that can deal with different document types, the parameter itsCmdNumber will indicate which type of document has been opened.
Initialization of the new document is done by the constructor TEditDocument::TEditDocument(). We do not need to call a special initializing method, as we would have to in Pascal. However, we need to call the generic IDocument method from within our constructor.
The documents window and views within that window are created by the DoMakeViews() method, which is called by MacApp. In the case of a document which is opened for printing, the flag forPrinting would be set to true; we ignore it here since we make no distinction between a document opened for display or for printing.
We create the document window from a view template which was created with ViewEdit. This template contains a TEditView, which is a subclass of TScroller (see last months column). That view contains four subviews of type TBoxView, our text boxes which we want to draw. Although TEditView contains a list of its subviews, well keep references to the text boxes in a separate list, fShapeList, to which we can later add other types of shapes (rectangles, lines, etc.), which are not views. This will be the list of objects to be drawn in our window.
Thus, we will define a new subclass of TShape, TEditBox, which contains a pointer to its document and to the TBoxView as instance variables. A TEditBox can then be accessed through the generic ForEachShapeDo method in TEditView, just as all the other shapes in the shape list. Here, the views frame is drawn by the method TEditBox::DrawYourself. The view itself will be drawn independently, because it is installed as a subview in the document window. (We could handle the drawing of text boxes completely separately from the other objects in the shape list, by defining an Adorn method for TBoxView; the way Ive done it here is just one possibility).
A TBoxView should respond to mouse clicks by becoming the target for text input. Thus, we define a method TBoxView::DoMouseCommand, whose only two statements are:
/* 1 */
this->GetWindow()->SetTarget(this);
inherited::DoMouseCommand(theMouse, info, hysteresis);
Thus, it sets the currently active target to itself and then calls DoMouseCommand from its superclass, so that the standard text selection mouse commands work.
DoMakeViews() gets references to the four subviews of the TEditView and creates TEditBoxes containing those views. This is just for testing purposes; later, well add a palette selection which will create a new text box and install it in the shape list, and its TBoxView in the list of subviews of the document window. I should point out here that, in order to get the views enclosing rectangles (their extents) with the GetFrame method, one has to focus on the superview containing them. Focusing means: setting the GrafPort to the window in which the view is installed, setting the windows origin to the origin of the view, and setting the clip region to the views superview. One can only focus a view whose window is visible, so we have to show the window first (aWindow->Show(true,false)).
Once the views have been created and installed in the window, they will respond to mouse clicks as you expect; when you type text in the active text box, you can select it with the mouse, cut, copy and paste; and when you click in an inactive text box, the blinking caret will appear there.
When you try out the example, note that the bottom left text box initially is set to the Courier font, while all the others are Geneva. When you copy text from one box to the other, youll notice that you can mix fonts in each box; Geneva text stays Geneva, irrespectively of which box you copy it to. Yes, MacApp text views are styled. Well explore this property further in the coming columns, and add a style selection menu as well.
Finally, Ill give you three screen dumps which show the ViewEdit settings for the view resources in the editor.rsrc file. They should allow those of you who dont get the source code disks to recreate the example from scratch. Although I recommend subscribing to the source disks, if only to avoid typographic errors.
See you next month.
Screen 1: Example view hierarchy (the view in the background is the TEditView)
Screen 2: TWindow parameters for the example (target setting is not important since the program changes the target anyway)
Screen 3: TEditView parameters (note setting of view ID and class name)
Screen 4: TBoxView parameters (note class name and view ID. The four views should have IDs tx01, tx02, tx03 and tx04 respectively)
Listing 1: editor.h
class TEditor : public TApplication {
public:
pascal TEditor(OSType itsMainFileType);
virtual pascal struct TDocument
*DoMakeDocument(CmdNumber itsCmdNumber);
pascal void HandleFinderRequest();
#ifdef qDebug
virtual pascal void IdentifySoftware();
#endif
};
class TEditView;
class TBoxView : public TTEView {
public:
pascal struct TCommand *DoMouseCommand
(Point *theMouse, EventInfo *info, Point *hysteresis);
#ifdef qDebug
virtual pascal void Fields
(pascal void (*DoToField) (StringPtr fieldName,
Ptr fieldAddr, short fieldType, void *link), void *link);
#endif
};
class TEditDocument : public TDocument {
public:
TEditView*fEditView;
pascal TEditDocument();
pascal void DoMakeViews(Boolean forPrinting);
pascal void DoNeedDiskSpace
(long *dataForkBytes, long *rsrcForkBytes);
pascal void DoRead(short aRefNum,
Boolean rsrcExists, Boolean forPrinting);
pascal void DoWrite(short aRefNum,
Boolean makingCopy);
pascal void Free();
#ifdef qDebug
virtual pascal void Fields
(pascal void (*DoToField) (StringPtr fieldName,
Ptr fieldAddr, short fieldType, void *link), void *link);
#endif
};
class TBox : public TObject {
public:
Rect fLocation;
BooleanfSelected;
RgnHandlefTagRgn;
Rect fTL,fTR,fBL,fBR,
fT,fB,fL,fR;
pascal TBox(Rect *itsLocation);
virtual pascal void DrawShape();
#ifdef qDebug
virtual pascal void Fields
(pascal void (*DoToField) (StringPtr fieldName,
Ptr fieldAddr, short fieldType, void *link), void *link);
#endif
};
class TShape : public TBox {
public:
short fPenSize;
PatternfPenPat;
PatternfFillPat;
pascal TShape(Rect *itsLocation);
#ifdef qDebug
virtual pascal void Fields
(pascal void (*DoToField) (StringPtr fieldName,
Ptr fieldAddr, short fieldType, void *link), void *link);
#endif
};
class TEditBox : public TShape {
public:
TBoxView *fBoxView;
TEditDocument *fEditDocument;
pascal TEditBox(Rect *itsLocation,
TBoxView *itsView, TEditDocument *itsDocument);
pascal void DrawShape();
#ifdef qDebug
virtual pascal void Fields
(pascal void (*DoToField) (StringPtr fieldName,
Ptr fieldAddr, short fieldType, void *link), void *link);
#endif
};
class TEditView : public TScroller {
public:
TEditDocument *fDocument;
TList *fShapeList;
// list of shapes (TEditView, boxes)
pascal void IEditView(TEditDocument *itsDocument);
pascal void AddShape(TBox *aBox);
pascal void DeleteShape();
pascal void ForEachShapeDo
(pascal void (*DoToItem) (TObject *item, void
*DoToItem_Staticlink),void *DoToItem_Staticlink);
pascal void Draw(Rect *area);
pascal void Free();
#ifdef qDebug
virtual pascal void Fields
(pascal void (*DoToField) (StringPtr fieldName,
Ptr fieldAddr, short fieldType, void *link), void *link);
#endif
};
// -- global definitions --
typedef pascal void (*DoToObject)
(TObject *aObject, void *DoToObject_staticlink);
Listing 2: editor.cp
#include <UMacApp.h>
#include <UPrinting.h>
#include <UTEView.h>
#include <Fonts.h>
#include <ToolUtils.h>
#include editor.h
// ***** Global constants
const OSType kSignature = JLMT;
const OSType kFileType = JL01;
const int kWindowID= 1002;
// ***** Class TEditor
pascal TEditor::TEditor(OSType itsMainFileType)
{TEditView*aEditView;
TBoxView *aBoxView;
IApplication(itsMainFileType);
if (gDeadStripSuppression)
{ aEditView = new TEditView;
aBoxView = new TBoxView;}
}
pascal struct TDocument
*TEditor::DoMakeDocument(CmdNumber itsCmdNumber)
{TEditDocument* aEditDocument;
aEditDocument = new TEditDocument;
FailNIL(aEditDocument);
return aEditDocument; }
pascal void TEditor::HandleFinderRequest() {};
#ifdef qDebug
pascal void TEditor::IdentifySoftware()
{ProgramReport
(\pEditor ©J.Langowski/MacTutor June 1991,false);
inherited::IdentifySoftware(); }
#endif
// ***** Class TEditDocument
pascal TEditDocument::TEditDocument()
{IDocument(kFileType, kSignature, kUsesDataFork,
!kUsesRsrcFork, !kDataOpen, !kRsrcOpen);
fSavePrintInfo = true; // save print info in data fork
}
pascal void
TEditDocument::DoMakeViews(Boolean forPrinting)
{VRect itsExtent;
Rect itsQDExtent;
TBoxView *aBoxView;
TEditBox *aEditBox;
TWindow*aWindow;
aWindow = NewTemplateWindow(kWindowID,this);
FailNIL(aWindow);
aWindow->SimpleStagger(kStdStaggerAmount,
kStdStaggerAmount, &gStdStaggerCount);
fEditView =
(TEditView*) aWindow->FindSubView(scrl);
FailNIL(fEditView);
fEditView->IEditView(this);
aWindow->Show(true,false); // so view can be focused
if (fEditView->Focus())
// must focus for ViewToQDRect
{ aBoxView =
(TBoxView*) aWindow->FindSubView(tx01);
FailNIL(aBoxView);
aBoxView->GetFrame(&itsExtent);
fEditView->
ViewToQDRect(&itsExtent,&itsQDExtent);
aEditBox =
new TEditBox(&itsQDExtent,aBoxView,this);
FailNIL(aEditBox);
fEditView->AddShape(aEditBox);
aBoxView =
(TBoxView*) aWindow->FindSubView(tx02);
FailNIL(aBoxView);
aBoxView->GetFrame(&itsExtent);
fEditView->
ViewToQDRect(&itsExtent,&itsQDExtent);
aEditBox =
new TEditBox(&itsQDExtent,aBoxView,this);
FailNIL(aEditBox);
fEditView->AddShape(aEditBox);
aWindow->SetTarget(aBoxView);
// just for testing, set to view no. 2
aBoxView =
(TBoxView*) aWindow->FindSubView(tx03);
FailNIL(aBoxView);
aBoxView->GetFrame(&itsExtent);
fEditView->
ViewToQDRect(&itsExtent,&itsQDExtent);
aEditBox =
new TEditBox(&itsQDExtent,aBoxView,this);
FailNIL(aEditBox);
fEditView->AddShape(aEditBox);
aBoxView =
(TBoxView*) aWindow->FindSubView(tx04);
FailNIL(aBoxView);
aBoxView->GetFrame(&itsExtent);
fEditView->
ViewToQDRect(&itsExtent,&itsQDExtent);
aEditBox =
new TEditBox(&itsQDExtent,aBoxView,this);
FailNIL(aEditBox);
fEditView->AddShape(aEditBox); }
}
pascal void TEditDocument::DoNeedDiskSpace
(long *dataForkBytes, long *rsrcForkBytes) {}
pascal void TEditDocument::DoRead(short aRefNum,
Boolean rsrcExists, Boolean forPrinting) {}
pascal void TEditDocument::DoWrite
(short aRefNum, Boolean makingCopy) {}
pascal void TEditDocument::Free() {}
#ifdef qDebug
pascal void TEditDocument::Fields(pascal void
(*DoToField) (StringPtr fieldName, Ptr fieldAddr,
short fieldType, void *link), void *link)
{DoToField(\pTEditDocument, nil, bClass, link);
DoToField(\pfEditView, (Ptr) &fEditView,
bObject, link);
inherited::Fields(DoToField, link); }
#endif
// ***** Shape class methods
pascal TBox::TBox(Rect *itsLocation)
{ fLocation = *itsLocation;
fSelected = false;}
pascal void TBox::DrawShape()
{// add code later for shape selections }
#ifdef qDebug
pascal void TBox::Fields(pascal void (*DoToField)
(StringPtr fieldName, Ptr fieldAddr, short fieldType,
void *link), void *link)
{DoToField(\pTBox, nil, bClass, link);
DoToField(\pfLocation, (Ptr) &fLocation, bRect, link);
DoToField(\pfSelected, (Ptr) &fSelected,
bBoolean, link);
DoToField(\pfTagRgn, (Ptr) &fTagRgn,
bRgnHandle, link);
DoToField(\pfTL, (Ptr) &fTL, bRect, link);
DoToField(\pfTR, (Ptr) &fTR, bRect, link);
DoToField(\pfBL, (Ptr) &fBL, bRect, link);
DoToField(\pfBR, (Ptr) &fBR, bRect, link);
DoToField(\pfT, (Ptr) &fT, bRect, link);
DoToField(\pfB, (Ptr) &fB, bRect, link);
DoToField(\pfL, (Ptr) &fL, bRect, link);
DoToField(\pfR, (Ptr) &fR, bRect, link);
inherited::Fields(DoToField, link); }
#endif
pascal TShape::TShape(Rect *itsLocation) : (itsLocation)
{ fPenSize = 1;
for (int i = 0; i<8 ; i++)
{ fPenPat[i] = qd.black[i];
fFillPat[i] = qd.gray[i];}
}
#ifdef qDebug
pascal void TShape::Fields(pascal void (*DoToField)
(StringPtr fieldName, Ptr fieldAddr, short fieldType,
void *link), void *link)
{DoToField(\pTShape, nil, bClass, link);
DoToField(\pfPenSize, (Ptr) &fPenSize,
bInteger, link);
DoToField(\pfPenPat, (Ptr) &fPenPat,
bPattern, link);
DoToField(\pfFillPat, (Ptr) &fFillPat, bPattern, link);
inherited::Fields(DoToField, link); }
#endif
pascal TEditBox::TEditBox(Rect *itsLocation, TBoxView
*itsView, TEditDocument *itsDocument) : (itsLocation)
{fBoxView = itsView;
fEditDocument = itsDocument; }
pascal void TEditBox::DrawShape()
{VRect itsExtent;
Rect itsQDExtent;
PenNormal();
fBoxView->GetFrame(&itsExtent);
fEditDocument->fEditView->
ViewToQDRect(&itsExtent,&itsQDExtent);
FrameRect(&itsQDExtent);
inherited::DrawShape(); }
#ifdef qDebug
pascal void TEditBox::Fields(pascal void (*DoToField)
(StringPtr fieldName, Ptr fieldAddr, short fieldType,
void *link), void *link)
{DoToField(\pTEditBox, nil, bClass, link);
DoToField(\pfBoxView, (Ptr) &fBoxView,
bObject, link);
DoToField(\pfEditDocument, (Ptr) &fEditDocument,
bObject, link);
inherited::Fields(DoToField, link); }
#endif
// ***** TBoxView methods
pascal struct TCommand
*TBoxView::DoMouseCommand(Point *theMouse,
EventInfo *info, Point *hysteresis)
{this->GetWindow()->SetTarget(this);
inherited::DoMouseCommand
(theMouse, info, hysteresis);
return gNoChanges;}
#ifdef qDebug
pascal void TBoxView::Fields(pascal void (*DoToField)
(StringPtr fieldName, Ptr fieldAddr, short fieldType,
void *link), void *link)
{DoToField(\pTBoxView, nil, bClass, link);
inherited::Fields(DoToField, link); }
#endif
// ***** TEditView methods
pascal void TEditView::IEditView
(TEditDocument *itsDocument)
{TStdPrintHandler*aStdPrintHandler;
TList *aList;
aStdPrintHandler = new TStdPrintHandler;
FailNIL(aStdPrintHandler);
aStdPrintHandler->IStdPrintHandler
(nil,this,kSquareDots,kFixedSize,!kFixedSize);
fPrintHandler = aStdPrintHandler;
fDocument = itsDocument;
aList = NewList();
fShapeList = aList; }
pascal void TEditView::AddShape(TBox *aBox)
{ fShapeList->InsertFirst(aBox); }
pascal void TEditView::DeleteShape()
{ fShapeList->Delete(fShapeList->First()); }
pascal void TEditView::ForEachShapeDo
(pascal void (*DoToItem) (TObject *item, void
*DoToItem_Staticlink),void *DoToItem_Staticlink)
{fShapeList->Each(DoToItem,DoToItem_Staticlink); }
pascal void DrawYourself(TBox *aBox, void *link)
{ aBox->DrawShape();}
pascal void TEditView::Draw(Rect *area)
{void *link;
ForEachShapeDo((DoToObject)DrawYourself,link); }
pascal void TEditView::Free() { }
#ifdef qDebug
pascal void TEditView::Fields(pascal void (*DoToField)
(StringPtr fieldName, Ptr fieldAddr, short fieldType,
void *link), void *link)
{DoToField(\pTEditView, nil, bClass, link);
DoToField(\pfDocument, (Ptr) &fDocument,
bObject, link);
DoToField(\pfShapeList, (Ptr) &fShapeList,
bObject, link);
inherited::Fields(DoToField, link); }
#endif
TEditor *gEditor;
int main()
{InitToolBox();
if (ValidateConfiguration(&gConfiguration))
{
InitUMacApp(8); InitUPrinting();
InitUTEView();
gEditor = new TEditor(kFileType);
FailNIL(gEditor);
gEditor->Run(); }
else StdAlert(phUnsupportedConfiguration);
return 0;}
Listing 3: editor.r
/* editor.r
Rez file for MacTutor C++/MacApp Editor example
J. Langowski March 1991 */
#ifndef __TYPES.R__
#include Types.r
#endif
#ifndef __SYSTYPES.R__
#include SysTypes.r
#endif
#ifndef __MacAppTypes__
#include MacAppTypes.r
#endif
#ifndef __ViewTypes__
#include ViewTypes.r
#endif
#if qDebug
include Debug.rsrc;
#endif
include MacApp.rsrc;
include Printing.rsrc;
include Defaults.rsrc SIZE(-1);
include Defaults.rsrc ALRT(phAboutApp);
include Defaults.rsrc DITL(phAboutApp);
include Defaults.rsrc cmnu(mApple);
include Defaults.rsrc cmnu(mEdit);
include Defaults.rsrc cmnu(mBuzzWords);
include Editor CODE;
include editor.rsrc;
#define kSignature JLMT
#define kDocFileType JL01
#define getInfoString©1991 J.Langowski/MacTutor.
resource cmnu (2) {
2, textMenuProc, allEnabled, enabled, File,
{New, noIcon, N, noMark, plain, 10;
Open , noIcon, O, noMark, plain, 20;
-, noIcon, noKey, noMark, plain, nocommand;
Close, noIcon, noKey, noMark, plain, 31;
Save, noIcon, S, noMark, plain, 30;
Save As , noIcon, noKey, noMark, plain, 32;
Save a Copy In , noIcon, noKey,
noMark, plain, 33;
-, noIcon, noKey, noMark, plain, nocommand;
Page Setup , noIcon, noKey,
noMark, plain, 176;
Print One, noIcon, P, noMark, plain, 177;
Print , noIcon, noKey, noMark, plain, 178;
-, noIcon, noKey, noMark, plain, nocommand;
Quit, noIcon, Q, noMark, plain, 36}
};
resource MBAR (kMBarDisplayed,purgeable)
{{mApple; 2; mEdit;} };
RESOURCE vers (2,
#if qNames
Package Version,
#endif
purgeable) { 0x02, 0x00, beta, 0x06, verUs, 2.0,
MacApp® 2.0, ©Apple Computer, Inc. 1990 };
RESOURCE vers (1,
#if qNames
File Version,
#endif
purgeable) { 0x01, 0x00, beta, 0x05, verUs, Editor,
©JL/MacTutor June 1991 };
resource dbug (kDebugParamsID,
#if qNames
Debug,
#endif
purgeable) { {350, 4, 474, 636}, /* Bounding rect */
1, /* font */ 9, /* font size */
100, /* Number of lines */ 100, /* Width of lines */
true, /* open initially */
Jörgs Debug Window /* Window title */ };