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; 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 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; 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; 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 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. ˆDeskTopsImpl.mesa -- Last Edited by: Maxwell, December 7, 1983 9:28 am ******* interface code ******* create menu create new class [self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL] ; invert if selected erase icon area draw outlines of inner boxes paint in grey areas draw lines draw name path: Graphics.Path; path _ Graphics.NewPath[4]; Graphics.Rectangle[path, x1, y1, x2, y2]; Graphics.DrawStroke[self: context, path: path, closed: TRUE]; ******* basic procedures ******* ******* detail procedures ******* sort by name if no height is given move the rest of the buttons down. delete buttons that fall off the bottom (limit). add the new button move the viewer to where it should be IF all AND ~e.viewer.iconic THEN {ViewerOps.TopViewer[e.viewer, ~all]; moved _ TRUE}; if the viewer hasn't been moved, repaint it [viewer: Viewer, event: ViewerEvent, before: BOOL] Ê“˜Jšœ™JšÏc4™4J˜šÏk ˜ Jšœžœ˜ Jšœžœ8˜EJšœ žœ˜(Jšœ žœ ˜Jšœ ˜ Jšžœžœ˜Jšœ žœx˜†Jšœ žœ:˜KJšœ žœ˜!Jšœ žœ˜!Jšžœžœzžœžœ˜•Jšœžœ<˜GJšœžœ˜$Jšœžœ%žœ˜5Jšœžœ˜+šœžœ@˜SJšœ3˜3—Jšœ žœ ˜2Jšœ žœ#˜4šœ žœC˜RJšœÊ˜Ê—Jšœ žœ˜/Jšœ žœ+˜<—J˜Jšœžœžœ˜Jšžœ'žœ0žœd˜ÈJšžœ ˜Jšžœ ž˜Jšžœ˜J˜Jšœ žœžœ˜'šœžœžœ˜Jšœ$žœ˜7Jšœžœ˜Jšœ žœ˜—J˜J˜J˜Jšœžœ˜Jšœ žœ˜J˜J˜J˜WJšœ žœ˜Jšœ žœ˜J˜Jšœ™J™šÏnœžœž˜J˜6Jšœ ™ J˜J˜DJ˜CJ˜FJšœC˜CJšœD˜DJšœH˜HJšœA˜AJšœC˜CJ˜-Jšœ˜8J˜3Jšœ™Jšœ žœ˜+J˜Jšœ˜J˜J˜J˜J˜Jšœ ˜6J˜1J˜NJ˜EJ˜?J˜TJ˜SJšžœ˜J˜—šÏbœž˜#Jšœ žœ˜Jšœžœžœ˜Jšœ žœžœ˜!J˜$J˜šžœžœž˜Jšœ'žœ˜AJšœ(žœ˜LJšžœ˜—Jšžœ˜J˜—Jš  œ&˜1J˜Jš  œ"žœ žœ ˜LJš  œ"žœ žœ ˜JJš  œ"žœ žœ ˜LJš œ"žœ žœ˜NJš œ"žœ žœ˜NJš  œ"žœ žœ˜RJš œ"žœ žœ ˜HJš  œ"žœ žœ ˜JJ˜š  œ˜&J˜—š  œ˜'Jšœžœ˜-J˜Jšœ˜J˜+J˜/Jšœ,˜,šžœ žœžœ˜'Jšœ˜Jšœžœ˜—J˜J˜—š  œž˜-JšœN™NJ˜Jšžœžœžœ˜Jšœ˜Jšžœžœžœ˜;J˜#JšœM˜MJšœ™šžœ!žœ˜(J˜,J˜8—Jšžœ˜J˜Jšœžœ˜—J˜šŸœžœ$ž˜?Jšœžœ˜J˜Jšœžœ˜Jšœ žœžœ˜Jšœ˜Jšœ žœ ˜Jšœ žœ ˜J™Jšœ0˜0Jšœ<˜˜EJšœžœ>˜FJšžœ˜ —Jšœ ™ J˜+J˜AJ˜GJ˜&J˜'Jšœ ™ J˜JšœB˜Bšžœžœ;˜TJ˜,Jšœ;˜;Jšœ-˜-—Jšžœ˜J˜šŸ œž œ,žœž˜QJšœ™Jšœ™Jšœ)™)Jšœ7žœ™=J˜ J˜!J˜!J˜!J˜!Jšžœ˜——š  œ˜$šžœžœž˜Jšœ#žœ žœ˜GJšœ$žœ žœ˜IJšœžœ žœ˜9Jšžœžœ žœ˜<——J˜š  œ-˜9Jšžœ,˜0Jšžœ/˜3J˜—š  œž˜/šžœžœ žœž˜2šžœ ž˜J˜J˜$Jšœ;˜;JšœEžœ˜KJšœEžœ˜LJšœ<˜Jšœ˜Jšœ˜Jšœ!˜!Jšœžœ ˜7Jšœ(˜(Jšžœ˜J˜—šŸœž œž˜+Jšœ˜Jš  œ5žœ˜HJš  œ&˜0Jš  œžœžœžœ$˜zJ˜Jš žœ žœžœ!žœžœ˜@Jšžœžœžœ˜Ašžœ žœžœ)˜AJ˜Jšœ žœ&˜5J˜Jšœ˜Jšœžœ˜—Jšœ˜$Jšœžœ˜4Jšœ!˜!šžœ0˜2Jšžœ,˜.Jšžœ>˜B—Jšœ2˜2Jšœ1˜1J˜)Jšœ!žœ˜'Jšœžœ ˜7J˜Jšžœ˜J˜—š œžœž˜0š  œ˜$Jšžœžœžœ˜!Jšœžœ ˜+—Jšœ.˜.Jšœ/˜/Jšœ+˜+Jšœ!žœ˜'Jšœ)˜)Jšœžœ ˜7Jšžœ˜J˜—š Ÿ œžœ žœžœž˜5š  œ˜#Jšžœžœžœ˜!Jšžœ žœžœ˜2Jšžœ žœ%˜P—J˜Jšœ(˜(Jšžœ˜J˜—šÐbn œžœžœž˜šŸ œžœž˜š  œ˜!šžœ žœžœžœ˜Jšžœ˜—Jšžœžœžœ˜Jšžœ˜J˜—š Ÿœžœžœ/žœ ž˜YJšœ žœ˜J˜Jšžœžœžœ˜7J˜-Jšœ@˜@J™"š žœ žœžœ&žœžœž˜LJšœ˜šžœžœžœžœ˜8Jšœ%˜%Jšžœžœ˜=Jšžœ˜—šžœ'˜)Jšžœ žœ˜/—Jšžœ˜—Jšžœ žœ˜(J™Tšžœ&žœžœž˜9Jšžœ žœžœžœ˜SJšžœžœ@žœ˜]Jšžœ˜—J™šœ˜Jšœ1˜1Jšœžœ˜"—Jšœ)˜)Jšžœ˜J˜—š ¡ œžœžœžœž˜:Jšœžœžœ ˜#Jšœžœžœ˜š žœ žœžœžœ˜NJ˜(Jš žœ žœžœ"žœ žœ˜Kšžœ žœžœ˜JšœŽ˜Žšžœ,žœ žœž˜CJšžœ!žœžœ˜-Jšœžœ$˜FJšžœ˜—Jšžœžœžœ:˜VJšœžœ˜——Jšœ%™%šžœžœ˜Jšœ+˜+Jšžœžœ˜!—šžœžœžœ˜%Jšœ0žœ˜6—šžœžœ-˜RJšžœžœžœ'˜GJšœ4žœ˜:—šžœ žœžœ˜&Jšœ9žœ˜?—Jšžœžœžœ/žœ™UJšžœžœ*žœ˜