DIRECTORY Atom USING [GetPName, PropList], Buttons USING [Button, ButtonProc, Create, ReLabel, SetDisplayStyle], Commander USING [CommandProc, Register], Containers USING [Create], Desktops, FS USING [Error, StreamOpen], Imager, ImagerBackdoor, 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, Font], 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, Imager, ImagerBackdoor, 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: ImagerBackdoor.Bitmap _ NIL, -- icon bitmap openLeftWidth: INTEGER _ 0, openBottomY: INTEGER _ 0 ]; current: Viewer; MRUCache: Viewer; buttonHeight: INTEGER _ 16; firstButton: INTEGER _ 3; menu: Menus.Menu; iconFont: VFonts.Font _ VFonts.EstablishFont["TimesRoman", 8]; Init: PROC = { 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."]; }; DoIt: Commander.CommandProc = { 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]; }; 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 _ NIL; 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 = { data: DeskTopData; IF NOT self.iconic THEN RETURN; data _ FetchDeskTopData[self]; IF data.iconBitmap = NIL THEN PaintDeskTopIcon[self, data]; ImagerBackdoor.DrawBits[context: context, base: data.iconBitmap.base, wordsPerLine: data.iconBitmap.wordsPerLine, sMin: 0, fMin: 0, sSize: data.iconBitmap.height, fSize: data.iconBitmap.width, tx: 0, ty: data.iconBitmap.height]; IF ViewerTools.GetSelectedViewer[] = self THEN { Imager.SetColor[context, ImagerBackdoor.invert]; Imager.MaskRectangleI[context, 0, 0, 64, 64]; }; }; darkGrey: Imager.Color ~ ImagerBackdoor.MakeStipple[122645B]; lightGrey: Imager.Color ~ ImagerBackdoor.MakeStipple[012024B]; PaintDeskTopIcon: PROC[self: Viewer, data: DeskTopData] = { bot: INTEGER ~ 13; MapX: PROC [x: NAT] RETURNS [NAT] ~ INLINE { RETURN[(x+8)/16] }; MapY: PROC [y: NAT] RETURNS [NAT] ~ INLINE { RETURN[bot+(y+8)/16] }; w: INTEGER ~ 64; h: INTEGER ~ 64; frobX, frobY: INTEGER; leftOpen, rightOpen: BOOL _ FALSE; context: Imager.Context; data.iconBitmap _ ImagerBackdoor.NewBitmap[w, h]; context _ ImagerBackdoor.BitmapContext[data.iconBitmap]; Imager.SetColor[context, Imager.white]; Imager.MaskRectangleI[context, 0, 0, w, h]; frobX _ MapX[data.openLeftWidth]; frobY _ MapY[data.openBottomY]; Imager.SetColor[context, Imager.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 => { Imager.MaskRectangleI[context, 0, MapY[entry.wy], frobX, 1]; leftOpen _ TRUE; }; right => { Imager.MaskRectangleI[context, frobX, MapY[entry.wy], w-frobX, 1]; rightOpen _ TRUE; }; ENDCASE; ENDLOOP; Imager.SetColor[context, lightGrey]; Imager.MaskRectangleI[context, 0, bot, w, frobY-bot]; SELECT TRUE FROM leftOpen AND rightOpen => NULL; leftOpen => Imager.MaskRectangleI[context, frobX, bot, w-frobX, h-bot]; rightOpen => Imager.MaskRectangleI[context, 0, bot, frobX, h-bot]; ENDCASE => Imager.MaskRectangleI[context, 0, bot, w, h-bot]; Imager.SetColor[context, Imager.black]; IF leftOpen OR rightOpen THEN Imager.MaskRectangleI[context, frobX, frobY, 1, h-frobY]; Imager.MaskRectangleI[context, 0, bot, w, -1]; Imager.MaskRectangleI[context, 0, 0, w, 2]; Imager.MaskRectangleI[context, 0, 0, 2, h]; Imager.MaskRectangleI[context, w, h, -w, -2]; Imager.MaskRectangleI[context, w, h, -2, -h]; Imager.SetXYI[context, 3, 4]; Imager.SetFont[context, iconFont]; Imager.ShowRope[context, self.name]; IF self = current THEN { -- invert name region to indicate it is the current desktop Imager.SetColor[context, ImagerBackdoor.invert]; Imager.MaskRectangleI[context, 2, 2, w-4, bot-1-2]; }; }; 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 = { 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; }; SelectedViewer: PROC RETURNS[viewer: Viewer] = { 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; }; SelectedDeskTop: PROC RETURNS[DeskTop] = { viewer: Viewer _ ViewerTools.GetSelectedViewer[]; WHILE viewer # NIL DO IF viewer.class.flavor = $DeskTop THEN RETURN[viewer]; viewer _ viewer.parent; ENDLOOP; RETURN[NIL]; }; Create: PUBLIC PROC[name: Rope.ROPE _ NIL] RETURNS[desktop: Viewer] = { 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]; }; Rename: PUBLIC PROC[desktop: DeskTop, name: Rope.ROPE] = { data: DeskTopData; desktop.name _ name; data _ FetchDeskTopData[desktop]; data.iconBitmap _ NIL; -- so new bitmap will be created ViewerOps.PaintViewer[desktop, caption]; }; FlyTo: PUBLIC PROC[desktop: Viewer] = { 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[]; }; RecordScreenState: PROC[desktop: Viewer] = { 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 }; ClearScreen: PROC[offDeskTop: BOOL _ TRUE] = { 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]; }; ClearIcons: PUBLIC PROC = { LockedClear: PROC = { 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]; }; 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]; }; EnumerateEntries: PUBLIC PROC[desktop: DeskTop, proc: EnumProc] = { child: Viewer _ desktop.child; WHILE child # NIL DO next: Viewer _ child.sibling; IF ~proc[FetchEntryData[child]] THEN EXIT; child _ next; ENDLOOP; }; SetHeight: PROC[button: Viewer] = INLINE { e: Entry _ FetchEntryData[button]; IF e.iconic THEN RETURN; e.viewer.position _ e.position; e.viewer.openHeight _ e.height; }; UneditedDocument: PROC[v: Viewer] RETURNS[BOOL] = INLINE { RETURN[v # NIL AND v.class.flavor = $Text AND ~v.newVersion AND ~v.newFile]}; continue: BOOL _ TRUE; ReadFile: PUBLIC PROC [filename: Rope.ROPE] = { 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[]; }; WriteFile: PUBLIC PROC [desktop: DeskTop, filename: Rope.ROPE _ NIL] = { 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[]; }; MoveViewer: PUBLIC PROC[viewer: Viewer, old, new: DeskTop, remove: BOOL] = { 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]}; }; RemoveViewer: PROC[desktop: DeskTop, viewer: Viewer] = { 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]; }; GetEntry: PROC[desktop, v: Viewer] RETURNS[entry: Entry, button: Buttons.Button] = { 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]; }; AddEntry: PUBLIC PROC[desktop: Viewer, entry: Entry, height, limit: INTEGER _ -1] = { 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]; }; OpenEntry: PROC[e: Entry, all, grow: BOOL _ FALSE] = { iconic: BOOL _ all AND e.iconic; moved: BOOL _ 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]; }; 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 = { 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; }; Init[]; END. DDeskTopsImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Last Edited by: Maxwell, December 7, 1983 9:28 am Doug Wyatt, April 15, 1985 5:50:45 pm PST ******* interface code ******* create menu create new class [self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL]; invert if selected erase icon area draw outlines of inner boxes paint in grey areas draw name ******* 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] ÊT˜codešœ™Kšœ Ïmœ1™K˜Kšœ™K™šÏnœžœ˜K˜6Kšœ ™ K˜K˜DK˜CK˜FKšœC˜CKšœD˜DKšœH˜HKšœA˜AKšœC˜CK˜-Kšœ ˜8K˜3Kšœ™Kšœ žœ˜+K˜Kšœ˜K˜K˜K˜K˜Kšœ  ˜6K˜1K˜NK˜EK˜?K˜TK˜SKšœ˜K˜—š¡œ˜Kšœ žœ˜Kšœžœžœ˜Kšœ žœžœ˜!K˜$K˜šžœžœž˜Kšœ'žœ˜AKšœ(žœ˜LKšžœ˜—Kšœ˜K˜—Kš¡ œ&˜1K˜Kš¡ œ"žœ žœ ˜LKš¡ œ"žœ žœ ˜JKš¡ œ"žœ žœ ˜LKš¡œ"žœ žœ˜NKš¡œ"žœ žœ˜NKš¡ œ"žœ žœ˜RKš¡œ"žœ žœ ˜HKš¡ œ"žœ žœ ˜JK˜š¡ œžœ˜,K˜—š¡ œ˜'Kšœžœ˜-K˜Kšœ˜K˜+K˜/Kšœ,˜,šžœ žœžœ ˜'Kšœ˜Kšœžœ˜Kšœ˜—K˜K˜—š¡ œ˜)KšœK™KK˜Kšžœžœ žœžœ˜Kšœ˜Kšžœžœžœ˜;Kšœç˜çKšœ™šžœ(žœ˜0Kšœ0˜0K˜-K˜—Kšœ˜K˜—Kšœ=˜=Kšœ>˜>K˜š¡œžœ%˜;Kšœžœ˜Kš¡œžœžœžœžœžœžœ ˜@Kš¡œžœžœžœžœžœžœ˜DKšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœžœ˜"Kšœ˜K™Kšœ1˜1Kšœ8˜8Kšœ'˜'K˜+Kšœ™Kšœ!˜!Kšœ˜K˜'šžœ+žœ žœž˜BKšœ%˜%Kš žœ žœžœžœžœ˜)šžœž˜šœ ˜ Kšœ<˜˜B—Kšœ2˜2Kšœ1˜1K˜)Kšœ!žœ˜'Kšœžœ  ˜7K˜Kšœ˜K˜—š¡œžœ˜,šœ$˜$Kšžœžœžœ˜!Kšœžœ ˜+—Kšœ.˜.Kšœ/˜/Kšœ+˜+Kšœ!žœ˜'Kšœ)˜)Kšœžœ  ˜7Kšœ˜K˜—š¡ œžœ žœžœ˜.šœ#˜#Kšžœžœžœ˜!Kšžœ žœžœ˜2Kšžœ žœ% ˜P—K˜Kšœ(˜(Kšœ˜K˜—š¡ œžœžœ˜š¡ œžœ˜šœ!˜!šžœ žœžœžœ˜Kšžœ˜—Kšžœžœžœ˜Kšœ˜K˜—š¡œžœžœ/žœ ˜UKšœ žœ˜K˜Kšžœžœžœ ˜7K˜-Kšœ@˜@K™"š žœ žœžœ&žœžœž˜LKšœ˜šžœžœžœžœ˜8Kšœ%˜%Kšžœžœ˜=Kšžœ˜—šžœ'˜)Kšžœ žœ˜/—Kšžœ˜—Kšžœ žœ˜(K™Tšžœ&žœžœž˜9Kšžœ žœžœžœ˜SKšžœžœ@žœ˜]Kšžœ˜—K™šœ˜Kšœ1˜1Kšœžœ˜"—Kšœ)˜)Kšœ˜K˜—š¡ œžœžœžœ˜6Kšœžœžœ ˜ Kšœžœžœ˜š žœ žœžœžœ ˜NK˜(Kš žœ žœžœ"žœ žœ˜Kšžœ žœžœ˜KšœŽ˜Žšžœ,žœ žœž˜CKšžœ!žœžœ˜-Kšœžœ$˜FKšžœ˜—Kšžœžœžœ:˜VKšœžœ˜——Kšœ%™%šžœžœ˜Kšœ+˜+Kšžœžœ˜!—šžœžœžœ˜%Kšœ0žœ˜6—šžœžœ -˜RKšžœžœžœ'˜GKšœ4žœ˜:—šžœ žœžœ˜&Kšœ9žœ˜?—KšœU™UKšžœžœ*žœ˜