DeskTopsImpl.mesa
-- 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;
create menu
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];
create new class
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];
invert if selected
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: BOOLTRUE;
context: Graphics.Context;
darkGrey: CARDINAL = 122645B;
lightGrey: CARDINAL = 012024B;
erase icon area
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];
draw outlines of inner boxes
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;
paint in grey areas
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;
draw lines
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];
draw name
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
path: Graphics.Path;
path ← Graphics.NewPath[4];
Graphics.Rectangle[path, x1, y1, x2, y2];
Graphics.DrawStroke[self: context, path: path, closed: TRUE];
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.ROPENIL] 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: BOOLEANTRUE] = 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: BOOLEANTRUE;
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.ROPENIL] = 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];
sort by name if no height is given
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;
move the rest of the buttons down. delete buttons that fall off the bottom (limit).
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;
add the new button
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: BOOLFALSE] = BEGIN
iconic: BOOLEAN ← all AND e.iconic;
moved: BOOLEANFALSE;
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}};
move the viewer to where it should be
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 all AND ~e.viewer.iconic THEN {ViewerOps.TopViewer[e.viewer, ~all]; moved ← TRUE};
IF grow THEN {ViewerOps.GrowViewer[e.viewer]; moved ← TRUE};
if the viewer hasn't been moved, repaint it
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.