{ Provides the framework for a palette view consisting of a rectangular array of palette boxes (all the same size), and a selection into the palette. Clients must: (1) subclass TPalView to implement a method for drawing the palette symbols (2) after creating the TPalView, create a TPalSelection in the panel (the same TPalSelection is reused throughout) Clients may: (1) subclass TPalSelection, in order to implement NewCommand *** THINGS TO CONSIDER *** Handling commands without subclassing TPalSelection Adding a flag to indicate whether to blink the highlighting rapidly If user changes his mind after clicking in palette Handle mouse outside palette } UNIT UPalette; {$E ERRORS.TEXT} {$E+} {go into the editor automagically} INTERFACE USES {$U UObject} UObject, {$U QuickDraw} QuickDraw, {$U UDraw} UDraw, {$U UABC} UABC; CONST palKind = 1; TYPE TPalView = SUBCLASS OF TView {Fields} palBoxH: LONGINT; palBoxV: LONGINT; numCols: INTEGER; numRows: INTEGER; {Creation/Destruction} FUNCTION {TPalView.}CREATE(object: TObject; itsHeap: THeap; itsPanel: TPanel; itsBoxH, itsBoxV: LONGINT; itsNumCols, itsNumRows: INTEGER): TPalView; {NOTE: rows and columns are numbered from 1} {Display/Display Metrics} PROCEDURE {TPalView.}BoxWith(lPt: LPoint; VAR atCol, atRow: INTEGER); PROCEDURE {TPalView.}ChangedSelection(atCol, atRow: INTEGER); {Called by TPalSelection.SetSelection, and TPalSelection.Restore in case the view wants to do something when the selection changes; clients should not call this directly.} PROCEDURE {TPalView.}GetBoxLRect(atCol, atRow: INTEGER; VAR boxLRect: LRect); {PROCEDURE TPalView. Draw;} PROCEDURE {TPalView.}DrawSymbol(atCol, atRow: INTEGER); PROCEDURE {TPalView.}HighlightSymbol(atCol, atRow: INTEGER; highTransit: THighTransit); END; TPalSelection = SUBCLASS OF TSelection {Fields} selCol: INTEGER; selRow: INTEGER; {Creation/Destruction} FUNCTION {TPalSelection.}CREATE(object: TObject; itsHeap: THeap; itsView: TPalView): TPalSelection; {initial selection is column 1, row 1} {Highlighting} {PROCEDURE TPalSelection. Highlight(highTransit: THighTransit);} PROCEDURE {TPalSelection.}SetSelection(atCol, atRow: INTEGER; fDoHighlight, fUnconditional, fNotifyView: BOOLEAN); {if fDoHighlight is TRUE, updates the highlighting; if fUnconditional is FALSE, then first checks to see if the current selection matches the desired one, and if so does no highlighting; if fNotifyView is TRUE and the selection is different or fUnconditional is TRUE then we will call TPalView.ChangedSelection with the new selection} {Mouse Tracking} {PROCEDURE TPalSelection. MouseMove(mouseLPt: LPoint);} {PROCEDURE TPalSelection. MousePress(mouseLPt: LPoint);} {PROCEDURE TPalSelection. MouseRelease;} {MouseRelease just calls TPalView.ChangedSelection} {Commands} {FUNCTION TPalSelection. NewCommand(cmdNumber: TCmdNumber): TCommand;} {PROCEDURE TPalSelection. Restore;} END; IMPLEMENTATION METHODS OF TPalView; FUNCTION {TPalView.}CREATE{(object: TObject; itsHeap: THeap; itsPanel: TPanel; itsBoxH, itsBoxV: LONGINT; itsNumCols, itsNumRows: INTEGER): TPalView}; VAR extentLRect: LRect; BEGIN {$IFC fTrace}BP(10);{$ENDC} SetLRect(extentLRect, 0, 0, (itsBoxH+1)*itsNumCols-1, (itsBoxV+1)*itsNumRows-1); IF object = NIL THEN object := NewObject(itsHeap, THISCLASS); SELF := TPalView(itspanel.NewStatusView(object, extentLRect)); WITH SELF DO BEGIN palBoxH := itsBoxH; palBoxV := itsBoxV; numCols := itsNumCols; numRows := itsNumRows; scrollPastEnd := zeroPt; END; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE {TPalView.}Fields{(PROCEDURE Field(nameAndType: S255))}; BEGIN TView.Fields(Field); Field('palBoxH: LONGINT'); Field('palBoxV: LONGINT'); Field('numCols: INTEGER'); Field('numRows: INTEGER'); Field(''); END; {$ENDC} PROCEDURE {TPalView.}BoxWith{(lPt: LPoint; VAR atCol, atRow: INTEGER)}; BEGIN {$IFC fTrace}BP(12);{$ENDC} WITH SELF DO BEGIN atCol := (lPt.h DIV (palBoxH+1)) + 1; IF atCol < 1 THEN atCol := 1 ELSE IF atCol > numCols THEN atCol := numCols; atRow := (lPt.v DIV (palBoxV+1)) + 1; IF atRow < 1 THEN atRow := 1 ELSE IF atRow > numRows THEN atRow := numRows; END; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TPalView.}ChangedSelection(atCol, atRow: INTEGER); BEGIN END; PROCEDURE {TPalView.}GetBoxLRect{(atCol, atRow: INTEGER; VAR boxLRect: LRect)}; BEGIN {$IFC fTrace}BP(12);{$ENDC} WITH SELF DO {$H-} IF (atCol >= 1) AND (atCol <= SELF.numCols) AND (atRow >= 1) AND (atRow <= SELF.numRows) THEN BEGIN SetLRect(boxLRect, 0, 0, palBoxH, palBoxV); OffSetLRect(boxLRect, (atCol-1)*(palBoxH+1), (atRow-1)*(palBoxV+1)); END ELSE boxLRect := zeroLRect; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TPalView.}Draw; VAR row: INTEGER; col: INTEGER; boxLRect: LRect; BEGIN {$IFC fTrace}BP(12);{$ENDC} PenNormal; FOR row := 1 TO SELF.numRows DO FOR col := 1 TO SELF.numCols DO BEGIN SELF.GetBoxLRect(col, row, boxLRect); InsetLRect(boxLRect, -1, -1); IF LRectIsVisible(boxLRect) THEN BEGIN FrameLRect(boxLRect); SELF.DrawSymbol(col, row); END; END; {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TPalView.}DrawSymbol{(atCol, atRow: INTEGER)}; BEGIN {$IFC fTrace}BP(12);{$ENDC} {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TPalView.}HighlightSymbol{(atCol, atRow: INTEGER; highTransit: THighTransit)}; VAR boxLRect: LRect; BEGIN {$IFC fTrace}BP(12);{$ENDC} thePad.SetPenToHighlight(highTransit); PenSize(2, 1); SELF.GetBoxLRect(atCol, atRow, boxLRect); FrameLRect(boxLRect); {$IFC fTrace}EP;{$ENDC} END; END; METHODS OF TPalSelection; FUNCTION {TPalSelection.}CREATE{(object: TObject; itsHeap: THeap; itsView: TPalView): TPalSelection}; BEGIN {$IFC fTrace}BP(11);{$ENDC} IF object = NIL THEN object := NewObject(itsHeap, THISCLASS); SELF := TPalSelection(TSelection.CREATE(object, itsHeap, itsView, palKind, zeroLPt)); WITH SELF DO BEGIN selCol := 1; selRow := 1; END; {$IFC fTrace}EP;{$ENDC} END; {$IFC fDebugMethods} PROCEDURE {TPalSelection.}Fields{(PROCEDURE Field(nameAndType: S255))}; BEGIN TSelection.Fields(Field); Field('selCol: INTEGER'); Field('selRow: INTEGER'); Field(''); END; {$ENDC} PROCEDURE {TPalSelection.}Highlight{(highTransit: THighTransit)}; BEGIN {$IFC fTrace}BP(12);{$ENDC} WITH SELF DO {$H-} TPalView(view).HighlightSymbol(selCol, selRow, highTransit); {$H+} {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TPalSelection.}MousePress{(mouseLPt: LPoint)}; VAR newCol: INTEGER; newRow: INTEGER; BEGIN {$IFC fTrace}BP(12);{$ENDC} TPalView(SELF.view).BoxWith(mouseLPt, newCol, newRow); SELF.SetSelection(newCol, newRow, TRUE, TRUE, FALSE); {the TRUEs means the highlighting will blink rapidly} {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TPalSelection.}MouseMove{(mouseLPt: LPoint)}; BEGIN {$IFC fTrace}BP(12);{$ENDC} SELF.MousePress(mouseLPt); {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TPalSelection.}MouseRelease; BEGIN {$IFC fTrace}BP(12);{$ENDC} TPalView(SELF.view).ChangedSelection(SELF.selCol, SELF.selRow); {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TPalSelection.}Restore; BEGIN {$IFC fTrace}BP(12);{$ENDC} SUPERSELF.Restore; TPalView(SELF.view).ChangedSelection(SELF.selCol, SELF.selRow); {$IFC fTrace}EP;{$ENDC} END; PROCEDURE {TPalSelection.}SetSelection{(atCol, atRow: INTEGER; fDoHighlight, fUnconditional, fNotifyView: BOOLEAN)}; VAR panel: TPanel; BEGIN {$IFC fTrace}BP(12);{$ENDC} IF fUnconditional OR (atCol <> SELF.selCol) OR (atRow <> SELF.selRow) THEN BEGIN panel := SELF.panel; IF fDoHighlight THEN panel.Highlight(SELF, hOnToOff); WITH SELF DO BEGIN selCol := atCol; selRow := atRow; END; {Notify view} IF fNotifyView THEN TPalView(SELF.view).ChangedSelection(atCol, atRow); IF fDoHighlight THEN panel.Highlight(SELF, hOffToOn); END; {$IFC fTrace}EP;{$ENDC} END; END; END.