{URecipes2} {Implementation part of URecipes} {Copyright 1984, Apple Computer Inc.} {Note: This is a sample program that demonstrates many of the features of the Text Building Block. Not everything is implemented. The reader of this sample may wish to complete some the unfinished portions or add enhancements of his or her own. This sample has not undergone rigorous testing and there may be some bugs. If you find one, fix it!} CONST tkTeam = 'Apple ToolKit/32 Team'; direcFormat = 3; tabColumn = 12; ingredHeight = 10; phReallyDelRecipe = 4002; {$IFC NOT debugMe} fDrawRects = FALSE; fRecipeTrace = FALSE; {$ENDC} VAR foodTitles: ARRAY [TFoodType] OF STR255; titlesLRect: ARRAY [TFoodType] OF LRect; chapStyles: TArray; chapDfltStyle: TTypeStyle; nameInRecipeStyle: TTypeStyle; ingredStyle: TTypeStyle; cbStyle: TTypeStyle; direcStyle: TTypeStyle; {$IFC debugMe} fRecipeTrace: BOOLEAN; fDrawRects: BOOLEAN; {$ENDC} rHandle: RgnHandle; METHODS OF TRecipe; FUNCTION TRecipe.CREATE(object: TObject; heap: THeap; itsName: TString; itsIngredients: TList; itsDirections: TList): TRecipe; VAR firstIngred: TString; firstPara: TParagraph; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TRecipe(object); IF itsName = NIL THEN itsName := TString.CREATE(NIL, heap, 0); IF itsIngredients = NIL THEN BEGIN itsIngredients := TList.CREATE(NIL, heap, 0); firstIngred := TString.CREATE(NIL, heap, 0); itsIngredients.InsLast(firstIngred); END; IF itsDirections = NIL THEN BEGIN itsDirections := TList.CREATE(NIL, heap, 0); firstPara := TParagraph.CREATE(NIL, heap, 0, direcStyle); itsDirections.InsLast(firstPara); END; WITH SELF DO BEGIN name := itsName; ingredients := itsIngredients; directions := itsDirections; END; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TRecipe.Free; BEGIN {$IFC fTrace}BP(11);{$ENDC} SELF.name.Free; SELF.ingredients.Free; SELF.directions.Free; SUPERSELF.Free; {$IFC fTrace}EP;{$ENDC} END; FUNCTION TRecipe.Clone(heap: THeap): TObject; VAR clName: TString; clIngred: TList; clDirec: TList; clRecipe: TRecipe; BEGIN {$IFC fTrace}BP(11);{$ENDC} clName := TString(SELF.name.Clone(heap)); clIngred := TList(SELF.ingredients.Clone(heap)); clDirec := TList(SELF.directions.Clone(heap)); clRecipe := TRecipe(SUPERSELF.Clone(heap)); WITH clrecipe DO BEGIN name := clName; ingredients := clIngred; directions := clDirec; END; Clone := clRecipe; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE TRecipe.Fields(PROCEDURE Field(nameAndType: S255)); BEGIN SUPERSELF.Fields(Field); Field('name: TString'); Field('ingredients: TList'); Field('directions: TList'); Field(''); END; {$ENDC} FUNCTION TRecipe.MakeIntoImage(itsView: TView): TRecipeImage; {This Function creates a displayable image from a TRecipe object. It takes each field of TRecipe and creates a text image capable of displaying it. The reader may wish modify this routine to implement one of the features not demonstrated in this sample program. That feature is the ability to have text flow from one box to another. There is a boolean field in TRecipeImage called twoColumn that is unused but is intended to be used to indicate whether the directions are to be displayed as one column or two. It would be a simple matter to add the code to create a second text image and set up the link fields in it and the directions text image so that the directions would now appear as two columns. You may also wish to add a command to switch between the two display modes} VAR image: TTextImage; nameImage: TTextImage; direcImage: TTextImage; ingredList: TList; heap: THeap; direcText: TText; imageLRect: LRect; styleSheet: TStyleSheet; format: TParaFormat; FUNCTION CreateImage(imageLRect: LRect; str: TString): TTextImage; VAR image: TTextImage; text: TText; BEGIN text := TText.CREATE(NIL, heap, styleSheet); image := text.DfltTextImage(itsView, imageLRect, FALSE); TEditPara(text.paragraphs.First).ReplTString(0, 0, str, 0, str.size); image.InvalAll; TParaImage(image.imageList.First).InvalLinesWith(0, MAXINT); image.RecomputeImages(actionNone, TRUE); CreateImage := image; END; PROCEDURE InstallImage(obj: TObject); BEGIN ingredList.InsLast(CreateImage(imageLRect, TString(obj))); OffSetLRect(imageLRect, 0, ingredHeight); END; PROCEDURE AddToText(obj: TObject); VAR editPara: TEditPara; BEGIN editPara := TEditPara.CREATE(NIL, heap, TParagraph(obj).size, TParaFormat(styleSheet.formats.At(direcFormat))); editPara.ReplPara(0, 0, TParagraph(obj), 0, TParagraph(obj).size); direcText.paragraphs.InsLast(editPara); END; BEGIN {$IFC fTrace}BP(10);{$ENDC} heap := SELF.Heap; styleSheet := TMyWindow(itsView.panel.window).styleSheet; SetLRect(imageLRect, 30, 10, 400, 35); {Our local procedure CreateImage creates a textImage from the string passed in. It uses the TText routine DfltTextImage to generate all the neccessary data structures. The problem with using this short cut routine, however, is that it uses the first paraFormat in the styleSheet when creating the paragraph. For the recipe name we want to use the second format so we do a little funny stuff here to temporarily make the second format become the first format} format := TParaFormat(styleSheet.formats.First); styleSheet.formats.DelAt(1, FALSE); nameImage := CreateImage(imageLRect, SELF.name); styleSheet.formats.InsFirst(format); ingredList := TList.CREATE(NIL, heap, 0); SetLRect(imageLRect, 50, 40, 400, 40+ingredHeight); SELF.ingredients.Each(InstallImage); direcText := TText.CREATE(NIL, heap, TMyWindow(itsView.panel.window).styleSheet); SELF.directions.Each(AddToText); WITH imageLRect DO SetLRect(imageLRect, 30, bottom+15, 500, bottom+100); direcImage := TTextImage.CREATE(NIL, heap, itsView, imageLRect, direcText, TRUE); direcText.txtImgList.InsLast(direcImage); {Test for two colmns goes here. NOTE: Do NOT add the text image of the seconmd column to direcText.txtImgList. Only "head" textImages are put in this list} direcImage.RecomputeImage(actionNone, TRUE); MakeIntoImage := TRecipeImage.CREATE(NIL, heap, itsView, nameImage, ingredList, direcImage); {$IFC fTrace}EP;{$ENDC} END; END;{METHODS OF TRecipe} METHODS OF TRecipeImage; FUNCTION TRecipeImage.CREATE(object: TObject; heap: THeap; itsView: TView; nameImage: TTextImage; ingredList: TList; direcImage: TTextImage): TRecipeImage; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TRecipeImage(TImage.CREATE(object, heap, itsView.extentLRect, itsView)); WITH SELF DO BEGIN name := nameImage; ingredients := ingredList; directions := direcImage; changed := FALSE; END; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TRecipeImage.Free; PROCEDURE FreeText(obj: TObject); BEGIN TTextImage(obj).text.Free; END; BEGIN {$IFC fTrace}BP(11);{$ENDC} SELF.name.text.Free; SELF.ingredients.Each(FreeText); SELF.ingredients.FreeObject; SELF.directions.text.Free; SUPERSELF.Free; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE TRecipeImage.Fields(PROCEDURE Field(nameAndType: S255)); BEGIN SUPERSELF.Fields(Field); Field('name: TTextImage'); Field('ingredients: TList'); Field('twoColumn: BOOLEAN'); Field('directions: TTextImage'); Field('changed: BOOLEAN'); Field(''); END; {$ENDC} FUNCTION TRecipeImage.CursorAt(mouseLPt: LPoint): TCursorNumber; VAR index: LONGINT; BEGIN {$IFC fTrace}BP(10);{$ENDC} IF SELF.ImageAt(mouseLPt, index) <> NIL THEN CursorAt := textCursor ELSE CursorAt := SUPERSELF.CursorAt(mouseLPt); {$IFC fTrace}EP;{$ENDC} END; FUNCTION TRecipeImage.DismantleImage: TRecipe; VAR nameStr: TString; direcList: TList; ingredList: TList; recipe: TRecipe; FUNCTION CreateString(textImage: TTextImage): TString; VAR para: TEditPara; str: TString; BEGIN para := TEditPara(textImage.text.paragraphs.First); str := TString.CREATE(NIL, SELF.Heap, para.size); str.InsManyAt(1, para, 1, para.size); CreateString := str; END; PROCEDURE InstallString(obj: TObject); BEGIN ingredList.InsLast(CreateString(TTextImage(obj))); END; PROCEDURE AddParagraph(obj: TObject); VAR paragraph: TParagraph; editPara: TEditPara; BEGIN editPara := TEditPara(obj); paragraph := TParagraph.CREATE(NIL, SELF.heap, editPara.size, editPara.format.dfltTStyle); paragraph.ReplPara(0, 0, editPara, 0, editPara.size); direcList.InsLast(paragraph); END; BEGIN {$IFC fTrace}BP(10);{$ENDC} nameStr := CreateString(SELF.name); ingredList := TList.CREATE(NIL, SELF.Heap, 0); SELF.ingredients.Each(InstallString); direcList := TList.CREATE(NIL, SELF.Heap, 0); SELF.directions.text.paragraphs.Each(AddParagraph); recipe := TRecipe.CREATE(NIL, SELF.Heap, nameStr, ingredList, direcList); DismantleImage := recipe; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TRecipeImage.Draw; VAR r: LRect; i: INTEGER; x: LONGINT; y: LONGINT; PROCEDURE DrawEach(obj: TObject); BEGIN IF fDrawRects THEN BEGIN PenSize(1,1); FrameLRect(TTextImage(obj).extentLRect); END; SetQDTypeStyle(ingredStyle); WITH TTextImage(obj).extentLRect DO {$H-} BEGIN MoveToL(left - 6, (top+bottom) DIV 2 + 2); DrawChar('¥'); END; TTextImage(obj).Draw; END; BEGIN {$IFC fTrace}BP(10);{$ENDC} PenNormal; SELF.name.Draw; SELF.ingredients.Each(DrawEach); {draw dashed line between ingredients and directions} r := TTextImage(SELF.ingredients.Last).extentLRect; x := 25; y := r.bottom + 5; PenSize(2,2); FOR i := 1 TO 11 DO BEGIN MoveToL(x, y); LineToL(x + 30, y); x := x + 45; END; IF fDrawRects THEN BEGIN PenSize(1,1); FrameLRect(SELF.directions.extentLRect); END; SELF.directions.Draw; {$IFC fTrace}EP;{$ENDC} END; FUNCTION TRecipeImage.ImageAt(mouseLPt: LPoint; VAR ingredIndex: LONGINT): TTextImage; VAR hitTxtImage: TTextImage; s: TListScanner; BEGIN {$IFC fTrace}BP(11);{$ENDC} hitTxtImage := NIL; ingredIndex := 0; IF LPtInLRect(mouseLPt, SELF.name.extentLRect) THEN hitTxtImage := SELF.name ELSE IF LPtInLRect(mouseLPt, SELF.directions.extentLRect) THEN hitTxtImage := SELF.directions ELSE BEGIN s := SELF.ingredients.Scanner; WHILE s.Scan(hitTxtImage) DO IF LPtInLRect(mouseLPt, hitTxtImage.extentLRect) THEN BEGIN ingredIndex := s.position; s.Done; END; END; ImageAt := hitTxtImage; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TRecipeImage.MousePress(mouseLPt: LPoint); VAR hitTxtImage: TTextImage; noSelection: TSelection; panelSelection: TSelection; needNewSel: BOOLEAN; ingredIndex: LONGINT; heap: THeap; textImage: TTextImage; {$IFC debugMe} str: S255; {$ENDC} BEGIN {$IFC fTrace}BP(11);{$ENDC} heap := SELF.Heap; panelSelection := SELF.view.panel.selection; {The user has clicked the mouse in the recipe panel. The first thing we need to do is see which textImage the mouse was over, if any} hitTxtImage := SELF.ImageAt(mouseLPt, ingredIndex); {$IFC debugMe} IF fRecipeTrace THEN BEGIN LIntToHex(ORD(hitTxtImage), @str); WriteLn('** In TRecipeImage.MousePress, hitTxtImg = ', str); END; {$ENDC} {If the mouse was not over any textImage we simply want to Deselect} IF hitTxtImage = NIL THEN {Calling BeginSelection automatically dehighlights and frees the current selection in all panels and replaces them with a NoSelection. Note that in this application we override this behaviour for the two left panels (see TBookSelection.DeSelect and TChapSelection.DeSelect)} SELF.view.panel.BeginSelection ELSE BEGIN {The mouse was over a textImage so we want to create a cover selection specific to which textImage was clicked upon. We will then call the textImage's MousePress which will establish a textSelection in our cover selection's coSelection field. First, however we need to see if we have already done the above on a previous click, since if we click again on the same textImage there is no need to CREATE a whole new cover selection. We discover we need to CREATE a new cover selection if there is no current coSelection at all or, if there is one, it's textImage is not the one that we just now clicked on} needNewSel := FALSE; IF panelSelection.coSelection = NIL THEN needNewSel := TRUE ELSE IF TTextSelection(panelSelection.coSelection).textImage <> hitTxtImage THEN needNewSel := TRUE; IF needNewSel THEN BEGIN SELF.view.panel.BeginSelection; IF hitTxtImage = SELF.name THEN BEGIN panelSelection := panelSelection.FreedAndReplacedBy( TNameSelection.CREATE(NIL, heap, SELF.view, mouseLPt)); {Clicking on the recipe name requires special treatment because we want any editing of the title to be reflected in the title displayed in the chapter panel. Thus we call CreateSecondTxtImg which creates all the necessary data structures} textImage := TRecipeView(SELF.view).CreateSecondTxtImg; END ELSE IF hitTxtImage = SELF.directions THEN panelSelection := panelSelection.FreedAndReplacedBy( TDirecSelection.CREATE(NIL, heap, SELF.view, mouseLPt)) ELSE panelSelection := panelSelection.FreedAndReplacedBy( TIngredSelection.CREATE(NIL, heap, SELF.view, mouseLPt, SELF.ingredients, ingredIndex)); {When we CREATE a cover selection, we need to set its coSelection field to NoSelection beacause MousePress below will try to Free and replace it with a textSelection} noSelection := SELF.view.NoSelection; panelSelection.coSelection := noSelection; END ELSE IF hitTxtImage = SELF.name THEN {The user has clicked a second or greater time on the name field in the recipe. Normally all selections are automatically turned off by the Text Building block in textImage.MousePress which would turn off the insertion point in the chapter panel, but it cannot automatically turn off selections in other panels if the selection is a coSelection of the panel selection, so we must do it ourselves here} BEGIN panelSelection := TMyWindow(SELF.view.panel.window).chapterPanel.selection.coSelection; TMyWindow(SELF.view.panel.window).chapterPanel.Highlight(panelSelection, hOnToOff); END; hitTxtImage.MousePress(mouseLPt); END; {Until we can think of something better, always set changed flag if the user clicks here} SELF.changed := TRUE; {$IFC fTrace}EP;{$ENDC} END; BEGIN {$IFC debugMe} fDrawRects := FALSE; {$ENDC} END; METHODS OF TCookBookView; FUNCTION TCookBookView.CREATE(object: TObject; heap: THeap; itsPanel: TPanel; itsExtent: LRect): TCookBookView; VAR aList: TList; food: TFoodType; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TCookBookView(itsPanel.NewView(object, itsExtent, NIL, stdMargins, FALSE)); FOR food := mainCourse TO dessert DO BEGIN aList := TList.CREATE(NIL, heap, 0); SELF.headings[food] := aList; END; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE TCookBookView.Fields(PROCEDURE Field(nameAndType: S255)); BEGIN SUPERSELF.Fields(Field); Field('headings: ARRAY [1..5] OF TList'); Field(''); END; {$ENDC} PROCEDURE TCookBookView.Draw; VAR food: TFoodType; BEGIN {$IFC fTrace}BP(10);{$ENDC} PenNormal; PenSize(1,1); SetQDTypeStyle(cbStyle); FOR food := mainCourse TO dessert DO BEGIN MoveToL(titlesLRect[food].left + 10, titlesLRect[food].top + 10); DrawString(foodTitles[food]); END; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TCookBookView.MousePress(mouseLPt: LPoint); VAR food: TFoodType; bookSel: TBookSelection; chapView: TChapterView; BEGIN {$IFC fTrace}BP(11);{$ENDC} SELF.panel.BeginSelection; bookSel := TBookSelection(SELF.panel.selection); chapView := TChapterView(TMyWindow(SELF.panel.window).chapterPanel.view); titlesLRect[noFood] := SELF.extentLrect; food := mainCourse; WHILE NOT LPtInLRect(mouseLPt, titlesLRect[food]) DO food := SUCC(food); IF bookSel.currFood <> food THEN BEGIN SELF.panel.Highlight(bookSel, hOnToOff); bookSel.currFood := food; SELF.panel.Highlight(bookSel, hOffToOn); chapView.ChangeFood(food); END; {$IFC fTrace}EP;{$ENDC} END; {Creation Block} BEGIN {SHOULD READ FROM PHRASE FILE!!!} foodTitles[mainCourse] := 'Main Courses'; foodTitles[soup] := 'Soups'; foodTitles[appetizer] := 'Appetizers'; foodTitles[sandwich] := 'Sandwiches'; foodTitles[dessert] := 'Desserts'; SetLRect(titlesLRect[mainCourse], 10, 5, 170, 20); SetLRect(titlesLRect[soup], 10, 20, 170, 35); SetLRect(titlesLRect[appetizer], 10, 35, 170, 50); SetLRect(titlesLRect[sandwich], 10, 50, 170, 65); SetLRect(titlesLRect[dessert], 10, 65, 170, 80); END; METHODS OF TChapterView; FUNCTION TChapterView.CREATE(object: TObject; heap: THeap; itsPanel: TPanel; itsExtent: LRect): TChapterView; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TChapterView(itsPanel.NewView(object, itsExtent, NIL, stdMargins, FALSE)); WITH SELF DO BEGIN recipes := NIL; editImage := NIL; foodType := noFood; END; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE TChapterView.Fields(PROCEDURE Field(nameAndType: S255)); BEGIN SUPERSELF.Fields(Field); Field('foodType: INTEGER'); Field('recipes: TList'); Field('editImage: TTextImage'); Field(''); END; {$ENDC} PROCEDURE TChapterView.ChangeFood(food: TFoodTypes); VAR recipeView: TRecipeView; BEGIN {$IFC fTrace}BP(10);{$ENDC} {This forces the last commmand to Commit so we don't have to hang on to old data structures just in case the user wants to Undo. (Hence, of course, the user cannot Undo after bringing up a new chapter)} SELF.panel.window.CommitLast; SELF.panel.window.SaveCommand(NIL); recipeView := TRecipeView(TMyWindow(SELF.panel.window).recipePanel.view); recipeView.FreeRecipeImage; SELF.foodType := food; IF food = noFood THEN SELF.recipes := NIL ELSE SELF.recipes := TCookBookView(TMyWindow(SELF.panel.window).cookBookPanel.view).headings[food]; TChapSelection(SELF.panel.selection).recipeIndex := 0; SELF.panel.Invalidate; {$IFC fTrace}EP;{$ENDC} END; FUNCTION TChapterView.CursorAt(mouseLPt: LPoint): TCursorNumber; BEGIN {$IFC fTrace}BP(10);{$ENDC} IF SELF.editImage <> NIL THEN IF LPtInLRect(mouseLPt, SELF.editImage.extentLRect) THEN CursorAt := textCursor ELSE CursorAt := arrowCursor ELSE CursorAt := SUPERSELF.CursorAt(mouseLPt); {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TChapterView.Draw; VAR r: LRect; x: LONGINT; y: LONGINT; index: INTEGER; theIndex: INTEGER; PROCEDURE DrawIt(obj: TObject); VAR name: TString; BEGIN index := index + 1; IF index = theIndex THEN BEGIN TTextImage(TRecipeView(TMyWindow( SELF.panel.window).recipePanel.view).recipeImage.name.text.txtImgList.Last).Draw; SetQDTypeStyle(chapDfltStyle); END ELSE BEGIN MoveToL(x, y); name := TRecipe(obj).name; name.Draw(1, name.size); y := y + 14; END; END; BEGIN {$IFC fTrace}BP(10);{$ENDC} PenNormal; SetQDTypeStyle(chapDfltStyle); IF SELF.panel.selection.coSelection <> NIL THEN theIndex := TChapSelection(SELF.panel.selection).recipeIndex ELSE theIndex := 0; x := 10; y := 14; index := 0; IF SELF.recipes <> NIL THEN IF SELF.recipes.size > 0 THEN SELF.recipes.Each(DrawIt) ELSE BEGIN MoveToL(10, 14); TextFace([italic]); DrawString('no recipes'); END; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TChapterView.MousePress(mouseLPt: LPoint); VAR recipe: TRecipe; currRecipe: TRecipe; recipeView: TRecipeView; chapSelection: TChapSelection; textImage: TTextImage; index: INTEGER; selection: TSelection; noSelection: TSelection; FUNCTION FindRecipe(lPt: LPoint): TRecipe; VAR stillLooking: BOOLEAN; x: LONGINT; BEGIN x := 14; index := 0; stillLooking := SELF.recipes.size > 0; WHILE stillLooking DO BEGIN index := index + 1; IF lPt.v < x THEN stillLooking := FALSE ELSE IF index >= SELF.recipes.size THEN BEGIN stillLooking := FALSE; index := 0; END ELSE x := x + 14; END; IF index = 0 THEN FindRecipe := NIL ELSE FindRecipe := TRecipe(SELF.recipes.At(index)); END; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF SELF.recipes <> NIL THEN BEGIN recipe := FindRecipe(mouseLPt); recipeView := TRecipeView(TMyWindow(SELF.panel.window).recipePanel.view); chapSelection := TChapSelection(SELF.panel.selection); IF chapSelection.recipeIndex <> 0 THEN currRecipe := TRecipe(SELF.recipes.At(chapSelection.recipeIndex)) ELSE currRecipe := NIL; IF currRecipe <> recipe THEN BEGIN SELF.panel.BeginSelection; recipeView.ChangeRecipe(recipe); chapSelection.panel.Highlight(chapSelection, hOnToOff); chapSelection.recipeindex := index; chapSelection.panel.Highlight(chapSelection, hOffToOn); SELF.editImage := NIL; END ELSE IF recipe <> NIL THEN BEGIN {The user has clicked a second time on the same recipe name, which means she wants to edit it. Thus we need to set up a textImage in this panel that points to the same text object as the name in the recipe panel. When we do this, editing of the recipe name will be reflected in both panels simultaneously. CreateSecondTxtImg also unhighlights the chapterSelection and installs a NoSelection in its coSelection field. Notice that CreateSecondTxtImg is also called when the user clicks on the recipe name in the recipe panel (See TRecipeImage.MousePress)} IF chapSelection.coSelection = NIL THEN BEGIN SELF.panel.BeginSelection; textImage := recipeView.CreateSecondTxtImg; selection := recipeView.panel.selection.FreedAndReplacedBy( TNameSelection.CREATE(NIL, SELF.Heap, recipeView, mouseLPt)); noSelection := recipeView.NoSelection; selection.coSelection := noSelection; textImage.MousePress(mouseLPt); END ELSE BEGIN {We must explicitly turn off the highlighting of the name in the recipe panel because the text building block is unable to do this automatically when coSelections are used} selection := recipeView.panel.selection.coSelection; recipeView.panel.Highlight(selection, hOnToOff); SELF.editImage.MousePress(mouseLPt); END END ELSE SELF.editImage := NIL; END; {$IFC fTrace}EP;{$ENDC} END; FUNCTION TChapterView.NewRecipe(recipeName: STR255): TRecipe; VAR itsName: TString; firstIngred: TString; itsIngredients: TList; itsDirections: TList; firstPara: TParagraph; BEGIN {$IFC fTrace}BP(11);{$ENDC} itsName := TString.CREATE(NIL, SELF.Heap, Length(recipeName)); itsName.InsPStrAt(1, @recipeName); NewRecipe := TRecipe.CREATE(NIL, SELF.Heap, itsName, NIL, NIL); {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TRecipeView; FUNCTION TRecipeView.CREATE(object: TObject; heap: THeap; itsPanel: TPanel; itsExtent: LRect): TRecipeView; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TRecipeView(itsPanel.NewView(object, itsExtent, TPrintManager.CREATE(NIL, heap), stdMargins, FALSE)); WITH SELF DO BEGIN recipeImage := NIL; {The Reader may wish to use this field to disallow editing of recipes} canEdit := TRUE; END; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE TRecipeView.Fields(PROCEDURE Field(nameAndType: S255)); BEGIN SUPERSELF.Fields(Field); Field('recipeImage: TRecipeImage'); Field('canEdit: BOOLEAN'); Field(''); END; {$ENDC} PROCEDURE TRecipeView.ChangeRecipe(recipe: TRecipe); VAR recipeImage: TRecipeImage; BEGIN {$IFC fTrace}BP(10);{$ENDC} {This forces the last commmand to Commit so we don't have to hang on to old data structures just in case the user wants to Undo. (Hence, of course, the user cannot Undo after bringing up a new recipe)} SELF.panel.window.CommitLast; SELF.panel.window.SaveCommand(NIL); SELF.FreeRecipeImage; IF recipe <> NIL THEN BEGIN recipeImage := recipe.MakeIntoImage(SELF); SELF.recipeImage := recipeImage; END; {$IFC fTrace}EP;{$ENDC} END; FUNCTION TRecipeView.CreateSecondTxtImg: TTextImage; {This routine is called when the user clicks on the recipe name either the first time in the recipe panel (this view's panel) or the second time on the name in the chapterPanel. (See TRecipeImage. MousePress and TChapterView.Mousepress.) Here we CREATE a TChapTextImage of the name in the chapter panel. (See TChapTextImage.FilterAndDo for discussion of why we sub-classed TTextImage.) Also in this routine we unhighlight the current chapter panel selection and install a NoSelection in the coSelection field. The calling routine will next call textImage.MousePress to install the textSelection in the coSelection. Finally, in this routine, we call RecomputeImages which will compute the paraImage for the chapter panel} VAR name: TTextImage; textImage: TTextImage; chapView: TChapterView; r: LRect; fInfo: FontInfo; recipeIndex: LONGINT; selection: TSelection; BEGIN {$IFC fTrace}BP(11);{$ENDC} chapView := TChapterView(TMyWindow(SELF.panel.window).chapterPanel.view); name := SELF.recipeImage.name; recipeIndex := TChapSelection(chapView.panel.selection).recipeIndex; SetQDTypeSTyle(chapDfltStyle); GetFontInfo(fInfo); SetLRect(r, 10, recipeIndex * 14 - fInfo.ascent, 150, (recipeIndex + 1) * 14 - fInfo.ascent); textImage := TChapTextImage.CREATE(NIL, SELF.Heap, chapView, r, name.text, FALSE); name.text.txtImgList.InsLast(textImage); textImage.RecomputeImage(actionNone, TRUE); chapView.panel.Highlight(chapView.panel.selection, hOnToOff); selection := chapView.NoSelection; chapView.panel.selection.coSelection := selection; textImage.RecomputeImages(actionNone, TRUE); SELF.recipeImage.changed := TRUE; chapView.editImage := textImage; CreateSecondTxtImg := textImage; {$IFC fTrace}EP;{$ENDC} END; FUNCTION TRecipeView.CursorAt(mouseLPt: LPoint): TCursorNumber; BEGIN {$IFC fTrace}BP(10);{$ENDC} IF SELF.recipeImage <> NIL THEN CursorAt := SELF.recipeImage.CursorAt(mouseLPt) ELSE CursorAt := SUPERSELF.CursorAt(mouseLPt); {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TRecipeView.Draw; BEGIN {$IFC fTrace}BP(10);{$ENDC} IF SELF.recipeImage <> NIL THEN SELF.recipeImage.Draw; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TRecipeView.FreeRecipeImage; VAR chapPanel: TPanel; chapView: TChapterView; chapSelection: TChapSelection; recipe: TRecipe; BEGIN {$IFC fTrace}BP(10);{$ENDC} IF SELF.recipeImage <> NIL THEN BEGIN IF SELF.recipeImage.changed THEN BEGIN recipe := SELF.recipeImage.DismantleImage; chapPanel := TMyWindow(SELF.panel.window).chapterPanel; chapView := TChapterView(chapPanel.view); chapSelection := TChapSelection(chapPanel.selection); chapView.recipes.PutAt(chapSelection.recipeIndex, recipe, TRUE); END; SELF.recipeImage.Free; SELF.recipeImage := NIL; END; SELF.panel.Invalidate; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TRecipeView.MousePress(mouseLPt: LPoint); BEGIN {$IFC fTrace}BP(11);{$ENDC} IF SELF.canEdit AND (SELF.recipeImage <> NIL) THEN SELF.recipeImage.MousePress(mouseLPt) ELSE BEGIN {Alert about can't edit; use menu to enable; (based on recipeImage NIL or not} {*} END; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TRecipeView.ScrollStuff(ingredIndex: LONGINT; moveDown: BOOLEAN); {This routine is called when an ingredient is inserted or deleted. It updates the appropriate text image's extentLRects and then calls QuickDraw's ScrollRect to Scroll the correct portion of the view up or down. Notice we use ScrollRect here instead of the normal paradigm of invalidating a portion of the screen and waiting for TWindow.Update to tell us to Draw. We're doing it here for speed; scrolling is faster than redrawing, but we have to know exactly what's going on on the screen lest we scroll something that should not have moved} VAR recipeImage: TRecipeImage; delta: INTEGER; scanIndex: LONGINT; s: TListScanner; textImage: TTextImage; deltaLPt: LPoint; lr: LRect; r: Rect; top: LONGINT; PROCEDURE ScrollIt; BEGIN thePad.LRectToRect(lr, r); ScrollRect(r, 0, delta, rHandle); thePad.InvalRect(rHandle^^.rgnBBox); END; BEGIN {$IFC fTrace}BP(11);{$ENDC} recipeImage := SELF.recipeImage; IF moveDown THEN BEGIN scanIndex := ingredIndex; delta := ingredHeight; END ELSE BEGIN scanIndex := ingredIndex - 1; delta := -ingredHeight; END; SetLPt(deltaLpt, 0, delta); s := recipeImage.ingredients.ScannerFrom(scanIndex, scanForward); WHILE s.Scan(textImage) DO textImage.OffSetBy(deltaLPt); recipeImage.directions.OffsetBy(deltaLPt); IF ingredIndex <= recipeImage.ingredients.size THEN top := TTextImage(recipeImage.ingredients.At(ingredIndex)).extentLRect.top ELSE top := TTextImage(recipeImage.ingredients.Last).extentLRect.bottom; SetLRect(lr, 0, top, 500, SELF.extentLRect.bottom); SELF.panel.OnAllPadsDo(ScrollIt); {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TRecipeClipView; FUNCTION TRecipeClipView.CREATE(object: TObject; heap: THeap; itsPanel: TPanel; itsExtent: LRect; itsFoodType: TFoodType; itsRecipe: TRecipe): TRecipeClipView; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TRecipeClipView(itsPanel.NewView(object, itsExtent, NIL, stdMargins, FALSE)); WITH SELF DO BEGIN foodType := itsFoodType; recipe := itsRecipe; END; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE TRecipeClipView.Fields(PROCEDURE Field(nameAndType: S255)); BEGIN SUPERSELF.Fields(Field); Field('foodType: INTEGER'); Field('recipe: TRecipe'); Field(''); END; {$ENDC} PROCEDURE TRecipeClipView.Draw; BEGIN {$IFC fTrace}BP(10);{$ENDC} IF SELF.recipe <> NIL THEN BEGIN SetQDTypeStyle(nameInRecipeStyle); MoveToL(10, 10); SELF.recipe.name.Draw(1, SELF.recipe.name.size); END; {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TClipSelection; FUNCTION TClipSelection.CREATE(object: TObject; heap: THeap; itsView: TView): TClipSelection; BEGIN {$IFC fTrace}BP(9);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TClipSelection(TSelection.CREATE(object, heap, itsView, somethingKind, zeroLPt)); {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TBookSelection; FUNCTION TBookSelection.CREATE(object: TObject; heap: THeap; itsView: TView; itsAnchorLPt: LPoint; whatFood: TFoodTypes): TBookSelection; BEGIN {$IFC fTrace}BP(9);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TBookSelection(TSelection.CREATE(object, heap, itsView, somethingKind, itsAnchorLPt)); SELF.currFood := whatFood; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE TBookSelection.Fields(PROCEDURE Field(nameAndType: S255)); BEGIN SUPERSELF.Fields(Field); Field('currFood: INTEGER'); Field(''); END; {$ENDC} PROCEDURE TBookSelection.Deselect; VAR selection: TSelection; BEGIN {$IFC fTrace}BP(7);{$ENDC} {When a selection is created, panel.BeginSelection is called which calls selection.Deselect for each panel in the window. The default of Deselect is to unHighlight and replace SELF with a noSelection. But in our case we don't want to do either of those when someone is editing in the recipe panel so we override deselect to do nothing} {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TBookSelection.Highlight(highTransit: THighTransit); BEGIN {$IFC fTrace}BP(10);{$ENDC} SetPenState(highPen[highTransit]); IF (SELF.currFood <> noFood) AND TMyWindow(SELF.window).okayToHilite THEN CASE highTransit OF hOnToDim: BEGIN InvrtLRect(titlesLRect[SELF.currFood]); {this is the same as hOnToOff} SELF.Highlight(hOffToDim); {this is NOT the same as just doing FrameLRect(titlesLRect[SELF.currFood]); because the pen state is different} END; hDimToOn: BEGIN SELF.Highlight(hDimToOff); InvrtLRect(titlesLRect[SELF.currFood]); {this is the same as hOnToOff} END; hOffToDim, hDimToOff: BEGIN FrameLRect(titlesLRect[SELF.currFood]); END; OTHERWISE PaintLRect(titlesLRect[SELF.currFood]); END; {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TTextCoverSelection; FUNCTION TTextCoverSelection.CREATE(object: TObject; heap: THeap; itsView: TView; itsAnchorLPt: LPoint): TTextCoverSelection; BEGIN {$IFC fTrace}BP(9);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TTextCoverSelection(TSelection.CREATE(object, heap, itsView, somethingKind, itsAnchorLPt)); {$IFC fTrace}EP;{$ENDC} END; FUNCTION TTextCoverSelection.CanDoCommand(cmdNumber: TCmdNumber; VAR checkIt: BOOLEAN): BOOLEAN; BEGIN {$IFC fTrace}BP(10);{$ENDC} CASE cmdNumber OF uModern, uClassic, u20Pitch, u15Pitch, u12Pitch, u10Pitch, u12Point, u14Point, u18Point, u24Point: BEGIN CanDoCommand := FALSE; END; uPlain, uBold, uItalic, uUnderline, uShadow, uOutline: BEGIN CanDoCommand := FALSE; END; uPaste: BEGIN CanDoCommand := FALSE; END; OTHERWISE CanDoCommand := SUPERSELF.CanDoCommand(cmdNumber, checkIt); END; {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TChapSelection; FUNCTION TChapSelection.CREATE(object: TObject; heap: THeap; itsView: TView; itsAnchorLPt: LPoint): TChapSelection; BEGIN {$IFC fTrace}BP(9);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TChapSelection(TSelection.CREATE(object, heap, itsView, somethingKind, itsAnchorLPt)); WITH SELF DO BEGIN recipeIndex := 0; END; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TChapSelection.Free; BEGIN {$IFC fTrace}BP(9);{$ENDC} IF SELF.coSelection <> NIL THEN TChapTextImage(TTextSelection(SELF.coSelection).textImage).ChangeRefCountBy(-1); SUPERSELF.Free; {$IFC fTrace}EP;{$ENDC} END; FUNCTION TChapSelection.Clone(heap: THeap): TObject; BEGIN {$IFC fTrace}BP(9);{$ENDC} Clone := SUPERSELF.Clone(heap); IF SELF.coSelection <> NIL THEN TChapTextImage(TTextSelection(SELF.coSelection).textImage).ChangeRefCountBy(1); {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE TChapSelection.Fields(PROCEDURE Field(nameAndType: S255)); BEGIN SUPERSELF.Fields(Field); Field('recipeIndex: INTEGER'); Field(''); END; {$ENDC} PROCEDURE TChapSelection.AddRecipe(recipeName: STR255); VAR chapView: TChapterView; str: STR255; newRecipe: TRecipe; s: TListScanner; recipe: TRecipe; inserted: BOOLEAN; recipes: TList; BEGIN {$IFC fTrace}BP(10);{$ENDC} chapView := TChapterView(SELF.view); recipes := chapView.recipes; newRecipe := chapView.NewRecipe(recipeName); TRecipeView(TMyWindow(SELF.window).recipePanel.view).ChangeRecipe(newRecipe); s := recipes.Scanner; inserted := FALSE; WHILE s.Scan(recipe) DO BEGIN recipe.name.ToPStr(@str); IF recipeName < str THEN BEGIN inserted := TRUE; SELF.recipeIndex := s.position; s.Skip(-1); s.Append(newRecipe); s.Done; END; END; IF NOT inserted THEN BEGIN recipes.InsLast(newRecipe); SELF.recipeIndex := recipes.size; END; chapView.panel.Invalidate; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TChapSelection.Deselect; VAR selection: TSelection; BEGIN {$IFC fTrace}BP(7);{$ENDC} {When a selection is created, panel.BeginSelection is called which calls selection.Deselect for each panel in the window. The default of Deselect is to unHighlight and replace SELF with a NoSelection. But in our case we want the recipe name to remain highlighted when someone is editing in the recipe panel so we override Deselect so that it does not Free the selection. However, if someone was editing the recipe name, then we want to, unhighlight whatever textSelections existed and rehighlight the entire recipe name} IF SELF.coSelection <> NIL THEN BEGIN SELF.panel.Highlight(SELF.coSelection, hOnToOff); TChapTextImage(TTextSelection(SELF.coSelection).textImage).ChangeRefCountBy(-1); SELF.coSelection.Free; SELF.coSelection := NIL; SELF.panel.Highlight(SELF, hOffToOn); END; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TChapSelection.Highlight(highTransit: THighTransit); VAR r: LRect; BEGIN {$IFC fTrace}BP(10);{$ENDC} SetPenState(highPen[highTransit]); IF SELF.coSelection = NIL THEN BEGIN IF (SELF.recipeIndex <> 0) AND TMyWindow(SELF.window).okayToHilite THEN BEGIN SetLRect(r, 5, SELF.recipeIndex*14 - 10, 150, SELF.recipeIndex*14 + 4); CASE highTransit OF hOnToDim: BEGIN InvrtLRect(r); {this is the same as hOnToOff} SELF.Highlight(hOffToDim); {this is NOT the same as just doing FrameLRect(titlesLRect[SELF.currFood]); because the pen state is different} END; hDimToOn: BEGIN SELF.Highlight(hDimToOff); InvrtLRect(r); {this is the same as hOnToOff} END; hOffToDim, hDimToOff: BEGIN FrameLRect(r); END; OTHERWISE PaintLRect(r); END; END; END ELSE SELF.coSelection.Highlight(highTransit); {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TChapSelection.KeyReturn; BEGIN {$IFC fTrace}BP(10);{$ENDC} {We don't want to allow multiple paragraphs when someone is editing the name, so we override KeyReturn to do nothing} {$IFC fTrace}EP;{$ENDC} END; FUNCTION TChapSelection.NewCommand(cmdNumber: TCmdNumber): TCommand; VAR heap: THeap; inputString: S255; BEGIN {$IFC fTrace}BP(10);{$ENDC} heap := SELF.heap; NewCommand := NIL; CASE cmdNumber OF {Adding a recipe is NOT undoable. We do not create a command here, although AddRecipe indirectly Commits the last command via TRecipeView.ChangeRecipe. The user can simply delete the recipe if she didn't really want it} uAddRecipe: BEGIN TMyWindow(SELF.window).inputFrame.GetContents(inputString); IF inputString <> '' THEN SELF.AddRecipe(inputString); TMyWindow(SELF.window).recipePanel.selection.MarkChanged; END; OTHERWISE NewCommand := SUPERSELF.NewCommand(cmdNumber); END; {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TNameSelection; FUNCTION TNameSelection.CREATE(object: TObject; heap: THeap; itsView: TView; itsAnchorLPt: LPoint): TNameSelection; BEGIN {$IFC fTrace}BP(9);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TNameSelection(TTextCoverSelection.CREATE(object, heap, itsView, itsAnchorLPt)); {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TNameSelection.KeyReturn; BEGIN {$IFC fTrace}BP(10);{$ENDC} {We don't want to allow multiple paragraphs when someone is editing the name, so we override KeyReturn to do nothing} {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TIngredSelection; FUNCTION TIngredSelection.CREATE(object: TObject; heap: THeap; itsView: TView; itsAnchorLPt: LPoint; itsIngredients: TList; index: INTEGER): TIngredSelection; BEGIN {$IFC fTrace}BP(9);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TIngredSelection(TTextCoverSelection.CREATE(object, heap, itsView, itsAnchorLPt)); WITH SELF DO BEGIN ingredients := itsIngredients; ingredIndex := index; END; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE TIngredSelection.Fields(PROCEDURE Field(nameAndType: S255)); BEGIN SUPERSELF.Fields(Field); Field('ingredients: TList'); Field('ingredIndex: INTEGER'); Field(''); END; {$ENDC} FUNCTION TIngredSelection.CanDoCommand(cmdNumber: TCmdNumber; VAR checkIt: BOOLEAN): BOOLEAN; BEGIN {$IFC fTrace}BP(10);{$ENDC} CASE cmdNumber OF uAddIngred, uInsIngred: BEGIN CanDoCommand := TRUE; END; uDeleteIngred: BEGIN CanDoCommand := SELF.ingredients.size > 1; END; OTHERWISE CanDoCommand := SUPERSELF.CanDoCommand(cmdNumber, checkIt); END; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TIngredSelection.KeyReturn; VAR recipeImage: TRecipeImage; BEGIN {$IFC fTrace}BP(10);{$ENDC} recipeImage := TRecipeView(SELF.view).recipeImage; SELF.coSelection.KeyPause; IF SELF.ingredIndex = recipeImage.ingredients.size THEN SELF.window.PerformCommand(TAddIngredCmd.CREATE(NIL, SELF.Heap, SELF.view, uAddIngredient, SELF.ingredients, SELF.ingredIndex + 1)) ELSE SELF.view.MousePress(TTextImage(SELF.ingredients.At(SELF.ingredIndex+1)).extentLRect.topLeft); {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TIngredSelection.KeyTab(fBackward: BOOLEAN); VAR textRange: TTextRange; textImage: TTextImage; str: S255; selection: TSelection; i: INTEGER; BEGIN {$IFC fTrace}BP(10);{$ENDC} textRange := TTextSelection(SELF.coSelection).textRange; textImage := TTextSelection(SELF.coSelection).textImage; IF textRange.firstLP < tabColumn THEN BEGIN IF tabColumn > textRange.firstPara.size THEN BEGIN str := ''; FOR i := 1 TO tabColumn - textRange.firstPara.size DO str := Concat(str, ' '); textRange.firstPara.ReplPStr(textRange.firstPara.size, 0, @str); TParaImage(textImage.imageList.First).InvalLinesWith(0, MAXINT); textImage.text.RecomputeImages; END; SELF.coSelection.panel.Highlight(SELF.coSelection, hOnToOff); selection := SELF.coSelection.FreedAndReplacedBy(TInsertionPoint.CREATE(NIL, SELF.Heap, SELF.view, textImage, zeroLPt, textRange.firstPara, 1, tabColumn)); selection.panel.Highlight(selection, hOffToOn); END; {$IFC fTrace}EP;{$ENDC} END; FUNCTION TIngredSelection.NewCommand(cmdNumber: TCmdNumber): TCommand; VAR heap: THeap; BEGIN {$IFC fTrace}BP(10);{$ENDC} heap := SELF.heap; CASE cmdNumber OF uAddIngred, uInsIngred: BEGIN NewCommand := TAddIngredCmd.CREATE(NIL, heap, SELF.view, cmdNumber, SELF.ingredients, SELF.ingredIndex + (uInsIngred - cmdNumber)); END; uDeleteIngred: NewCommand := TDelIngredCmd.CREATE(NIL, heap, SELF.view, SELF, SELF.ingredients, SELF.ingredIndex); OTHERWISE NewCommand := SUPERSELF.NewCommand(cmdNumber); END; {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TDirecSelection; FUNCTION TDirecSelection.CREATE(object: TObject; heap: THeap; itsView: TView; itsAnchorLPt: LPoint): TDirecSelection; BEGIN {$IFC fTrace}BP(9);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TDirecSelection(TTextCoverSelection.CREATE(object, heap, itsView, itsAnchorLPt)); {$IFC fTrace}EP;{$ENDC} END; FUNCTION TDirecSelection.CanDoCommand(cmdNumber: TCmdNumber; VAR checkIt: BOOLEAN): BOOLEAN; VAR dummy: BOOLEAN; BEGIN {$IFC fTrace}BP(10);{$ENDC} CASE cmdNumber OF u18Point, u24Point: BEGIN CanDoCommand := FALSE; {don't allow these sizes} END; {for the command numbers uModern, uClassic, u20Pitch, u15Pitch, u12Pitch, u10Pitch, u12Point, u14Point, uPlain, uBold, uItalic, uUnderline, uShadow, uOutline we can just call SELF.coSelection.CanDoCommand because we know that we have a coSelection that is a TTextSelection. TTextSelections always enable typestyle commands and (at no extra charge) check off the appropriate menu items.} uModern, uClassic, u20Pitch, u15Pitch, u12Pitch, u10Pitch, u12Point, u14Point, uPlain, uBold, uItalic, uUnderline, uShadow, uOutline: CanDoCommand := SELF.coSelection.CanDoCommand(cmdNumber, checkIt); uPaste: {allow pasting into the directions} BEGIN CanDoCommand := TRUE; END; OTHERWISE CanDoCommand := SUPERSELF.CanDoCommand(cmdNumber, checkIt); END; {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TChapTextImage; FUNCTION TChapTextImage.CREATE(object: TObject; heap: THeap; itsView: TView; itsLRect: LRect; itsText: TText; isGrowable: BOOLEAN): TChapTextImage; VAR imgList: TList; BEGIN {$IFC fTrace}BP(10);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TChapTextImage(TTextImage.CREATE(object, heap, itsView, itsLRect, itsText, isGrowable)); SELF.refCount := 1; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE TChapTextImage.Fields(PROCEDURE Field(nameAndType: S255)); BEGIN SUPERSELF.Fields(Field); Field('refCount: INTEGER'); Field(''); END; {$ENDC} PROCEDURE TChapTextImage.ChangeRefCountBy(delta: INTEGER); VAR editPara: TEditPara; BEGIN {$IFC fTrace}BP(11);{$ENDC} SELF.refCount := SELF.refCount + delta; IF SELF.refCount <= 0 THEN BEGIN TChapterView(SELF.view).editImage := NIL; editPara := TEditPara(SELF.text.paragraphs.First); editPara.images.DelObject(SELF.imageList.First, FALSE); SELF.text.txtImgList.DelObject(SELF, TRUE); END; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TChapTextImage.FilterAndDo(actualObj: TObject; PROCEDURE DoToObject(filteredObj: TObject)); VAR saveStyles: TArray; saveDflt: TTypeStyle; BEGIN {$IFC fTrace}BP(11);{$ENDC} {This procedure does our own bit of filtering on the paragraph. We're getting a little fancy in that when the recipe name is being edited we want the editing to be reflected in both panels at the same time. However, we want the names in the two panels to appear in a different type style. Since all access to the paragraph's typeStyles goes through FilterAndDo, we override it to stuff in our version of the paragraph's character runs array. Notice that we can only do this because we don't allow typeStyle changes on the recipe title. If we did, this would cause strange ambiguities with conflicting filters. (I don't even want to think about it!)} saveStyles := TParaImage(actualObject).paragraph.typeStyles; saveDflt := TParaImage(actualObject).paragraph.format.dfltTStyle; TParaImage(actualObject).paragraph.typeStyles := chapStyles; TParaImage(actualObject).paragraph.format.dfltTStyle := chapDfltStyle; SUPERSELF.FilterAndDo(actualObject, DoToObject); TParaImage(actualObject).paragraph.typeStyles := saveStyles; TParaImage(actualObject).paragraph.format.dfltTStyle := saveDflt; {$IFC fTrace}EP;{$ENDC} END; FUNCTION TChapTextImage.NewParaImage(itsParagraph: TEditPara; itsLRect: LRect; lineTop: LONGINT; lineLeft: LONGINT): TParaImage; VAR paraImage: TParaImage; BEGIN {$IFC fTrace}BP(10);{$ENDC} paraImage := TChapParaImage.CREATE(NIL, SELF.Heap, SELF.view, itsParagraph, itsLRect, lineTop, lineLeft); paraImage.textImage := SELF; itsParagraph.InsImage(paraImage); NewParaImage := paraImage; {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TChapParaImage; FUNCTION TChapParaImage.CREATE(object: TObject; heap: THeap; itsView: TView; itsParagraph: TEditPara; itsLRect: LRect; lineTop: LONGINT; lineLeft: LONGINT): TChapParaImage; BEGIN {$IFC fTrace}BP(10);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TChapParaImage(TParaImage.CREATE(object, heap, itsView, itsParagraph, itsLRect, lineTop, lineLeft)); {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TChapParaImage.FilterAndDo{(actualObj: TObject; PROCEDURE DoToObject(filteredObj: TObject))}; VAR saveStyles: TArray; BEGIN {$IFC fTrace}BP(11);{$ENDC} {See the comments for TChapTextImage.FilterAndDo} SELF.textImage.FilterAndDo(actualObj, DoToObject); {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TAddIngredCmd; FUNCTION TAddIngredCmd.CREATE(object: TObject; heap: THeap; itsImage: TImage; cmdNumber: TCmdNumber; itsIngredients: TList; itsIndex: INTEGER): TAddIngredCmd; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TAddIngredCmd(TCommand.CREATE(object, heap, cmdNumber, itsImage, FALSE, revealAll)); WITH SELF DO BEGIN ingredients := itsIngredients; ingredIndex := itsIndex; unHiliteBefore[doPhase] := FALSE; unHiliteBefore[redoPhase] := FALSE; END; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE TAddIngredCmd.Fields(PROCEDURE Field(nameAndType: S255)); BEGIN SUPERSELF.Fields(Field); Field('ingredients: TList'); Field('ingredIndex: INTEGER'); Field(''); END; {$ENDC} PROCEDURE TAddIngredCmd.Perform(cmdPhase: TCmdPhase); VAR textImage: TTextImage; text: TText; imageLRect: LRect; BEGIN {$IFC fTrace}BP(10);{$ENDC} CASE cmdPhase OF doPhase, redoPhase: BEGIN text := TText.CREATE(NIL, SELF.Heap, TMyWindow(TView(SELF.image).panel.window).styleSheet); imageLRect := TTextImage(SELF.ingredients.At( Min(SELF.ingredIndex, SELF.ingredients.size))).extentLRect; IF SELF.ingredIndex > SELF.ingredients.size THEN OffSetLRect(imageLRect, 0, ingredHeight); textImage := text.DfltTextImage(TView(SELF.image), imageLRect, FALSE); SELF.ingredients.InsAt(SELF.ingredIndex, textImage); TRecipeView(SELF.image).ScrollStuff(SELF.ingredIndex, TRUE); TRecipeView(SELF.image).MousePress(textImage.extentLRect.topLeft); END; undoPhase: BEGIN END; END; TView(SELF.image).panel.selection.MarkChanged; {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TDelIngredCmd; FUNCTION TDelIngredCmd.CREATE(object: TObject; heap: THeap; itsImage: TImage; itsSelection: TIngredSelection; itsIngredients: TList; itsIndex: INTEGER): TDelIngredCmd; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TDelIngredCmd(TCommand.CREATE(object, heap, uDeleteIngred, itsImage, TRUE, revealAll)); WITH SELF DO BEGIN ingredients := itsIngredients; ingredIndex := itsIndex; ingredSelection := itsSelection; unHiliteBefore[undoPhase] := FALSE; END; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE TDelIngredCmd.Fields(PROCEDURE Field(nameAndType: S255)); BEGIN SUPERSELF.Fields(Field); Field('ingredients: TList'); Field('ingredIndex: INTEGER'); Field('ingredSelection: TIngredSelection'); Field('saveTextImage: TTextImage'); Field(''); END; {$ENDC} PROCEDURE TDelIngredCmd.Commit; BEGIN {$IFC fTrace}BP(10);{$ENDC} IF SELF.saveTextImage <> NIL THEN SELF.saveTextImage.text.Free; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TDelIngredCmd.Perform(cmdPhase: TCmdPhase); VAR textImage: TTextImage; mouseLPt: LPoint; newIndex: LONGINT; selection: TSelection; BEGIN {$IFC fTrace}BP(10);{$ENDC} CASE cmdPhase OF doPhase, redoPhase: BEGIN textImage := TTextImage(SELF.ingredients.At(SELF.ingredIndex)); SELF.saveTextImage := textImage; SELF.ingredients.DelAt(SELF.ingredIndex, FALSE); newIndex := Min(SELF.ingredIndex, SELF.ingredients.size); textImage := TTextImage(SELF.ingredients.At(newIndex)); selection := SELF.ingredSelection.coSelection.FreedAndReplacedBy( TInsertionPoint.CREATE( NIL, SELF.Heap, TView(SELF.image), textImage, zeroLPt, TEditPara(textImage.text.paragraphs.First), 1, 0)); TRecipeView(SELF.image).ScrollStuff(SELF.ingredIndex, FALSE); END; undoPhase: BEGIN SELF.ingredients.InsAt(SELF.ingredIndex, SELF.saveTextImage); TRecipeView(SELF.image).ScrollStuff(SELF.ingredIndex, TRUE); TRecipeView(SELF.image).MousePress(SELF.saveTextImage.extentLRect.topLeft); END; END; TView(SELF.image).panel.selection.MarkChanged; {$IFC fTrace}EP;{$ENDC} END; END; {An exercise for the reader is to make this undoable; then, of course you would not need to confirm the command with the user in TMyWindow.NewCommand.} METHODS OF TDelRecipeCmd; FUNCTION TDelRecipeCmd.CREATE(object: TObject; heap: THeap; itsImage: TImage; itsIndex: INTEGER): TDelRecipeCmd; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TDelRecipeCmd(TCommand.CREATE(object, heap, uDeleteRecipe, itsImage, FALSE, revealAll)); WITH SELF DO BEGIN recipeIndex := itsIndex; END; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE TDelRecipeCmd.Fields(PROCEDURE Field(nameAndType: S255)); BEGIN SUPERSELF.Fields(Field); Field('recipeIndex: INTEGER'); Field(''); END; {$ENDC} PROCEDURE TDelRecipeCmd.Commit; BEGIN {$IFC fTrace}BP(10);{$ENDC} {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TDelRecipeCmd.Perform(cmdPhase: TCmdPhase); BEGIN {$IFC fTrace}BP(10);{$ENDC} CASE cmdPhase OF doPhase: BEGIN END; redoPhase: BEGIN END; undoPhase: BEGIN END; END; {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TCopyRecipeCmd; FUNCTION TCopyRecipeCmd.CREATE(object: TObject; heap: THeap; itsImage: TImage; itsIndex: INTEGER): TCopyRecipeCmd; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TCopyRecipeCmd( TCutCopyCommand.CREATE(object, heap, uCopyRecipe, itsImage, FALSE)); WITH SELF DO BEGIN recipeIndex := itsIndex; END; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE TCopyRecipeCmd.Fields(PROCEDURE Field(nameAndType: S255)); BEGIN SUPERSELF.Fields(Field); Field('recipeIndex: INTEGER'); Field(''); END; {$ENDC} PROCEDURE TCopyRecipeCmd.DoCutCopy(clipSelection: TSelection; deleteOriginal: BOOLEAN; cmdPhase: TCmdPhase); VAR clipHeap: THeap; clipPanel: TPanel; clipView: TRecipeClipView; viewLRect: LRect; clipRecipe: TRecipe; selection: TSelection; recipeImage: TRecipeImage; BEGIN {$IFC fTrace}BP(10);{$ENDC} CASE cmdPhase OF doPhase, redoPhase: BEGIN clipHeap := clipSelection.heap; clipPanel := clipSelection.panel; recipeImage := TRecipeView(TMyWindow(TView( SELF.image).panel.window).recipePanel.view).recipeImage; IF recipeImage.changed THEN clipRecipe := recipeImage.DismantleImage ELSE clipRecipe := TRecipe(TChapterView(SELF.image).recipes.At(SELF.recipeIndex)); clipRecipe := TRecipe(clipRecipe.Clone(clipHeap)); SetLRect(viewLRect, 0, 0, 500, 100); clipView := TRecipeClipView.CREATE(NIL, clipHeap, clipPanel, viewLRect, TChapterView(SELF.image).foodType, clipRecipe); selection := clipSelection.FreedAndReplacedBy(TClipSelection.CREATE(NIL, clipHeap, clipView)); END; undoPhase: BEGIN END; END; TView(SELF.image).panel.selection.MarkChanged; {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TPasteRecipeCmd; FUNCTION TPasteRecipeCmd.CREATE(object: TObject; heap: THeap; itsImage: TImage): TPasteRecipeCmd; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TPasteRecipeCmd(TPasteCommand.CREATE(object, heap, uPasteRecipe, itsImage)); SELF.undoable := FALSE; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TPasteRecipeCmd.DoPaste(clipSelection: TSelection; pic: PicHandle; cmdPhase: TCmdPhase); VAR clipView: TRecipeClipView; recipe: TRecipe; pasteRecipe: TRecipe; myWindow: TMyWindow; recipeView: TRecipeView; chapView: TChapterView; recipes: TList; s: TListScanner; inserted: BOOLEAN; recipeName: S255; str: S255; recipeImage: TRecipeImage; bookSel: TBookSelection; BEGIN {$IFC fTrace}BP(10);{$ENDC} CASE cmdPhase OF doPhase, redoPhase: BEGIN IF InClass(clipSelection.view, TRecipeClipView) THEN BEGIN clipView := TRecipeClipView(clipSelection.view); pasteRecipe := TRecipe(clipView.recipe.Clone(SELF.Heap)); myWindow := TMyWindow(TView(SELF.image).panel.window); myWindow.okayToHilite := TRUE; recipeView := TRecipeView(myWindow.recipePanel.view); chapView := TChapterView(myWindow.chapterPanel.view); recipes := TCookBookView(myWindow.cookBookPanel.view).headings[clipView.foodType]; pasteRecipe.name.ToPStr(@recipeName); s := recipes.Scanner; inserted := FALSE; WHILE s.Scan(recipe) DO BEGIN recipe.name.ToPStr(@str); IF recipeName < str THEN BEGIN inserted := TRUE; TChapSelection(chapView.panel.selection).recipeIndex := s.position; s.Skip(-1); s.Append(pasteRecipe); s.Done; END; END; IF NOT inserted THEN BEGIN recipes.InsLast(pasteRecipe); TChapSelection(myWindow.chapterPanel.selection).recipeIndex := recipes.size; END; chapView.panel.Invalidate; recipeView.panel.selection.Deselect; recipeView.FreeRecipeImage; recipeImage := pasteRecipe.MakeIntoImage(recipeView); recipeView.recipeImage := recipeImage; bookSel := TBookSelection(myWindow.cookBookPanel.selection); bookSel.panel.Highlight(bookSel, hOnToOff); bookSel.currFood := clipView.foodType; chapView.foodType := clipView.foodType; chapView.recipes := recipes; END; END; undoPhase: BEGIN END; END; TView(SELF.image).panel.selection.MarkChanged; {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TMyProcess; FUNCTION TMyProcess.CREATE: TMyProcess; BEGIN {$IFC fTrace}BP(11);{$ENDC} SELF := TMyProcess(TProcess.CREATE(NewObject(mainHeap, THISCLASS), mainHeap)); {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TMyProcess.Commence(phraseVersion: INTEGER); VAR styleChange: TStyleChange; BEGIN {$IFC fTrace}BP(11);{$ENDC} {If you override this, be SURE to call SUPERSELF.Commence!} SUPERSELF.Commence(phraseVersion); {Initialize some of my Globals} chapStyles := TArray.CREATE(NIL, SELF.Heap, 2, SIZEOF(TStyleChange)); MakeTypeStyle(famClassic, size12Pitch, [bold], chapDfltStyle); styleChange.newStyle := chapDfltStyle; styleChange.lp := MAXINT; chapStyles.InsAt(1, @styleChange); styleChange.lp := -1; chapStyles.InsAt(1, @styleChange); MakeTypeStyle(famClassic, size18Point, [], nameInRecipeStyle); MakeTypeStyle(famModern, size15Pitch, [], ingredStyle); MakeTypeStyle(famClassic, size12Point, [shadow], cbStyle); MakeTypeStyle(famModern, size12Pitch, [], direcStyle); rHandle := NewRgn; {$IFC fTrace}EP;{$ENDC} END; FUNCTION TMyProcess.NewDocManager(volumePrefix: TFilePath; openAsTool: BOOLEAN): TDocManager; BEGIN {$IFC fTrace}BP(11);{$ENDC} NewDocManager := TMyDocManager.CREATE(NIL, mainHeap, volumePrefix); {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TMyDocManager; FUNCTION TMyDocManager.CREATE(object: TObject; heap: THeap; itsPathPrefix: TFilePath): TMyDocManager; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TMyDocManager(TDocManager.CREATE(object, heap, itsPathPrefix)); {$IFC fTrace}EP;{$ENDC} END; FUNCTION TMyDocManager.NewWindow(heap: THeap; wmgrID: TWindowID):TWindow; BEGIN {$IFC fTrace}BP(11);{$ENDC} NewWindow := TMyWindow.CREATE(NIL, heap, wmgrID); {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TMyWindow; FUNCTION TMyWindow.CREATE(object: TObject; heap: THeap; itsWmgrID: TWindowID): TMyWindow; BEGIN {$IFC fTrace}BP(10);{$ENDC} IF object = NIL THEN object := NewObject(heap, THISCLASS); SELF := TMyWindow(TWindow.CREATE(object, heap, itsWmgrID, TRUE)); WITH SELF DO BEGIN styleSheet := NIL; cookBookPanel := NIL; chapterPanel := NIL; recipePanel := NIL; dialogWindow := NIL; dialog := NIL; inputFrame := NIL; okayToHilite := TRUE; END; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE TMyWindow.Fields(PROCEDURE Field(nameAndType: S255)); BEGIN SUPERSELF.Fields(Field); Field('styleSheet: TStyleSheet'); Field('cookBookPanel: TPanel'); Field('chapterPanel: TPanel'); Field('recipePanel: TPanel'); Field('dialogWindow: TDialogWindow'); Field('dialog: TDialog'); Field('inputFrame: TInputFrame'); Field('okayToHilite: BOOLEAN'); Field(''); END; {$ENDC} PROCEDURE TMyWindow.BlankStationery; VAR viewLRect: LRect; heap: THeap; cbPanel: TPanel; chapPanel: TPanel; recPanel: TPanel; cbView: TCookBookView; chapView: TChapterView; recView: TRecipeView; styleSheet: TStyleSheet; selection: TSelection; dialog: TDialog; dialogWindow: TDialogWindow; inputFrame: TInputFrame; paraFormat: TParaFormat; offset: Point; lPt: LPoint; BEGIN {$IFC fTrace}BP(10);{$ENDC} heap := SELF.Heap; {Create a styleSheet and call InitDefault to create and install a default paraFormat in the list} styleSheet := TStyleSheet.CREATE(NIL, heap); {Create the ingredients list paraFormat} paraFormat := TParaFormat.CREATE(NIL, heap, styleSheet); WITH paraFormat DO BEGIN permanent := TRUE; dfltTStyle := ingredStyle; END; styleSheet.formats.InsLast(paraFormat); {Create the recipe name paraFormat} paraFormat := TParaFormat.CREATE(NIL, heap, styleSheet); WITH paraFormat DO BEGIN permanent := TRUE; dfltTStyle := nameInRecipeStyle; END; styleSheet.formats.InsLast(paraFormat); {Create the directions paraFormat} paraFormat := TParaFormat.CREATE(NIL, heap, styleSheet); WITH paraFormat DO BEGIN permanent := TRUE; dfltTStyle := direcStyle; firstIndent := 24; END; styleSheet.formats.InsLast(paraFormat); SELF.styleSheet := styleSheet; {***** Create the recipe panel, which will contain the list of ingredients and directions ****} recPanel := TPanel.CREATE(NIL, heap, SELF, 20, 30, [aBar, aScroll, aSplit], [aBar, aScroll]); SetLRect(viewLRect, 0, 0, 530, 334); recView := TRecipeView.CREATE(NIL, heap, recPanel, viewLRect); SELF.recipePanel := recPanel; {******* Create the chapter panel, which will contain the list of recipes in a chapter ******} chapPanel := recPanel.Divide(h, 180, pixelsFromEdge, [], 180, [aBar, aScroll], [aBar]); SetLRect(viewLRect, 0, 0, 180, 270); chapView := TChapterView.CREATE(NIL, heap, chapPanel, viewLRect); selection := chapPanel.selection.FreedAndReplacedBy(TChapSelection.CREATE( NIL, heap, chapView, zeroLPt)); SELF.chapterPanel := chapPanel; {******* Create the cook book panel, which will contain the list of chapters ******} cbPanel := chapPanel.Divide(v, 90, pixelsFromEdge, [], 90, [], []); SetLRect(viewLRect, 0, 0, 180, 90); cbView := TCookBookView.CREATE(NIL, heap, cbPanel, viewLRect); selection := cbPanel.selection.FreedAndReplacedBy(TBookSelection.CREATE( NIL, heap, cbView, zeroLPt, noFood)); SELF.cookBookPanel := cbPanel; {Set up dialog box fields. We have just one dialog in this application: the one that askd for a recipe name when the user selects New Recipe} dialogWindow := NewStdDialogWindow(heap, 100, diAccept, diAccept, diDismissDialogBox); dialog := TDialog.CREATE(NIL, heap, 'INPU', dialogWindow.dialogView); dialog.AddOKButton(uAddRecipe); SetLPt(lPt, -20, -16); TButton(dialog.ObjectWithIDNumber(phOK)).OffSetBy(lPt); dialog.AddCancelButton(noCmdNumber); SetLPt(lPt, -20, -20); TButton(dialog.ObjectWithIDNumber(phCancel)).OffSetBy(lPt); dialog.SetDefaultButton(TButton(dialog.ObjectWithIDNumber(phCancel))); dialog.RecalcExtent; SetPt(offset, 15, 0); inputFrame := dialog.NewInputFrame(phTypeIn, sysTypeStyle, offset, stdInputTypeStyle, 30, stdFrameBorders, TRUE, FALSE); dialogWindow.dialogView.AddDialog(dialog); SELF.dialogWindow := dialogWindow; SELF.dialog := dialog; SELF.inputFrame := inputFrame; {$IFC fTrace}EP;{$ENDC} END; FUNCTION TMyWindow.CanDoCommand(cmdNumber: TCmdNumber; VAR checkIt: BOOLEAN): BOOLEAN; BEGIN {$IFC fTrace}BP(12);{$ENDC} CASE cmdNumber OF uRecipeDialog: CanDoCommand := TBookSelection(SELF.cookBookPanel.selection).currFood <> noFood; uDeleteRecipe: WITH SELF.chapterPanel DO {We are always assured of having a TChapSelection in the chapterPanel, because we override TChapSelection.Deselect.} CanDoCommand := (TChapterView(view).recipes <> NIL) AND (TChapSelection(selection).recipeIndex <> 0); {$IFC debugMe} cmdRefresh: CanDoCommand := TRUE; uDrawRects: BEGIN CanDoCommand := TRUE; checkIt := fDrawRects; END; uTraceRecipe: BEGIN CanDoCommand := TRUE; checkIt := fRecipeTrace; END; {$ENDC} uCopyRecipe: BEGIN CanDoCommand := TRecipeView(SELF.recipePanel.view).recipeImage <> NIL; END; uPasteRecipe: BEGIN clipboard.Inspect; CanDoCommand := clipBoard.hasView; END; uOneColumn: BEGIN CanDoCommand := FALSE; {recipe panel has contents} {*} checkIt := FALSE; {currentlyOneColumn} END; uTwoColumn: BEGIN CanDoCommand := FALSE; {recipe panel has contents} checkIt := FALSE; {currentlyTwoColumn} {*} END; OTHERWISE CanDoCommand := SUPERSELF.CanDoCommand(cmdNumber, checkIt); END; {$IFC fTrace}EP;{$ENDC} END; FUNCTION TMyWindow.NewCommand(cmdNumber: TCmdNumber): TCommand; VAR selection: TSelection; chapView: TChapterView; chapSel: TChapSelection; recipe: TRecipe; oldRecipe: TRecipe; s: S255; newIndex: INTEGER; PROCEDURE InvalAll(obj: TObject); BEGIN TPanel(obj).Invalidate; END; BEGIN {$IFC fTrace}BP(12);{$ENDC} NewCommand := NIL; CASE cmdNumber OF uRecipeDialog: IF SELF.dialogBox = NIL THEN BEGIN SELF.chapterPanel.BeginSelection; SELF.PutUpDialogBox(SELF.dialogWindow); SELF.inputFrame.SupplantContents(''); SELF.dialog.SelectInputFrame(SELF.inputFrame); END; uCopyRecipe: BEGIN NewCommand := TCopyRecipeCmd.CREATE(NIL, SELF.Heap, SELF.chapterPanel.view, TChapSelection(SELF.chapterPanel.selection).recipeIndex); END; uPasteRecipe: BEGIN NewCommand := TPasteRecipeCmd.CREATE(NIL, SELF.Heap, SELF.cookBookPanel.view); END; uDeleteRecipe: BEGIN WITH SELF.chapterPanel DO BEGIN chapView := TChapterView(view); chapSel := TChapSelection(selection); END; oldRecipe := TRecipe(chapView.recipes.At(chapSel.recipeIndex)); oldRecipe.name.ToPStr(@s); process.ArgAlert(1, s); IF process.Caution(phReallyDelRecipe) THEN {Delete Recipe is not undoable (an exercise for the reader would be to make it undoable); so we can simply return a generic TCommand with undoable set FALSE and delete the recipe here. Note that if this command was undoable, we would not need to have the user confirm it.} BEGIN {If you look at PerformLast below and the Highlight methods, you will see that we suppress highlighting in the left 2 panels while doing/undoing most commands. This is fine as long as the command does not change the highlighting. Deleting a recipe does change the highlighting. One way to make the highlighint happen is for the command object to set SELF.okayToHilite to TRUE inside .Perform (see TPasteRecipeCmd.DoPaste). We can't do that here because we are going to return a generic TCommand object. Therefore, we must explicitly turn off the highlighting, delete the recipe and turn the highlighting back on afterwards.} SELF.Update(TRUE); {make sure the screen is updated after removing the alert, otherwise turning off the highlighting may not work} SELF.cookbookPanel.Highlight(SELF.cookbookPanel.selection, hOnToOff); {This will update the TRecipe object we are about to delete & free the images, etc. that were created. Need to do this before we start messing with the list of TRecipes} TRecipeView(SELF.recipePanel.view).ChangeRecipe(NIL); chapView.recipes.DelAt(chapSel.recipeIndex, FALSE); SELF.chapterPanel.Invalidate; newIndex := Min(chapSel.recipeIndex, chapView.recipes.size); IF newIndex > 0 THEN recipe := TRecipe(chapView.recipes.At(newIndex)) ELSE recipe := NIL; TRecipeView(SELF.recipePanel.view).ChangeRecipe(recipe); oldRecipe.Free; chapSel.recipeIndex := newIndex; chapSel.MarkChanged; NewCommand := TCommand.CREATE(NIL, SELF.Heap, uDeleteRecipe, chapView, FALSE, revealNone); SELF.cookbookPanel.Highlight(SELF.cookbookPanel.selection, hOffToOn); SELF.Update(TRUE); {do the updating & highlighting of the chapter panel here because of the problem mentioned above; when the normal update occurs after the command is performed no highlighting will be done, so we must do it ourselves} END; END; {$IFC debugMe} cmdRefresh: SELF.panels.Each(InvalAll); uTraceRecipe: fRecipeTrace := NOT fRecipeTrace; uDrawRects: fDrawRects := NOT fDrawRects; {$ENDC} uOneColumn: BEGIN END; uTwoColumn: BEGIN END; OTHERWISE NewCommand := SUPERSELF.NewCommand(cmdNumber); END; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE TMyWindow.PerformLast(cmdPhase: TCmdPhase); BEGIN {$IFC fTrace}BP(12);{$ENDC} {Due to an oversight in TWindow, we must provide this kludge to avoid our selections in the left panels blinking on and off whenever a command is performed. The default PerformLast tells all panels to unhighlight before every command and rehighlight after every command. Thus we add a flag to the window which TBookSelection.Highlight and TChapSelection.Highlight check} SELF.okayToHilite := FALSE; SUPERSELF.PerformLast(cmdPhase); SELF.okayToHilite := TRUE; {$IFC fTrace}EP;{$ENDC} END; BEGIN {$IFC debugMe} fRecipeTrace := FALSE; {$ENDC} END;