DIRECTORY Atom USING [GetPName, PropList], Buttons USING [Button, ButtonProc, Create, ReLabel, SetDisplayStyle], Commander USING [CommandProc, Register], Containers USING [Create], Desktops USING [DeskTop, Entry, EntryRec, EnumProc], FS USING [Error, StreamOpen], Imager USING [black, Color, Context, Font, MaskRectangleI, SetColor, SetFont, SetXYI, ShowRope, white], ImagerBackdoor USING [Bitmap, BitmapContext, DrawBits, invert, MakeStipple, NewBitmap], InputFocus USING [SetInputFocus], IO USING [atom, bool, Close, EndOf, EndOfStream, GetAtom, GetBool, GetChar, GetInt, GetRopeLiteral, GetTokenRope, IDProc, int, Put, PutF, RIS, rope, STREAM], List USING [Assoc, PutAssoc], Menus USING [AppendMenuEntry, CreateEntry, CreateMenu, Menu, MenuProc], MessageWindow USING [Append, Blink], ProcessProps USING [GetProp], 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, List, Menus, MessageWindow, ProcessProps, Rope, VFonts, ViewerEvents, ViewerLocks, ViewerOps, ViewerSpecs, ViewerTools EXPORTS Desktops SHARES ViewerOps = { DeskTopData: TYPE = REF DeskTopDataRec; DeskTopDataRec: TYPE = RECORD[ iconBitmap: ImagerBackdoor.Bitmap _ NIL, -- icon bitmap openLeftWidth: INTEGER _ 0, openBottomY: INTEGER _ 0 ]; current: ViewerClasses.Viewer; MRUCache: ViewerClasses.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[ViewerClasses.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.", $CreateDeskTop]; Commander.Register["ReadDesktop", DoIt, "Reads a desktop from a file on the disk.", $ReadDeskTop]; Commander.Register["WriteDesktop", DoIt, "Overwrites a desktop file on the disk.", $WriteDeskTop]; }; DoIt: Commander.CommandProc = { token: Rope.ROPE; stream: IO.STREAM; stream _ IO.RIS[cmd.commandLine]; token _ stream.GetTokenRope[IO.IDProc].token; stream.Close[]; SELECT cmd^.procData^.clientData FROM $ReadDeskTop => ReadFile[token]; $WriteDeskTop => 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: ViewerClasses.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: ViewerClasses.Viewer _ self.child, child.sibling WHILE child # NIL DO entry: Desktops.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: ViewerClasses.Viewer] = { viewer _ ViewerTools.GetSelectedViewer[]; IF viewer = NIL THEN RETURN; IF viewer.class.flavor = $Button THEN { entry: Desktops.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[Desktops.DeskTop] = { viewer: ViewerClasses.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: ViewerClasses.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]]; desktop.props _ List.PutAssoc[$DeskTopWorkingDirectory, ProcessProps.GetProp[$WorkingDirectory], desktop.props]; }; IF desktop.offDeskTop THEN ViewerOps.ChangeColumn[desktop, right]; }; Rename: PUBLIC PROC[desktop: Desktops.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: ViewerClasses.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: Desktops.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: ViewerClasses.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: Desktops.DeskTop, proc: Desktops.EnumProc] = { child: ViewerClasses.Viewer _ desktop.child; WHILE child # NIL DO next: ViewerClasses.Viewer _ child.sibling; IF ~proc[FetchEntryData[child]] THEN EXIT; child _ next; ENDLOOP; }; SetHeight: PROC[button: ViewerClasses.Viewer] = INLINE { e: Desktops.Entry _ FetchEntryData[button]; IF e.iconic THEN RETURN; e.viewer.position _ e.position; e.viewer.openHeight _ e.height; }; UneditedDocument: PROC[v: ViewerClasses.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: Desktops.Entry; desktop: Desktops.DeskTop; stream: IO.STREAM; entries: LIST OF Desktops.Entry; IF filename.Find["."] < 0 THEN filename _ Rope.Cat[filename, ".desktop"]; stream _ FS.StreamOpen[fileName: filename, accessOptions: read, wDir: NARROW[ProcessProps.GetProp[$WorkingDirectory], Rope.ROPE]]; WHILE ~stream.EndOf[] DO token: Rope.ROPE _ stream.GetTokenRope[! IO.EndOfStream => EXIT].token; SELECT TRUE FROM Rope.Equal[token, "desktop"] => { desktop _ Create[stream.GetTokenRope[IO.IDProc].token]; [] _ FlushEntries[desktop, save, TRUE] }; Rope.Equal[token, "viewer"] => { IF desktop = NIL THEN LOOP; entry _ NEW[Desktops.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: Desktops.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: filename, accessOptions: create, wDir: NARROW[List.Assoc[$DeskTopWorkingDirectory, desktop.props], Rope.ROPE]]; stream.PutF["[desktop: %g,\n", IO.rope[desktop.name]]; FOR child: ViewerClasses.Viewer _ desktop.child, child.sibling WHILE child # NIL DO entry: Desktops.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]]; IF child.sibling # NIL THEN stream.Put[IO.rope[",\n"]]; ENDLOOP; stream.Put[IO.rope["];\n\n"]]; stream.Close[]; }; MoveViewer: PUBLIC PROC[viewer: ViewerClasses.Viewer, old, new: Desktops.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: Desktops.DeskTop, viewer: ViewerClasses.Viewer] = { entry: Desktops.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: ViewerClasses.Viewer] RETURNS[entry: Desktops.Entry, button: Buttons.Button] = { IF desktop = NIL THEN { IF v.props = NIL THEN ViewerOps.AddProp[v, $DesktopDummy, $PlaceHolder]; entry _ NEW[Desktops.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: ViewerClasses.Viewer _ desktop.child, child.sibling WHILE child # NIL DO entry: Desktops.Entry _ FetchEntryData[child]; IF entry # NIL AND entry.viewer = v THEN RETURN[entry, child]; ENDLOOP; RETURN[NIL, NIL]; }; AddEntry: PUBLIC PROC[desktop: ViewerClasses.Viewer, entry: Desktops.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: ViewerClasses.Viewer _ desktop.child, v.sibling WHILE v # NIL DO new: Desktops.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: ViewerClasses.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: Desktops.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: Desktops.Entry] = INLINE { RETURN[NARROW[ViewerOps.FetchProp[button, $Entry]]] }; FetchDeskTopData: PROC[desktop: Desktops.DeskTop] RETURNS[entry: DeskTopData] = INLINE { RETURN[NARROW[ViewerOps.FetchProp[desktop, $DeskTopData]]] }; FlushEntries: ViewerEvents.EventProc = { list: LIST OF ViewerClasses.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: Desktops.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[]; }. DDeskTopsImpl.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Peter Kessler May 15, 1986 10:57:22 am PDT Doug Wyatt, April 15, 1985 5:50:45 pm PST Russ Atkinson (RRA) August 19, 1985 8:26:12 pm PDT 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] Κ/˜codešœ™Kšœ Οmœ7™BKšœ*™*Kšœ)™)Kšœ2™2—K˜šΟk ˜ Kšœžœ˜ Kšœžœ8˜EKšœ žœ˜(Kšœ žœ ˜Kšœ žœ&˜4Kšžœžœ˜Kšœžœ[˜gKšœžœC˜WKšœ žœ˜!Kšžœžœ‚žœžœ˜Kšœžœ˜Kšœžœ<˜GKšœžœ˜$Kšœ žœ ˜Kšœžœ%žœ˜5Kšœžœ˜#Kšœžœs˜†Kšœ žœ ˜2Kšœ žœ#˜4Kšœ žœ˜œKšœ žœ˜/Kšœ žœ+˜<—K˜šΠln œžœžœ˜Kšžœ'žœ&žœx˜ΠKšžœ ˜Kšžœ ˜Kšœ˜Kšœ žœžœ˜'šœžœžœ˜Kšœ$žœΟc˜7Kšœžœ˜Kšœ žœ˜K˜—K˜K˜KšΟnœ˜Kšœžœ˜Kšœ žœ˜K˜K˜K˜>Iheadšœ™š‘œžœ˜K˜6šœ ™ K˜K˜DK˜CK˜FK˜CK˜DK˜HK˜AK˜CK˜-Kšœ ˜8K˜3—šœ™Kšœ žœ,˜9K˜K˜K˜K˜K˜K˜Kšœ  ˜6K˜1—K˜NK˜EK˜OK˜bK˜bK˜—K˜š‘œ˜Kšœ žœ˜Kšœžœžœ˜Kšœ žœžœ˜!Kšœžœ˜-K˜šžœž˜%K˜ K˜+Kšžœ˜—K˜—K˜Kš‘ œ&˜1Kš‘ œ"žœ žœ ˜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˜K˜+K˜K˜K˜K˜K˜#—Kšœ™šžœ(žœ˜0K˜0K˜-K˜—K˜—K˜K˜=K˜>š‘œžœ3˜IKšœžœ˜Kš‘œžœžœžœžœžœžœ ˜@Kš‘œžœžœžœžœžœžœ˜DKšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœžœ˜"K˜Kšœ™K˜1K˜8K˜'K˜+Kšœ™K˜!K˜K˜'šžœ9žœ žœž˜PKšœ.˜.Kš žœ žœžœžœžœ˜)šžœž˜˜ K˜K˜)Kšžœ žœžœžœ˜šžœžœ˜'Kšœ/˜/Kšžœ žœžœ˜)K˜—Kšžœ žœžœžœ˜Kšžœžœžœžœ˜=K˜—K˜š‘œžœžœ˜3K˜?šžœ žœž˜Kšžœ žœžœ ˜6K˜Kšžœ˜—Kšžœžœ˜ K˜—Lšœ™š ‘œžœžœ žœžœžœ#˜Uš‘ œ˜#Kšžœžœ˜:Kšžœžœžœ˜ K˜—K˜Kšžœžœ˜+K˜(šžœ žœžœ4˜IKšœ/žœ˜6—šžœ žœžœ˜Kšœ]žœ˜dK˜pK˜—Kšžœžœ(˜BK˜—K˜š‘œžœžœ'žœ˜CK˜K˜K˜!Kšœžœ  ˜7K˜(K˜—K˜š‘œžœžœ#˜5K˜Kš‘ œ5žœ˜HKš‘ œ&˜0Kš‘ œžœžœžœ$˜zK˜Kš žœ žœžœ!žœžœ˜@Kšžœžœžœ˜Ašžœ žœžœ )˜AK˜!Kšœ žœ &˜5K˜K˜Kšœžœ˜K˜—Kšœ ˜$Kšœžœ ˜4K˜!Kšžœ0˜2Kšžœ,˜.Kšžœ>˜BK˜2K˜1K˜)Kšœ!žœ˜'Kšœžœ  ˜7K˜K˜—K˜š‘œžœ#˜:š‘ œ˜$Kšžœžœžœ˜!Kšœžœ ˜)K˜—K˜.K˜/K˜+Kšœ!žœ˜'K˜)Kšœžœ  ˜7K˜—K˜š‘ œžœ žœžœ˜.š‘ œ˜#Kšžœžœžœ˜!Kšžœ žœžœ˜2Kšžœ žœ"˜4Kšœ ˜—K˜K˜(K˜—K˜š‘ œžœžœ˜š‘ œžœ˜š‘ œ˜!šžœ žœžœžœ˜Kšžœ˜—Kšžœžœžœ˜K˜—K˜š‘œžœžœFžœ ˜lKšœ žœ˜K˜Kšžœžœžœ ˜7K˜-K˜@Kšœ"™"š žœ žœžœ4žœžœž˜ZKšœ(˜(šžœžœžœžœ˜8K˜%Kšžœžœ˜=Kšž˜K˜—Kšžœ'˜)Kšžœ žœ˜/Kšžœ˜—Kšžœ žœ˜(KšœT™Tšžœ4žœžœž˜GKšžœ žœžœžœ˜SKšžœžœ@žœ˜]Kšžœ˜—Kšœ™˜K˜1Kšœžœ˜"—K˜)K˜—K˜š‘ œžœžœžœ˜?Kšœžœžœ ˜ Kšœžœžœ˜š žœ žœžœžœ ˜NK˜(Kš žœ žœžœ"žœ žœ˜Kšžœ žœžœ˜K˜Žšžœ,žœ žœž˜CKšžœ!žœžœ˜-Kšœžœ$˜FKšžœ˜—Kšžœžœžœ:˜VKšœž˜ K˜—K˜—Kšœ%™%šžœžœ˜K˜+Kšžœžœ˜K˜—šžœžœžœ˜%Kšœ0ž˜4K˜—šžœžœ -˜RKšžœžœžœ'˜GKšœ4ž˜8K˜—šžœ žœžœ˜&Kšœ9ž˜=K˜—KšœU™UKšžœžœ*žœ˜