<> <<-- Last Edited by: Maxwell, December 7, 1983 9:28 am>> DIRECTORY Atom USING [GetPName, PropList], Buttons USING [Button, ButtonProc, Create, ReLabel, SetDisplayStyle], Commander USING [CommandProc, Register], Containers USING [Create], Desktops, FS USING [Error, StreamOpen], Graphics USING [black, Box, Context, DrawBox, DrawRope, DrawTo, FontRef, GetBounds, SetColor, SetCP, SetPaintMode, SetStipple, white], GraphicsOps USING [BitmapRef, DrawBitmap, NewBitmap, NewContextFromBitmap], IconManager USING [selectedIcon], InputFocus USING [SetInputFocus], IO USING [atom, bool, Close, EndOf, EndOfStream, GetAtom, GetBool, GetChar, GetInt, GetRopeLiteral, GetTokenRope, int, Put, PutF, RIS, rope, STREAM], Menus USING [AppendMenuEntry, CreateEntry, CreateMenu, Menu, MenuProc], MessageWindow USING [Append, Blink], Rope USING [Cat, Compare, Equal, Find, Length, ROPE], VFonts USING [EstablishFont, GraphicsFont], ViewerClasses USING[Column, InitProc, ModifyProc, NotifyProc, PaintProc, SaveProc, ViewerClass, ViewerClassRec, ViewerFlavor, Viewer], ViewerEvents USING [EventProc, RegisterEventProc], ViewerLocks USING [CallUnderViewerTreeLock, Wedged], ViewerOps USING [AddProp, CloseViewer, ChangeColumn, ComputeColumn, CreateViewer, DestroyViewer, EnumerateChildren, EnumerateViewers, EnumProc, FetchProp, FetchViewerClass, FindViewer, GrowViewer, MoveBoundary, MoveViewer, OpenIcon, PaintEverything, PaintViewer, RegisterViewerClass], ViewerSpecs USING [openLeftWidth, openBottomY], ViewerTools USING [GetSelectedViewer, GetSelectionContents]; DeskTopsImpl: CEDAR MONITOR IMPORTS Atom, Buttons, Commander, Containers, FS, Graphics, GraphicsOps, IconManager, InputFocus, IO, Menus, MessageWindow, Rope, VFonts, ViewerEvents, ViewerLocks, ViewerOps, ViewerSpecs, ViewerTools EXPORTS Desktops SHARES ViewerOps = BEGIN OPEN Desktops, ViewerClasses; DeskTopData: TYPE = REF DeskTopDataRec; DeskTopDataRec: TYPE = RECORD[ iconBitmap: GraphicsOps.BitmapRef _ NIL, -- icon bitmap openLeftWidth: INTEGER _ 0, openBottomY: INTEGER _ 0]; current: Viewer; MRUCache: Viewer; buttonHeight: INTEGER _ 16; firstButton: INTEGER _ 3; menu: Menus.Menu; iconFont: Graphics.FontRef _VFonts.GraphicsFont[VFonts.EstablishFont["TimesRoman", 8]]; screenH: INTEGER _ 808; screenW: INTEGER _ 1024; <<******* interface code *******>> <<>> Init: PROC = BEGIN desktop, container, button: ViewerClasses.ViewerClass; <> menu _ Menus.CreateMenu[]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["FlyTo", MenuFlyTo]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["New", MenuCreate]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Rename", MenuRename]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["AddSel", MenuMove]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["CopySel", MenuCopy]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["DeleteSel", MenuDelete]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Read", MenuRead]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Write", MenuWrite]]; button _ ViewerOps.FetchViewerClass[$Button]; button.modify _ ButtonModify; -- handle button selection container _ ViewerOps.FetchViewerClass[$Container]; <> desktop _ NEW[ViewerClassRec _ container^]; desktop.flavor _ $DeskTop; desktop.notify _ DeskTopNotify; desktop.init _ DeskTopInit; desktop.paint _ DeskTopPaint; desktop.menu _ menu; desktop.icon _ document; ContainerInit _ container.init; -- save for future use ViewerOps.RegisterViewerClass[$DeskTop, desktop]; [] _ Buttons.Create[info: [name: "Clean", column: static], proc: ButtonClean]; [] _ ViewerEvents.RegisterEventProc[FlushEntries, destroy, $DeskTop]; Commander.Register["Desktop", DoIt, "Creates a new desktop."]; Commander.Register["ReadDesktop", DoIt, "Reads a desktop from a file on the disk."]; Commander.Register["WriteDesktop", DoIt, "Overwrites a desktop file on the disk."]; END; DoIt: Commander.CommandProc = BEGIN token: Rope.ROPE; stream: IO.STREAM; stream _ IO.RIS[cmd.commandLine]; token _ stream.GetTokenRope[].token; stream.Close[]; SELECT TRUE FROM Rope.Equal[cmd.command, "ReadDesktop", FALSE] => ReadFile[token]; Rope.Equal[cmd.command, "WriteDesktop", FALSE] => WriteFile[Create[token]]; ENDCASE => [] _ Create[token]; END; ButtonClean: Buttons.ButtonProc = {ClearIcons[]}; MenuCreate: Menus.MenuProc = {DeskTopNotify[NARROW[parent], LIST[$Create]]}; MenuFlyTo: Menus.MenuProc = {DeskTopNotify[NARROW[parent], LIST[$FlyTo]]}; MenuRename: Menus.MenuProc = {DeskTopNotify[NARROW[parent], LIST[$Rename]]}; MenuMove: Menus.MenuProc = {DeskTopNotify[NARROW[parent], LIST[$MoveViewer]]}; MenuCopy: Menus.MenuProc = {DeskTopNotify[NARROW[parent], LIST[$CopyViewer]]}; MenuDelete: Menus.MenuProc = {DeskTopNotify[NARROW[parent], LIST[$RemoveViewer]]}; MenuRead: Menus.MenuProc = {DeskTopNotify[NARROW[parent], LIST[$Read]]}; MenuWrite: Menus.MenuProc = {DeskTopNotify[NARROW[parent], LIST[$Write]]}; ContainerInit: ViewerClasses.InitProc; DeskTopInit: ViewerClasses.InitProc = { data: DeskTopData _ NEW[DeskTopDataRec _ []]; ContainerInit[self]; self.icon _ private; data.openBottomY _ ViewerSpecs.openBottomY; data.openLeftWidth _ ViewerSpecs.openLeftWidth; ViewerOps.AddProp[self, $DeskTopData, data]; IF current = NIL THEN { -- make current current _ self; current.menu _ NIL}; }; DeskTopPaint: ViewerClasses.PaintProc = BEGIN <<[self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL] ;>> data: DeskTopData; IF ~self.iconic THEN RETURN; data _ FetchDeskTopData[self]; IF data.iconBitmap = NIL THEN PaintDeskTopIcon[self, data]; Graphics.SetCP[context, 0, height]; GraphicsOps.DrawBitmap[self: context, bitmap: data.iconBitmap, w: 64, h: 64]; <> IF IconManager.selectedIcon = self THEN{ [] _ Graphics.SetPaintMode[context, invert]; Graphics.DrawBox[context, Graphics.GetBounds[context]]}; END; height: INTEGER _ 64; PaintDeskTopIcon: PROC[self: Viewer, data: DeskTopData] = BEGIN scale: REAL = 16; box: Graphics.Box; frobX, frobY: REAL; left, right: BOOL _ TRUE; context: Graphics.Context; darkGrey: CARDINAL = 122645B; lightGrey: CARDINAL = 012024B; <> data.iconBitmap _ GraphicsOps.NewBitmap[64, 64]; context _ GraphicsOps.NewContextFromBitmap[data.iconBitmap]; box _ Graphics.GetBounds[context]; box.xmax _ box.xmax - 1; box.ymax _ box.ymax - 1; Graphics.SetColor[context, Graphics.white]; Graphics.DrawBox[context, box]; <> frobX _ data.openLeftWidth/scale; frobY _ data.openBottomY/scale + 13; Graphics.SetColor[context, Graphics.black]; FOR child: Viewer _ self.child, child.sibling WHILE child # NIL DO entry: Entry _ FetchEntryData[child]; IF entry = NIL OR entry.iconic THEN LOOP; SELECT entry.column FROM left => {DrawRectangle[context, 0, 13+entry.wy/scale, frobX, 13+entry.wy/scale+entry.wh/scale]; left _ FALSE}; right => {DrawRectangle[context, frobX, 13+entry.wy/scale, box.xmax, 13+entry.wy/scale+entry.wh/scale]; right _ FALSE}; ENDCASE; ENDLOOP; <> Graphics.SetStipple[context, lightGrey]; Graphics.DrawBox[context, [box.xmin, 13, box.xmax, frobY]]; SELECT TRUE FROM left AND right => Graphics.DrawBox[context, [box.xmin, 13, box.xmax, box.ymax]]; left => Graphics.DrawBox[context, [box.xmin, 13, frobX-1, box.ymax]]; right => Graphics.DrawBox[context, [frobX+1, 13, box.xmax, box.ymax]]; ENDCASE; <> Graphics.SetColor[context, Graphics.black]; DrawRectangle[context, box.xmin, box.ymin+2, box.xmax, box.ymax]; DrawRectangle[context, box.xmin+1, box.ymin+1, box.xmax-1, box.ymax-1]; Graphics.SetCP[context, box.xmin, 13]; Graphics.DrawTo[context, box.xmax, 13]; <> Graphics.SetCP[context, 3, 4]; Graphics.DrawRope[self: context, rope: self.name, font: iconFont]; IF self = current THEN { -- invert name region to indicate it is the current desktop [] _ Graphics.SetPaintMode[context, invert]; Graphics.DrawBox[context, [box.xmin+2, 3, box.xmax-2, 12]]; [] _ Graphics.SetPaintMode[context, opaque]}; END; DrawRectangle: PROCEDURE[context: Graphics.Context, x1, y1, x2, y2: REAL] = BEGIN <> <> <> <> Graphics.SetCP[context, x1, y1]; Graphics.DrawTo[context, x1, y2]; Graphics.DrawTo[context, x2, y2]; Graphics.DrawTo[context, x2, y1]; Graphics.DrawTo[context, x1, y1]; END; MyButtonProc: Buttons.ButtonProc = { SELECT TRUE FROM mouseButton = red => DeskTopNotify[NARROW[parent], LIST[$SelectEntry]]; mouseButton = blue => DeskTopNotify[NARROW[parent], LIST[$RemoveViewer]]; shift => DeskTopNotify[NARROW[parent], LIST[$GrowEntry]]; ENDCASE => DeskTopNotify[NARROW[parent], LIST[$OpenEntry]]}; ButtonModify: ViewerClasses.ModifyProc = {IF change = set THEN Buttons.SetDisplayStyle[self, $BlackOnGrey] ELSE Buttons.SetDisplayStyle[self, $BlackOnWhite]}; DeskTopNotify: ViewerClasses.NotifyProc = BEGIN FOR input _ input, input.rest WHILE input # NIL DO SELECT input.first FROM $ClearIcons => ClearIcons[]; $OpenDesktop, $FlyTo => FlyTo[self]; $Create => [] _ Create[ViewerTools.GetSelectionContents[]]; $MoveViewer => MoveViewer[SelectedViewer[], SelectedDeskTop[], self, TRUE]; $CopyViewer => MoveViewer[SelectedViewer[], SelectedDeskTop[], self, FALSE]; $Rename => Rename[self, ViewerTools.GetSelectionContents[]]; $OpenEntry => OpenEntry[FetchEntryData[self]]; $GrowEntry => OpenEntry[e: FetchEntryData[self], grow: TRUE]; $RemoveViewer => RemoveViewer[self, SelectedViewer[]]; $SelectEntry => InputFocus.SetInputFocus[self]; $Write => WriteFile[self]; $Read => { file: Rope.ROPE _ ViewerTools.GetSelectionContents[]; IF file.Length[] = 0 THEN file _ self.name; ReadFile[file]}; ENDCASE; ENDLOOP; END; SelectedViewer: PROC RETURNS[viewer: Viewer] = BEGIN viewer _ ViewerTools.GetSelectedViewer[]; IF viewer = NIL THEN RETURN; IF viewer.class.flavor = $Button THEN { entry: Entry _ FetchEntryData[viewer]; IF entry # NIL THEN viewer _ entry.viewer}; IF viewer = NIL THEN RETURN; WHILE viewer.parent # NIL DO viewer _ viewer.parent; ENDLOOP; END; SelectedDeskTop: PROC RETURNS[DeskTop] = BEGIN viewer: Viewer _ ViewerTools.GetSelectedViewer[]; WHILE viewer # NIL DO IF viewer.class.flavor = $DeskTop THEN RETURN[viewer]; viewer _ viewer.parent; ENDLOOP; RETURN[NIL]; END; <<******* basic procedures ******* >> Create: PUBLIC PROC[name: Rope.ROPE _ NIL] RETURNS[desktop: Viewer] = BEGIN FindDeskTop: ViewerOps.EnumProc = { IF v.class.flavor = $DeskTop AND Rope.Equal[v.name, name] THEN {desktop _ v; RETURN[TRUE]}}; IF name.Length[] = 0 THEN name _ "Desktop"; ViewerOps.EnumerateViewers[FindDeskTop]; IF current = NIL THEN current _ ViewerOps.CreateViewer[flavor: $DeskTop, info: [name: "Default", column: right, iconic: TRUE]]; IF desktop = NIL THEN desktop _ ViewerOps.CreateViewer[flavor: $DeskTop, info: [name: name, column: right, iconic: TRUE]]; IF desktop.offDeskTop THEN ViewerOps.ChangeColumn[desktop, right]; END; Rename: PUBLIC PROC[desktop: DeskTop, name: Rope.ROPE] = BEGIN data: DeskTopData; desktop.name _ name; data _ FetchDeskTopData[desktop]; data.iconBitmap _ NIL; -- so new bitmap will be created ViewerOps.PaintViewer[desktop, caption]; END; FlyTo: PUBLIC PROC[desktop: Viewer] = BEGIN data: DeskTopData; OpenEntries: ViewerOps.EnumProc = {OpenEntry[FetchEntryData[v], TRUE]}; SetHeights: ViewerOps.EnumProc = {SetHeight[v]}; OpenDesktops: ViewerOps.EnumProc = {IF v.offDeskTop AND v.class.flavor = $DeskTop THEN ViewerOps.ChangeColumn[v, right]}; IF desktop = NIL OR desktop.class.flavor # $DeskTop THEN RETURN; IF desktop = current THEN {ViewerOps.PaintEverything[]; RETURN}; IF current # NIL THEN { -- save the screen in the current desktop temp: DeskTop _ current; current _ NIL; -- allows additions to be made to temp temp.menu _ menu; RecordScreenState[temp]; ClearScreen[offDeskTop: TRUE]}; current _ desktop; -- make current desktop.menu _ NIL; -- standard for current desktop data _ FetchDeskTopData[desktop]; IF data.openLeftWidth # ViewerSpecs.openLeftWidth OR data.openBottomY # ViewerSpecs.openBottomY THEN ViewerOps.MoveBoundary[data.openLeftWidth, data.openBottomY]; ViewerOps.EnumerateChildren[desktop, OpenEntries]; ViewerOps.EnumerateChildren[desktop, SetHeights]; ViewerOps.EnumerateViewers[OpenDesktops]; [] _ FlushEntries[desktop, save, TRUE]; data.iconBitmap _ NIL; -- so new bitmap will be created ViewerOps.PaintEverything[]; END; RecordScreenState: PROC[desktop: Viewer] = BEGIN AddToDesktop: ViewerOps.EnumProc = { IF v.column = static THEN RETURN; AddEntry[desktop, GetEntry[NIL, v].entry]}; data: DeskTopData _ FetchDeskTopData[desktop]; data.openLeftWidth _ ViewerSpecs.openLeftWidth; data.openBottomY _ ViewerSpecs.openBottomY; [] _ FlushEntries[desktop, save, TRUE]; ViewerOps.EnumerateViewers[AddToDesktop]; data.iconBitmap _ NIL; -- so new bitmap will be created END; ClearScreen: PROC[offDeskTop: BOOLEAN _ TRUE] = BEGIN CloseViewer: ViewerOps.EnumProc = { IF v.column = static THEN RETURN; IF ~v.iconic THEN ViewerOps.CloseViewer[v, FALSE]; IF offDeskTop THEN ViewerOps.ChangeColumn[v, static]}; -- move it off the screen ViewerOps.EnumerateViewers[CloseViewer]; END; ClearIcons: PUBLIC PROC = BEGIN LockedClear: PROC = BEGIN AddIconic: ViewerOps.EnumProc = { IF v.iconic AND ~v.offDeskTop AND UneditedDocument[v] THEN { AddEntry[MRUCache, GetEntry[NIL, v].entry, firstButton, 25]; ViewerOps.DestroyViewer[v]}}; ViewerOps.EnumerateViewers[AddIconic]; END; IF MRUCache = NIL OR MRUCache.destroyed THEN { MRUCache _ Containers.Create[info: [name: "MRU Cache", column: right, iconic: TRUE]]; ViewerOps.AddProp[MRUCache, $DeskTopData, NEW[DeskTopDataRec _ []]]}; IF MRUCache.offDeskTop THEN ViewerOps.ChangeColumn[MRUCache, right]; InputFocus.SetInputFocus[]; ViewerLocks.CallUnderViewerTreeLock[LockedClear ! ViewerLocks.Wedged => CONTINUE]; END; EnumerateEntries: PUBLIC PROC[desktop: DeskTop, proc: EnumProc] = BEGIN child: Viewer _ desktop.child; WHILE child # NIL DO next: Viewer _ child.sibling; IF ~proc[FetchEntryData[child]] THEN EXIT; child _ next; ENDLOOP; END; SetHeight: PROC[button: Viewer] = INLINE BEGIN e: Entry _ FetchEntryData[button]; IF e.iconic THEN RETURN; e.viewer.position _ e.position; e.viewer.openHeight _ e.height; END; UneditedDocument: PROC[v: Viewer] RETURNS[BOOLEAN] = INLINE { RETURN[v # NIL AND v.class.flavor = $Text AND ~v.newVersion AND ~v.newFile]}; continue: BOOLEAN _ TRUE; ReadFile: PUBLIC PROC [filename: Rope.ROPE] = BEGIN ENABLE { UNWIND => NULL; FS.Error => { MessageWindow.Append[Rope.Cat["Error reading desktop file: ", error.explanation], TRUE]; MessageWindow.Blink[]; IF continue THEN CONTINUE}; ANY => { MessageWindow.Append[Rope.Cat["Error reading desktop file: ", filename], TRUE]; MessageWindow.Blink[]; IF continue THEN CONTINUE}}; entry: Entry; desktop: DeskTop; stream: IO.STREAM; entries: LIST OF Entry; IF filename.Find["."] < 0 THEN filename _ Rope.Cat[filename, ".desktop"]; stream _ FS.StreamOpen[filename, read]; WHILE ~stream.EndOf[] DO token: Rope.ROPE _ stream.GetTokenRope[! IO.EndOfStream => EXIT].token; SELECT TRUE FROM Rope.Equal[token, "desktop"] => { desktop _ Create[stream.GetTokenRope[].token]; [] _ FlushEntries[desktop, save, TRUE]}; Rope.Equal[token, "viewer"] => { IF desktop = NIL THEN LOOP; entry _ NEW[EntryRec _ []]; entries _ CONS[entry, entries]; -- use a list to reverse the order [] _ stream.GetChar[]; -- get rid of ':. entry.name _ stream.GetRopeLiteral[]}; Rope.Equal[token, "class"] => IF entry # NIL THEN { [] _ stream.GetChar[]; -- get rid of ':. entry.flavor _ stream.GetAtom[]}; Rope.Equal[token, "file"] => IF entry # NIL THEN { [] _ stream.GetChar[]; -- get rid of ':. entry.backingFile _ stream.GetRopeLiteral[]}; Rope.Equal[token, "column"] => IF entry # NIL THEN { column: Rope.ROPE _ stream.GetTokenRope[].token; SELECT TRUE FROM Rope.Equal[column, "left"] => entry.column _ left; Rope.Equal[column, "right"] => entry.column _ right; Rope.Equal[column, "color"] => entry.column _ color; Rope.Equal[column, "color"] => entry.column _ static; ENDCASE}; Rope.Equal[token, "iconic"] => IF entry # NIL THEN { [] _ stream.GetChar[]; -- get rid of ':. entry.iconic _ stream.GetBool[]}; Rope.Equal[token, "position"] => IF entry # NIL THEN { [] _ stream.GetChar[]; -- get rid of ':. entry.position _ stream.GetInt[]}; Rope.Equal[token, "openHeight"] => IF entry # NIL THEN { [] _ stream.GetChar[]; -- get rid of ':. entry.height _ stream.GetInt[]}; ENDCASE => NULL; ENDLOOP; FOR entries _ entries, entries.rest WHILE entries # NIL DO AddEntry[desktop, entries.first]; ENDLOOP; FetchDeskTopData[desktop].iconBitmap _ NIL; ViewerOps.PaintViewer[desktop, all]; stream.Close[]; END; WriteFile: PUBLIC PROC [desktop: DeskTop, filename: Rope.ROPE _ NIL] = BEGIN stream: IO.STREAM; IF filename.Length[] = 0 THEN filename _ desktop.name; IF filename.Find["."] < 0 THEN filename _ Rope.Cat[filename, ".desktop"]; stream _ FS.StreamOpen[filename, create]; stream.PutF["[desktop: %g,\n", IO.rope[desktop.name]]; FOR child: Viewer _ desktop.child, child.sibling WHILE child # NIL DO entry: Entry _ FetchEntryData[child]; stream.PutF["[viewer: ""%g"",\n\tclass: $%g, file: ""%g"",\n", IO.rope[entry.name], IO.atom[entry.flavor], IO.rope[entry.backingFile]]; SELECT entry.column FROM left => stream.Put[IO.rope["\tcolumn: left, "]]; right => stream.Put[IO.rope["\tcolumn: right, "]]; color => stream.Put[IO.rope["\tcolumn: color, "]]; static => stream.Put[IO.rope["\tcolumn: static, "]]; ENDCASE => ERROR; stream.PutF["iconic: %g, position: %g, openHeight: %g]", IO.bool[entry.iconic], IO.int[entry.position], IO.int[entry.height]]; stream.Put[IO.rope[IF child.sibling = NIL THEN "];\n\n" ELSE ",\n"]]; ENDLOOP; stream.Close[]; END; <<******* detail procedures *******>> <<>> MoveViewer: PUBLIC PROC[viewer: Viewer, old, new: DeskTop, remove: BOOL] = BEGIN IF viewer = NIL THEN RETURN; IF old = new AND remove THEN RETURN; IF old = NIL AND new = NIL THEN RETURN; IF old # NIL AND old.class.flavor # $DeskTop THEN RETURN; IF new # NIL AND new.class.flavor # $DeskTop THEN RETURN; IF new = current THEN RETURN; -- cannot add viewers WHILE viewer.parent # NIL DO viewer _ viewer.parent; ENDLOOP; IF new # NIL THEN {AddEntry[new, GetEntry[old, viewer].entry]; ViewerOps.PaintViewer[new, all]} ELSE OpenEntry[GetEntry[old, viewer].entry]; IF remove THEN IF old # NIL THEN RemoveViewer[old, viewer] ELSE { -- remove from screen IF ~viewer.iconic THEN { ViewerOps.CloseViewer[viewer, FALSE]; ViewerOps.ComputeColumn[viewer.column]}; ViewerOps.ChangeColumn[viewer, static]}; END; RemoveViewer: PROC[desktop: DeskTop, viewer: Viewer] = BEGIN entry: Entry; button: Buttons.Button; MoveUp: ViewerOps.EnumProc = {IF v.wy > button.wy THEN ViewerOps.MoveViewer[v, v.wx, v.wy-buttonHeight, v.ww, v.wh, FALSE]}; button _ GetEntry[desktop, viewer].button; IF button = NIL THEN RETURN; entry _ FetchEntryData[button]; IF entry.viewer # NIL AND ~entry.viewer.destroyed AND entry.viewer.offDeskTop THEN ViewerOps.ChangeColumn[entry.viewer, entry.column]; ViewerOps.EnumerateChildren[desktop, MoveUp]; ViewerOps.DestroyViewer[button, FALSE]; ViewerOps.PaintViewer[desktop, all]; END; GetEntry: PROC[desktop, v: Viewer] RETURNS[entry: Entry, button: Buttons.Button] = BEGIN IF desktop = NIL THEN { IF v.props = NIL THEN ViewerOps.AddProp[v, $DesktopDummy, $PlaceHolder]; entry _ NEW[EntryRec _ [viewer: v, name: v.name, backingFile: v.file, flavor: v.class.flavor, props: v.props, column: v.column, iconic: v.iconic, position: v.position, height: v.openHeight, wy: v.wy, wh: v.wh]]; RETURN[entry, NIL]}; FOR child: Viewer _ desktop.child, child.sibling WHILE child # NIL DO entry: Entry _ FetchEntryData[child]; IF entry # NIL AND entry.viewer = v THEN RETURN[entry, child]; ENDLOOP; RETURN[NIL, NIL]; END; AddEntry: PUBLIC PROC[desktop: Viewer, entry: Entry, height, limit: INTEGER _ -1] = BEGIN title: Rope.ROPE; button: Buttons.Button; IF desktop = current THEN RETURN; -- cannot add viewers [] _ desktop.class.scroll[desktop, thumb, 0]; title _ Rope.Cat[Atom.GetPName[entry.flavor], ": ", entry.name]; <> IF height < 0 THEN FOR v: Viewer _ desktop.child, v.sibling WHILE v # NIL DO new: Entry _ FetchEntryData[v]; IF new.viewer = entry.viewer AND new.viewer # NIL THEN { ViewerOps.AddProp[v, $Entry, entry]; IF ~Rope.Equal[title, v.name] THEN Buttons.ReLabel[v, title]; RETURN}; IF Rope.Compare[v.name, title] # greater THEN height _ MAX[height, v.wy + buttonHeight]; ENDLOOP; IF height < 0 THEN height _ firstButton; <> FOR v: Viewer _ desktop.child, v.sibling WHILE v # NIL DO IF limit > 0 AND v.wy > limit*buttonHeight THEN {ViewerOps.DestroyViewer[v]; LOOP}; IF v.wy >= height THEN ViewerOps.MoveViewer[v, v.wx, v.wy + buttonHeight, v.ww, v.wh, FALSE]; ENDLOOP; <> button _ Buttons.Create[ info: [name: title, parent: desktop, wy: height], proc: MyButtonProc, paint: FALSE]; ViewerOps.AddProp[button, $Entry, entry]; END; OpenEntry: PROC[e: Entry, all, grow: BOOL _ FALSE] = BEGIN iconic: BOOLEAN _ all AND e.iconic; moved: BOOLEAN _ FALSE; IF e.viewer = NIL OR e.viewer.destroyed THEN { -- find or create a replacement e.viewer _ ViewerOps.FindViewer[e.name]; IF e.viewer # NIL AND e.viewer.class.flavor # e.flavor THEN e.viewer _ NIL; IF e.viewer = NIL THEN { e.viewer _ ViewerOps.CreateViewer[flavor: e.flavor, info: [name: e.name, file: e.backingFile, iconic: iconic, column: e.column], paint: ~all]; FOR props: Atom.PropList _ e.props, props.rest WHILE props # NIL DO IF props.first.key = $DesktopDummy THEN LOOP; ViewerOps.AddProp[e.viewer, NARROW[props.first.key], props.first.val]; ENDLOOP; IF e.viewer.props = NIL THEN ViewerOps.AddProp[e.viewer, $DesktopDummy, $PlaceHolder]; moved _ TRUE}}; <> IF e.viewer.offDeskTop THEN { ViewerOps.ChangeColumn[e.viewer, e.column]; IF ~all THEN iconic _ e.iconic}; IF iconic AND ~e.viewer.iconic THEN { ViewerOps.CloseViewer[e.viewer, ~all]; moved _ TRUE}; IF e.column # e.viewer.column THEN { -- close viewer to avoid repainting the world IF ~e.viewer.iconic AND all THEN ViewerOps.CloseViewer[e.viewer, ~all]; ViewerOps.ChangeColumn[e.viewer, e.column]; moved _ TRUE}; IF ~iconic AND e.viewer.iconic THEN { ViewerOps.OpenIcon[icon: e.viewer, paint: ~all]; moved _ TRUE}; <> IF grow THEN {ViewerOps.GrowViewer[e.viewer]; moved _ TRUE}; <> IF ~all AND ~moved THEN ViewerOps.PaintViewer[e.viewer, all]; END; FetchEntryData: PROC[button: Buttons.Button] RETURNS[entry: Entry] = INLINE { RETURN[NARROW[ViewerOps.FetchProp[button, $Entry]]]}; FetchDeskTopData: PROC[desktop: DeskTop] RETURNS[entry: DeskTopData] = INLINE { RETURN[NARROW[ViewerOps.FetchProp[desktop, $DeskTopData]]]}; FlushEntries: ViewerEvents.EventProc = BEGIN <<[viewer: Viewer, event: ViewerEvent, before: BOOL]>> list: LIST OF Viewer; MakeList: ViewerOps.EnumProc = {list _ CONS[v, list]}; IF event = destroy AND viewer = current THEN current _ NIL; ViewerOps.EnumerateChildren[viewer, MakeList]; FOR list _ list, list.rest WHILE list # NIL DO entry: Entry = FetchEntryData[list.first]; IF entry # NIL AND entry.viewer # NIL AND ~entry.viewer.destroyed AND entry.viewer.offDeskTop THEN ViewerOps.ChangeColumn[entry.viewer, entry.column]; ViewerOps.DestroyViewer[list.first, FALSE]; ENDLOOP; END; Init[]; END.