Syntax10.Scn.Fnt Syntax10i.Scn.Fnt StampElems Alloc 26 Mar 96 Syntax10b.Scn.Fnt MODULE Dialogs; (** extended version Markus Knasm ller 25.May.94 - IMPORT Display, Files, Types, Modules, Oberon, TextFrames, TextPrinter, Texts, Viewers; CONST ok* = 0; objectIsAlreadyInPanel* = 1; nameExists* = 2; objectNotFound* = 3; wrongInput* = 4; noPanelSelected* = 5; objectWouldOverlap* = 6; tooManyObjectsSelected* = 7; maxItems = 64; TYPE Object* = POINTER TO ObjectDesc; Panel* = POINTER TO PanelDesc; ObjectDesc* = RECORD next: Object; x, y, w, h: LONGINT; name-: ARRAY 16 OF CHAR; (** a panel wide unique name *) cmd-: ARRAY 32 OF CHAR; (** a command to be executed when the obj is changed *) par-: ARRAY 32 OF CHAR; (** the invoked commands can assume that Oberon.par.text contains the contest of these text items *) selected-: BOOLEAN; (** TRUE if the object is selected *) overlapping-: BOOLEAN; (** TRUE if the object may overlap others *) panel-: Panel; (** panel containing the object *) visible: BOOLEAN; (* TRUE if the object is visible *) END; PanelDesc* = RECORD cmd-: ARRAY 64 OF CHAR; (** cmd which initialies the dialog *) contents: Object; END; NotifyMsg* = RECORD(Display.FrameMsg) id*: INTEGER; (** 0 = restore, 1 = hide, 2 = markMenu, 3 = restore all *) obj*: Object; (** defined if id = 0 or id = 1 *) p*: Panel; (** defined if id = 2 or id = 3 *) END; dUnit*, pUnit*: LONGINT; (** for device independent coordinates *) res*: INTEGER; (** result code from last operation *) Edit*: PROCEDURE (obj: Object); Update*: PROCEDURE (obj: Object; p: Panel); cmdPanel*: Panel; (** panel from which the last command was called *) editPanel*: Panel; (** panel for editing the properties of an object *) editObject*: Object; (** object which could be edited by editPanel *) deInit*: Panel; (** panel representing DEInit.Dlg *) lastin*: Object; (** most recently inserted object *) w0: Texts.Writer; PROCEDURE^ (p: Panel) MarkMenu*; PROCEDURE^ (p: Panel) NamedObject* (name: ARRAY OF CHAR): Object; PROCEDURE^ (p: Panel) GetObjects* (x, y, w, h: INTEGER; VAR obArray: ARRAY OF Object; VAR nofelems: INTEGER); PROCEDURE^ (p: Panel) RestoreOverlapped (x, y, w, h: INTEGER; o: Object); PROCEDURE^ (p: Panel) RemoveSelections*; PROCEDURE (o: Object) Draw* (x, y: INTEGER; f: Display.Frame); (** abstract: displays the object at (x, y) in frame f *) END Draw ; PROCEDURE (o: Object) Copy* (VAR dup: Object); (** allocates dup and makes a deep copy of o. For calling this methode dup should be equal NIL *) BEGIN IF dup = NIL THEN NEW (dup) END; dup.x := o.x; dup.y := o.y; dup.w := o.w; dup.h := o.h; dup.name := o.name; dup.next := NIL; dup.cmd := o.cmd; dup.par := o.par; dup.selected := FALSE; dup.overlapping := o.overlapping; dup.panel := NIL; END Copy; PROCEDURE (o: Object) Print* (x, y: INTEGER); (** abstract: prints the object at printer coordinates (x, y) *) END Print; PROCEDURE (o: Object) Handle* (f: Display.Frame; VAR msg: Display.FrameMsg); (** abstract: handles messages which were sent to frame f *) END Handle; PROCEDURE (o: Object) Edit*; (** opens a dialog for editing the properties of the object *) BEGIN IF Edit # NIL THEN Edit (o) END END Edit; PROCEDURE (o: Object) Update* (p: Panel); (** sets the properties of the object to the values defined in the dialog p opened with Edit *) BEGIN IF Update # NIL THEN Update (o, p) END END Update; PROCEDURE (o: Object) Init*; (** initialies the object, should be called after allocating the object with NEW *) BEGIN o.selected := FALSE; o.panel := NIL; o.cmd[0] := 0X; o.par[0] := 0X; o.visible := TRUE; END Init; PROCEDURE (o: Object) GetDim* (VAR x, y, w, h: INTEGER); (** Gets x, y, width and height of object o for drawing *) BEGIN x := SHORT (o.x DIV dUnit); y := SHORT (o.y DIV dUnit); w := SHORT (o.w DIV dUnit); h := SHORT (o.h DIV dUnit); END GetDim; PROCEDURE (o: Object) GetPDim* (VAR x, y, w, h: INTEGER); (** Gets x, y, width and height of object o for printing *) BEGIN x := SHORT (o.x DIV pUnit); y := SHORT (o.y DIV pUnit); w := SHORT (o.w DIV pUnit); h := SHORT (o.h DIV pUnit); END GetPDim; PROCEDURE (o: Object) Load* (VAR r: Files.Rider); (** reads the object from rider r *) VAR name1: ARRAY 64 OF CHAR; cmd1, par1: ARRAY 64 OF CHAR; BEGIN o.Init; Files.ReadString (r, name1); COPY (name1, o.name); Files.ReadString (r, cmd1); COPY (cmd1, o.cmd); Files.ReadString (r, par1); COPY (par1, o.par); Files.ReadLInt (r, o.x); Files.ReadLInt (r, o.y); Files.ReadLInt (r, o.w); Files.ReadLInt (r, o.h); Files.ReadBool (r, o.overlapping) END Load; PROCEDURE (o: Object) Store* (VAR r: Files.Rider); (** writes the object to rider r *) BEGIN Files.WriteString (r, o.name); Files.WriteString (r, o.cmd); Files.WriteString (r, o.par); Files.WriteLInt (r, o.x); Files.WriteLInt (r, o.y); Files.WriteLInt (r, o.w); Files.WriteLInt (r, o.h); Files.WriteBool (r, o.overlapping) END Store; PROCEDURE (o: Object) CallCmd* (f: Display.Frame; v: Viewers.Viewer; t: Texts.Text); (** invokes the command obj.cmd *) VAR callres: INTEGER; BEGIN IF o.cmd[0] # 0X THEN Oberon.Par.frame := f; Oberon.Par.vwr := v; Oberon.Par.text := t; Oberon.Par.pos := 0; cmdPanel := o.panel; Oberon.Call (o.cmd, Oberon.Par, FALSE, callres) END END CallCmd; PROCEDURE (o: Object) SetCmd* (cmd: ARRAY OF CHAR); (** sets the command of the object to cmd *) BEGIN IF cmd # o.cmd THEN COPY (cmd, o.cmd); IF o.panel # NIL THEN o.panel.MarkMenu END END END SetCmd; PROCEDURE (o: Object) SetPar* (par: ARRAY OF CHAR); (** sets the command of the object to par *) BEGIN IF par # o.par THEN COPY (par, o.par); IF o.panel # NIL THEN o.panel.MarkMenu END END END SetPar; PROCEDURE (o: Object) Restore*; (** restores object o => redraws it *) VAR msg: NotifyMsg; BEGIN msg.id := 0; msg.obj := o; Viewers.Broadcast (msg); o.visible := TRUE END Restore; PROCEDURE (o: Object) SetName* (name: ARRAY OF CHAR); (** sets the name of the object to name, unless in the panel containing o already exists such a name *) BEGIN IF (o.panel = NIL) OR (name[0] = 0X) OR (o.panel.NamedObject (name) = NIL) OR (o.panel.NamedObject (name) = o) THEN IF o.name # name THEN COPY (name, o.name); res := ok; IF o.panel # NIL THEN o.panel.MarkMenu; o.Restore; END END ELSE res := nameExists END END SetName; PROCEDURE (o: Object) Hide*; (** removes object from screen, but not from panel *) VAR msg: NotifyMsg; ox, oy, ow, oh, nofelems, i: INTEGER; obArray: ARRAY 50 OF Object; BEGIN IF o.panel = NIL THEN RETURN END; msg.id := 1; msg.obj := o; Viewers.Broadcast (msg); o.visible := FALSE; IF o.overlapping THEN o.GetDim (ox, oy, ow, oh); o.panel.RestoreOverlapped (ox, oy, ow, oh, o); (*o.panel.GetObjects (ox, oy, ow, oh, obArray, nofelems); i := 0; WHILE i < nofelems DO IF obArray[i] # o THEN obArray[i].Restore END; INC (i) END *) END END Hide; PROCEDURE (o: Object) Select* (); (** selects o and displays it selected *) VAR x, y, w, h: INTEGER; BEGIN IF ~ o.selected THEN o.selected := TRUE; o.Hide; o.Restore END END Select; PROCEDURE (o: Object) UnSelect* (); (** unselects o and displays it unselected *) VAR x, y, w, h: INTEGER; BEGIN IF o.selected THEN o.selected := FALSE; o.Hide; o.Restore; END END UnSelect; PROCEDURE (o: Object) IsIn (x, y, w, h: INTEGER): BOOLEAN; VAR x0, y0, w0, h0: LONGINT; BEGIN x0 := x * dUnit; y0 := y * dUnit; w0 := w * dUnit; h0 := h * dUnit; IF ~ (y0 + h0 < o.y) THEN IF (y0 + h0 >= o.y) & (y0 + h0 <= o.y + o.h) & ~ (((x0 < o.x) & (x0 + w0 < o.x)) OR (x0 > o.x + o.w)) THEN RETURN TRUE END; IF (y0 + h0 > o.y + o.h) & (y0 <= o.y + o.h) & ~ (((x0 < o.x) & (x0 + w0 < o.x)) OR (x0 > o.x + o.w)) THEN RETURN TRUE END END; RETURN FALSE END IsIn; PROCEDURE (o: Object) IsOverlapped (x, y, w, h: INTEGER): BOOLEAN; VAR x0, y0 , w0, h0: LONGINT; BEGIN x0 := x * dUnit; y0 := y * dUnit; w0 := w * dUnit; h0 := h * dUnit; RETURN (o.x >= x0) & (o.y >= y0) & (o.x + o.w <= x0 + w0) & (o.y + o.h <= y0 + h0) END IsOverlapped; PROCEDURE (o: Object) IsOverlapping (p: Panel; x, y, w, h: LONGINT; sel: BOOLEAN): BOOLEAN; (* Returns TRUE if o with new dimensions x, y, w, h would overlapping another object of panel p. If sel then overlapping a selected object doesn`t matter. *) VAR o1: Object; b: BOOLEAN; hx, hy, hw, hh: LONGINT; BEGIN IF o.overlapping THEN RETURN FALSE END; o1 := p.contents; WHILE o1 # NIL DO IF (o1 # o) & ~ o1.overlapping & ~(o1.selected & sel) THEN IF (o1.y < y + h) & (o1.y + o1.h > y) & (o1.x < x + w) & (o1.x + o1.w > x) THEN RETURN TRUE END END; o1 := o1.next END; RETURN FALSE END IsOverlapping; PROCEDURE (o: Object) SetDim* (x, y, w, h: INTEGER; cond: BOOLEAN); (** Sets x, y, width and height of object o *) VAR ox, oy, ow, oh: LONGINT; ax, ay, aw, ah: INTEGER; BEGIN o.GetDim (ax, ay, aw, ah); ox := x * dUnit; oy := y * dUnit; ow := w * dUnit; oh := h * dUnit; IF ow < dUnit THEN ow := dUnit END; IF oh < dUnit THEN oh := dUnit END; IF o.panel = NIL THEN o.x := ox; o.y := oy; o.w := ow; o.h := oh; res := ok ELSIF ~ o.IsOverlapping (o.panel, ox, oy, ow, oh, cond) THEN IF ~ o.selected THEN o.panel.RemoveSelections END; o.Hide; o.x := ox; o.y := oy; o.w := ow; o.h := oh; o.Restore; o.panel.MarkMenu; o.panel.RestoreOverlapped (ax, ay, aw, ah, o); res := ok ELSE res := objectWouldOverlap END; END SetDim; PROCEDURE (o: Object) OverlappingObject* (): Object; (** returns the object overlapping this object *) VAR o1, ret: Object; x, y, w, h, w1, h1: INTEGER; BEGIN IF o.panel = NIL THEN RETURN NIL END; o1 := o.panel.contents; ret := NIL; WHILE o1 # NIL DO IF (o # o1) & (o1.overlapping) THEN o1.GetDim (x, y, w, h); IF o.IsIn (x, y, w, h) THEN IF (ret = NIL) THEN ret := o1 ELSE ret.GetDim (x, y, w1, h1); IF w1 * h1 > w * h THEN ret := o1 END END; END END; o1 := o1.next; END; RETURN ret END OverlappingObject; PROCEDURE (p: Panel) RestoreOverlapped (x, y, w, h: INTEGER; o: Object); VAR o1: Object; PROCEDURE Redraw; BEGIN IF o1.selected THEN IF o1.visible THEN o1.Hide END; o1.Restore ELSE o1.Restore END END Redraw; BEGIN o1 := p.contents; WHILE o1 # NIL DO IF (o1 # o) & o1.overlapping & o1.visible & o1.IsIn (x, y, w, h) THEN Redraw END; o1 := o1.next END; IF o.overlapping THEN o1 := p.contents; WHILE o1 # NIL DO IF (o1 # o) & ~ o1.overlapping & o1.visible & o1.IsIn (x, y, w, h) THEN Redraw END; o1 := o1.next END END END RestoreOverlapped; PROCEDURE (p: Panel) SetCmd* (cmd: ARRAY OF CHAR); (** sets the command of the object to cmd *) BEGIN IF cmd # p.cmd THEN COPY (cmd, p.cmd); p.MarkMenu END END SetCmd; PROCEDURE (p: Panel) NamedObject* (name: ARRAY OF CHAR): Object; (** returns the object with name name *) VAR o: Object; BEGIN IF name = "" THEN RETURN NIL END; o := p.contents; WHILE (o # NIL) & (o.name # name) DO o := o.next END; RETURN o END NamedObject; PROCEDURE (p: Panel) Select* (x, y, w, h: INTEGER); (** selects all objects in p which are lying under the box specified by x, y, w, h *) VAR o: Object; BEGIN o := p.contents; WHILE o # NIL DO IF o.IsIn (x, y, w, h) THEN o.Select ELSE o.UnSelect END; o := o.next END END Select; PROCEDURE (p: Panel) GetObjects* (x, y, w, h: INTEGER; VAR obArray: ARRAY OF Object; VAR nofelems: INTEGER); (** gets all objects in p which are lying unter the box specified by x, y, w, h *) VAR o: Object; BEGIN nofelems := 0; o := p.contents; WHILE (o # NIL) & (nofelems < LEN (obArray)) DO IF o.IsIn (x, y, w, h) THEN obArray [nofelems] := o; INC (nofelems) END; o := o.next; END END GetObjects; PROCEDURE (p: Panel) MarkMenu*; (** marks the menu of the frames which are displaying p *) VAR msg: NotifyMsg; BEGIN msg.id := 2; msg.p := p; Viewers.Broadcast (msg); END MarkMenu; PROCEDURE (p: Panel) Restore*; (** restores the panel p => redraws it *) VAR msg: NotifyMsg; BEGIN msg.id := 3; msg.p := p; Viewers.Broadcast (msg) END Restore; PROCEDURE (p: Panel) Remove* (o: Object); (** removes object o of panel p *) VAR q, prev: Object; BEGIN q := p.contents; WHILE (q # NIL) & (q # o) DO prev := q; q := q.next END; IF q # NIL THEN q.Hide; IF q = p.contents THEN p.contents := q.next ELSE prev.next := q.next END; q.next := NIL; res := ok; p.MarkMenu ELSE res := objectNotFound END END Remove; PROCEDURE (p: Panel) RemoveObjects* (x, y, w, h: INTEGER); (** deletes all objects in p which are within x, y, w, h *) VAR o, next: Object; BEGIN o := p.contents; WHILE o # NIL DO next := o.next; IF o.IsIn (x, y, w, h) THEN p.Remove (o) END; o := next; END END RemoveObjects; PROCEDURE (p: Panel) Enumerate* (handle: PROCEDURE (obj: Object; VAR done: BOOLEAN)); (** calls the procedure handle for every object of the panel *) VAR obj: Object; done: BOOLEAN; BEGIN done := FALSE; obj := p.contents; WHILE (obj # NIL) & ~ done DO handle (obj, done); obj := obj.next END END Enumerate; PROCEDURE (p:Panel) RemoveSelections* (); (** Unselects all objects *) VAR o: Object; BEGIN o := p.contents; WHILE o # NIL DO o.UnSelect (); o := o.next END; END RemoveSelections; PROCEDURE (p: Panel) Insert* (o: Object; ov: BOOLEAN); (** inserts object o in panel p *) VAR i, x0, j: INTEGER; a, b: ARRAY 15 OF CHAR; BEGIN o.overlapping := ov; IF ~ o.IsOverlapping(p, o.x, o.y, o.w, o.h, FALSE) THEN IF p.NamedObject (o.name) = NIL THEN o.panel := p; o.next := p.contents; p.contents := o; o.Restore; o.panel.MarkMenu; lastin := o; ELSE res := nameExists END ELSE res := objectWouldOverlap END END Insert; PROCEDURE (p: Panel) Copy* (): Panel; (** returns a deep copy of p *) VAR copy: Panel; o, o1: Object; BEGIN NEW (copy); o := p.contents; copy.cmd := p.cmd; WHILE o # NIL DO o1 := NIL; o.Copy (o1); copy.Insert (o1, o.overlapping); o := o.next; END; RETURN copy END Copy; PROCEDURE (p: Panel) NofSelObjects* (): INTEGER; (** returns the number of selected objects in p *) VAR o: Object; count: INTEGER; BEGIN o := p.contents; count := 0; WHILE o # NIL DO IF o.selected THEN INC (count) END; o := o.next END; RETURN (count) END NofSelObjects; PROCEDURE (p: Panel) ThisObject* (x, y: INTEGER): Object; (** returns the object including the coordinates x and y; first it tries to get a not overlapping object *) VAR o1, o: Object; x0, y0: LONGINT; BEGIN o := p.contents; o1:= NIL; x0 := x * dUnit; y0 := y * dUnit; WHILE o # NIL DO IF (x0 >= o.x) & (x0 < o.x + o.w) & (y0 >= o.y) & (y0 < o.y + o.h) THEN IF (o1 = NIL) OR ~ o.overlapping THEN o1 := o END END; o := o.next END; RETURN o1 END ThisObject; PROCEDURE (p: Panel) Draw* (x, y: INTEGER; f: Display.Frame); (** draws the panel at (x, y) in frame f *) VAR o: Object; ox, oy, ow, oh: INTEGER; BEGIN o := p.contents; WHILE o # NIL DO IF o.overlapping THEN o.GetDim (ox, oy, ow, oh); o.Draw (x + ox, y + oy , f) END; o := o.next END; o := p.contents; WHILE o # NIL DO IF ~ o.overlapping THEN o.GetDim (ox, oy, ow, oh); o.Draw (x + ox, y + oy , f) END; o := o.next END END Draw; PROCEDURE (p: Panel) Print* (x, y: INTEGER); (** prints the panel at printer coordinates (x, y) *) VAR o: Object; ox, oy, ow, oh: INTEGER; BEGIN o := p.contents; WHILE o # NIL DO o.GetPDim (ox, oy, ow, oh); o.Print (x + ox, y + oy); o := o.next END END Print; PROCEDURE (p: Panel) Load* (VAR r: Files.Rider); (** reads the panel from rider r *) VAR cnt, end1, end2, h: INTEGER; o, prev: Object; module: Modules.ModuleName; name: ARRAY 32 OF CHAR; tab1: ARRAY maxItems OF Modules.ModuleName; tab2: ARRAY maxItems OF ARRAY 32 OF CHAR; pos: LONGINT; BEGIN p.contents := NIL; prev := NIL; Files.ReadInt(r, cnt); COPY ("", p.cmd); end1 := 0; end2 := 0; WHILE cnt # 0 DO DEC (cnt); pos := Files.Pos (r); Files.ReadInt (r, h); IF h < end1 THEN module := tab1[h] ELSE Files.Set (r, Files.Base (r), pos); Files.ReadString (r, module); tab1[end1] := module; INC (end1) END; pos := Files.Pos (r); Files.ReadInt (r, h); IF h < end2 THEN COPY (tab2[h], name) ELSE Files.Set (r, Files.Base (r), pos); Files.ReadString (r, name); COPY (name, tab2[end2]); INC (end2) END; Types.NewObj (o, Types.This (Modules.ThisMod (module), name)); ASSERT (o # NIL); o.Load (r); o.panel := p; IF prev # NIL THEN prev.next := o ELSE p.contents := o END; prev := o END; Files.ReadString (r, p.cmd); p.Restore () END Load; PROCEDURE (p: Panel) Store* (VAR r: Files.Rider); (** stores the panel from rider r *) VAR cnt, end1, end2, i: INTEGER; o: Object; type: Types.Type; cond: BOOLEAN; tab1, tab2: ARRAY maxItems OF ARRAY 32 OF CHAR; BEGIN o := p.contents; cnt := 0; end1 := 0; end2 := 0; WHILE o # NIL DO INC (cnt); o := o.next END; Files.WriteInt (r, cnt); o := p.contents; WHILE o # NIL DO type := Types.TypeOf (o); cond := FALSE; FOR i := 0 TO end1 -1 DO IF tab1[i] = type.module.name THEN Files.WriteInt (r, i); cond := TRUE END; END; IF ~cond THEN Files.WriteString (r, type.module.name); COPY (type.module.name, tab1[end1]); INC (end1) END; cond := FALSE; FOR i := 0 TO end2 -1 DO IF tab2[i] = type.name THEN Files.WriteInt (r, i); cond := TRUE END; END; IF ~cond THEN Files.WriteString (r, type.name); COPY (type.name, tab2[end2]); INC (end2) END; o.Store (r); o := o.next END; Files.WriteString (r, p.cmd) END Store; PROCEDURE (p: Panel) Contains* (o: Object): BOOLEAN; (** returns TRUE if the panel contains o *) VAR o1: Object; BEGIN o1 := p.contents; WHILE o1 # NIL DO IF o1 = o THEN RETURN TRUE END; o1 := o1.next END; RETURN FALSE END Contains; PROCEDURE (p: Panel) MoveSelected* (dx, dy: INTEGER); (** moves all selected objects around dx and dy *) VAR o: Object; ov: BOOLEAN; msg: NotifyMsg; ox, oy, ow, oh, i, nofelems: INTEGER; dx0, dy0: LONGINT; obArray: ARRAY 50 OF Object; BEGIN IF p.NofSelObjects () = 0 THEN res := ok; RETURN END; o := p.contents; ov := FALSE; dx0 := dx * dUnit; dy0 := dy * dUnit; WHILE (o # NIL) & (~ ov) DO IF o.selected THEN ov := o.IsOverlapping (p, o.x + dx0, o.y + dy0, o.w, o.h, TRUE) END; o := o.next END; o := p.contents; IF ~ ov THEN WHILE o # NIL DO IF o.selected THEN msg.id := 1; msg.obj := o; Viewers.Broadcast (msg); o.GetDim (ox, oy, ow, oh); p.GetObjects (ox, oy, ow, oh, obArray, nofelems); i := 0; WHILE i < nofelems DO IF (~ obArray[i].selected) THEN obArray[i].Restore END; INC (i) END END; o := o.next END; o := p.contents; WHILE o # NIL DO IF o.selected THEN o.x := o.x + dx0; o.y := o.y + dy0 END; o := o.next END; o := p.contents; WHILE o # NIL DO IF o.selected & o.overlapping THEN o.Restore END; o := o.next END; o := p.contents; WHILE o # NIL DO IF o.selected & ~ o.overlapping THEN o.Restore END; o := o.next END; res := ok; p.MarkMenu ELSE res := objectWouldOverlap END END MoveSelected; PROCEDURE (p: Panel) ChangeDistance (dir: CHAR); VAR sort: ARRAY 50 OF Object; n, i: INTEGER; o: Object; d: LONGINT; PROCEDURE Greater (o1, o2: Object): BOOLEAN; BEGIN IF (dir = "R") OR (dir = "L") THEN RETURN o1.x > o2.x ELSE RETURN o1.y > o2.y END END Greater; BEGIN (* ---- sort objects *) o := p.contents; n := 0; WHILE o # NIL DO IF o.selected THEN i := n - 1; WHILE (i >= 0) & Greater (sort [i], o) DO sort [i + 1] := sort [i]; DEC (i) END; sort [i + 1] := o; INC (n) END; o := o.next END; (* ---- calculate distance *) d := 0; IF (dir = "R") OR (dir = "L") THEN FOR i := 0 TO n - 2 DO d := d + sort[i].x - sort[i + 1].x - sort[i + 1].w END ELSE FOR i := 0 TO n - 2 DO d := d + sort[i].y - sort[i + 1].y - sort[i + 1].h END END; d := d DIV (n - 1); (* ---- change distance *) IF (dir = "R") OR (dir = "L") THEN FOR i := 0 TO n - 2 DO sort[i + 1].x := sort[i].x - sort[i + 1].w - d END ELSIF (dir = "U") OR (dir = "D") THEN FOR i := 0 TO n - 2 DO sort[i + 1].y := sort[i].y - sort[i + 1].h - d END END END ChangeDistance; PROCEDURE (p: Panel) AlignTest (dir: CHAR; x: LONGINT): BOOLEAN; (* returns TRUE if Align with parameters dir and x is not possible *) VAR p2: Panel; o: Object; BEGIN p2 := p.Copy (); o := p2.contents; WHILE o # NIL DO IF o.selected THEN IF dir = "R" THEN o.x := x - o.w ELSIF dir = "L" THEN o.x := x ELSIF dir = "U" THEN o.y := x - o.h ELSIF dir = "D" THEN o.y := x END; END; o := o.next END; o := p2.contents; WHILE o # NIL DO IF o.IsOverlapping (p2, o.x, o.y, o.w, o.h, FALSE) THEN RETURN TRUE END; o := o.next END; RETURN FALSE END AlignTest; PROCEDURE (p: Panel) RegulateDistanceTest (dir: CHAR): BOOLEAN; (* returns TRUE if RegulateDistance with parameters dir and x is not possible *) VAR p2: Panel; o: Object; BEGIN p2 := p.Copy (); p2.ChangeDistance (dir); o := p2.contents; WHILE o # NIL DO IF o.IsOverlapping (p2, o.x, o.y, o.w, o.h, FALSE) THEN RETURN TRUE END; o := o.next END; RETURN FALSE END RegulateDistanceTest; PROCEDURE (p: Panel) AlignSelected* (dir: CHAR); (** aligns the selected objects according to dir (Right, Left, Up or Down) *) VAR o: Object; x: LONGINT; PROCEDURE Max; BEGIN IF dir = "R" THEN IF o.x + o.w > x THEN x := o.x + o.w END ELSIF dir = "L" THEN IF o.x < x THEN x := o.x END ELSIF dir = "U" THEN IF o.y + o.h > x THEN x := o.y + o.h END ELSIF dir = "D" THEN IF o.y < x THEN x := o.y END END END Max; BEGIN IF (dir # "R") & (dir # "L") & (dir # "U") & (dir # "D") THEN res := wrongInput; RETURN END; IF p.NofSelObjects() = 0 THEN res:= ok; RETURN END; o := p.contents; IF (dir = "R") OR (dir = "D") THEN x := 0 ELSIF (dir = "L") THEN x := MAX (LONGINT) ELSE x := MIN (LONGINT); END; WHILE o # NIL DO IF o.selected THEN Max END; o := o.next END; IF ~ p.AlignTest (dir, x) THEN o := p.contents; WHILE o# NIL DO IF o.selected THEN IF dir = "R" THEN o.x := x - o.w ELSIF dir = "L" THEN o.x := x ELSIF dir = "U" THEN o.y := x - o.h ELSIF dir = "D" THEN o.y := x END END; o := o.next; END; p.Restore; res := ok; p.MarkMenu ELSE res := objectWouldOverlap END END AlignSelected; PROCEDURE (p: Panel) RegulateDistance* (dir: CHAR); (** aligns the selected objects along the direction dir such that they are equidistant *) BEGIN IF (dir # "R") & (dir # "L") & (dir # "U") & (dir # "D") THEN res := wrongInput; RETURN END; IF p.NofSelObjects () > 50 THEN res := tooManyObjectsSelected; RETURN END; IF p.NofSelObjects () < 3 THEN res := ok; RETURN END; IF ~ p.RegulateDistanceTest (dir) THEN p.ChangeDistance (dir); p.Restore (); res := ok; p.MarkMenu ELSE res := objectWouldOverlap END END RegulateDistance; PROCEDURE (p: Panel) Broadcast* (f: Display.Frame; VAR m: Display.FrameMsg); (** sends the message m to all objects in the panel p which is displayed in frame f *) VAR o, o1: Object; BEGIN o := p.contents; WHILE o # NIL DO o.Handle (f, m); o := o.next; END END Broadcast; PROCEDURE Error* (name: ARRAY OF CHAR); (** writes an error message to the log viewer *) BEGIN Texts.WriteString (w0, name); IF res = objectIsAlreadyInPanel THEN Texts.WriteString (w0, " Error 1: Object is already in Panel") ELSIF res = nameExists THEN Texts.WriteString (w0, " Error 2: Name exists") ELSIF res = objectNotFound THEN Texts.WriteString (w0, " Error 3: Object not found") ELSIF res = wrongInput THEN Texts.WriteString (w0, " Error 4: Wrong input") ELSIF res = noPanelSelected THEN Texts.WriteString (w0, "Error 5: No panel selected") ELSIF res = objectWouldOverlap THEN Texts.WriteString (w0, " Error 6: Object would overlap another object") ELSIF res = tooManyObjectsSelected THEN Texts.WriteString (w0, " Error 7: Too many objects selected") ELSE Texts.WriteInt (w0, res, 5) END; Texts.WriteLn (w0); Texts.Append (Oberon.Log, w0.buf) END Error; BEGIN dUnit := TextFrames.Unit; pUnit := TextPrinter.Unit; Edit := NIL; Update := NIL; res := ok; editPanel := NIL; cmdPanel := NIL; editObject := NIL; lastin := NIL; Texts.OpenWriter (w0); END Dialogs.