DeskTopsImpl.mesa
Copyright © 1983, 1984 Xerox Corporation. All rights reserved.
Last Edited by: Maxwell, December 7, 1983 9:28 am
Doug Wyatt, September 4, 1984 3:19:43 pm PDT
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, Box, Color, Context, FONT, MaskBox, MaskRectangleI, MaskStrokeClosed, MaskVector, SetColor, SetFont, SetStrokeEnd, SetStrokeWidth, SetXYI, ShowRope, white],
ImagerOps USING [ColorFromStipple, ImagerFromGraphics, XOR],
ImagerPath USING [PathProc],
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],
ViewerClasses
USING[Column, InitProc, ModifyProc, NotifyProc, PaintProc, SaveProc,
ViewerClass, ViewerClassRec, ViewerFlavor, Viewer],
ViewerEvents USING [EventProc, RegisterEventProc],
ViewerExtras USING [ImagerFont],
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, IconManager, Imager, ImagerOps, InputFocus, IO, Menus, MessageWindow, Rope, VFonts, ViewerEvents, ViewerExtras, ViewerLocks, ViewerOps, ViewerSpecs, ViewerTools
EXPORTS Desktops
SHARES ViewerOps
= BEGIN OPEN Desktops, ViewerClasses;
DeskTopData: TYPE = REF DeskTopDataRec;
DeskTopDataRec:
TYPE =
RECORD[
iconBitmap: REF ← NIL, -- icon bitmap
openLeftWidth: INTEGER ← 0,
openBottomY: INTEGER ← 0
];
current: Viewer ← NIL;
MRUCache: Viewer ← NIL;
buttonHeight: INTEGER ← 16;
firstButton: INTEGER ← 3;
menu: Menus.Menu ← NIL;
iconFont: Imager.FONT ← ViewerExtras.ImagerFont[VFonts.EstablishFont["TimesRoman", 8]];
******* interface code *******
Init:
PROC = {
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."];
};
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;
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 = {
[self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL] ;
imager: Imager.Context ~ ImagerOps.ImagerFromGraphics[context];
data: DeskTopData;
IF ~self.iconic THEN RETURN;
data ← FetchDeskTopData[self];
IF data.iconBitmap = NIL THEN PaintDeskTopIcon[self, data];
ImagerOps.DrawBitmap[context, data.iconBitmap, 0, 0];
invert if selected
IF IconManager.selectedIcon = self
THEN {
Imager.SetColor[imager, ImagerOps.XOR];
Imager.MaskRectangleI[imager, 0, 0, self.cw, self.ch];
};
};
darkGrey: Imager.Color = ImagerOps.ColorFromStipple[122645B];
lightGrey: Imager.Color = ImagerOps.ColorFromStipple[012024B];
PaintDeskTopIcon:
PROC[self: Viewer, data: DeskTopData] = {
scale: REAL = 16;
box: Imager.Box;
frobX, frobY: REAL;
left, right: BOOL ← TRUE;
context: Imager.Context;
erase icon area
data.iconBitmap ← GraphicsOps.NewBitmap[64, 64];
context ← GraphicsOps.NewContextFromBitmap[data.iconBitmap];
box ← [0, 0, 63, 63];
Imager.SetColor[context, Imager.white];
Imager.MaskBox[context, box];
draw outlines of inner boxes
frobX ← data.openLeftWidth/scale;
frobY ← data.openBottomY/scale + 13;
Imager.SetColor[context, Imager.black];
Imager.SetStrokeWidth[context, 1];
Imager.SetStrokeEnd[context, square];
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
Imager.SetColor[context, lightGrey];
Imager.MaskBox[context, [box.xmin, 13, box.xmax, frobY]];
SELECT
TRUE
FROM
left AND right => Imager.MaskBox[context, [box.xmin, 13, box.xmax, box.ymax]];
left => Imager.MaskBox[context, [box.xmin, 13, frobX-1, box.ymax]];
right => Imager.MaskBox[context, [frobX+1, 13, box.xmax, box.ymax]];
ENDCASE;
draw lines
Imager.SetColor[context, Imager.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];
Imager.MaskVector[context, [box.xmin, 13], [box.xmax, 13]];
draw name
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, ImagerOps.XOR];
Imager.MaskBox[context, [box.xmin+2, 3, box.xmax-2, 12]];
};
};
DrawRectangle:
PROC[context: Imager.Context, x1, y1, x2, y2:
REAL] = {
rectanglePath: ImagerPath.PathProc ~ {
moveTo[[x1, y1]]; lineTo[[x1, y2]]; lineTo[[x2, y2]]; lineTo[[x2, y1]];
};
Imager.MaskStrokeClosed[context, rectanglePath];
};
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 = {
PROC [self: Viewer, change: ModifyAction]
SELECT change
FROM
set => Buttons.SetDisplayStyle[self, $BlackOnGrey];
push, pop, kill => Buttons.SetDisplayStyle[self, $BlackOnWhite];
ENDCASE;
};
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];
};
******* basic procedures *******
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:
BOOLEAN ←
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[
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] = {
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[];
};
******* detail procedures *******
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];
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];
};
OpenEntry:
PROC[e: Entry, all, grow:
BOOL ←
FALSE] = {
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}};
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];
};
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 = {
[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;
};
Init[];
END.