DeskTopsImpl.mesa
-- Last Edited by: Maxwell, February 15, 1983 10:28 am
DIRECTORY
Buttons USING [ButtonProc, Create],
Containers USING [Create],
DeskTops USING [],
Graphics USING [black, Box, Context, DrawBox, DrawRope, DrawStroke, DrawTo, FontRef, GetBounds, NewPath, Path, Rectangle, SetColor, SetCP, SetPaintMode, SetStipple, white],
GraphicsOps USING [BitmapRef, DrawBitmap, NewBitmap, NewContextFromBitmap],
IconManager USING [selectedIcon],
Menus USING [AppendMenuEntry, CreateEntry, CreateMenu, Menu, MenuProc],
Rope USING [Cat, Equal, Length, ROPE],
VFonts USING [EstablishFont, GraphicsFont],
ViewerClasses USING[Column, InitProc, 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, TopViewer],
ViewerSpecs USING [openLeftWidth, openBottomY],
ViewerTools USING [GetSelectedViewer, GetSelectionContents];
DeskTopsImpl: CEDAR MONITOR
IMPORTS Buttons, Containers, Graphics, GraphicsOps, IconManager, Menus, Rope, VFonts, ViewerEvents, ViewerLocks, ViewerOps, ViewerSpecs, ViewerTools
EXPORTS DeskTops
SHARES ViewerOps = BEGIN
OPEN ViewerClasses;
ViewerSetData: TYPE = REF ViewerSetDataRec;
ViewerSetDataRec: TYPE = RECORD[
iconBitmap: GraphicsOps.BitmapRef ← NIL, -- icon bitmap
openLeftWidth: INTEGER ← 0,
openBottomY: INTEGER ← 0,
cacheLimit: INTEGER ← 0];
Entry: TYPE = REF EntryRec;
EntryRec: TYPE = RECORD[
name: Rope.ROPE,
flavor: ViewerClasses.ViewerFlavor,
viewer: ViewerClasses.Viewer,
column: ViewerClasses.Column,
iconic: BOOLEAN,
position: INTEGER,
height: INTEGER,
wy, wh: INTEGER];
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;
******* initialization and menu *******
Init: PROC = BEGIN
desktop, container: 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["AddSelected", MenuAddViewer]];
create new class
container ← ViewerOps.FetchViewerClass[$Container];
desktop ← NEW[ViewerClassRec ← container^];
desktop.flavor ← $DeskTop;
desktop.notify ← ClassNotify;
desktop.init ← ClassInit;
desktop.paint ← ClassPaint;
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];
END;
ButtonClean: Buttons.ButtonProc = {ClearIcons[]};
MenuCreate: Menus.MenuProc = {[] ← Create[]};
MenuFlyTo: Menus.MenuProc = {FlyTo[NARROW[parent]]};
MenuRename: Menus.MenuProc = {
data: ViewerSetData;
desktop: Viewer = NARROW[parent];
desktop.name ← ViewerTools.GetSelectionContents[];
data ← NARROW[ViewerOps.FetchProp[desktop, $ViewerSetData]];
data.iconBitmap ← NIL; -- so new bitmap will be created
ViewerOps.PaintViewer[desktop, caption]};
MenuAddViewer: Menus.MenuProc = {
MoveViewer[ViewerTools.GetSelectedViewer[], NARROW[parent]]};
ContainerInit: ViewerClasses.InitProc;
ClassInit: ViewerClasses.InitProc = {
data: ViewerSetData ← NEW[ViewerSetDataRec ← []];
ContainerInit[self];
self.icon ← private;
data.openBottomY ← ViewerSpecs.openBottomY;
data.openLeftWidth ← ViewerSpecs.openLeftWidth;
ViewerOps.AddProp[self, $ViewerSetData, data];
IF current = NIL THEN MakeCurrent[desktop: self]};
ClassPaint: ViewerClasses.PaintProc = BEGIN
[self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL] ;
data: ViewerSetData;
IF ~self.iconic THEN RETURN;
data ← NARROW[ViewerOps.FetchProp[self, $ViewerSetData]];
IF data.iconBitmap = NIL THEN PaintDeskTop[self, data];
Graphics.SetCP[context, 0, 63];
GraphicsOps.DrawBitmap[context, data.iconBitmap, 64, 64];
invert if selected
IF IconManager.selectedIcon = self THEN{
[] ← Graphics.SetPaintMode[context, invert];
Graphics.DrawBox[context, Graphics.GetBounds[context]]};
END;
PaintDeskTop: PROC[self: Viewer, data: ViewerSetData] = 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];
Graphics.SetColor[context, Graphics.white];
Graphics.DrawBox[context, [box.xmin, box.ymin, box.xmax, box.ymax]];
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 ← NARROW[ViewerOps.FetchProp[child, $Entry]];
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, 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, 3];
Graphics.DrawRope[self: context, rope: self.name, font: iconFont];
IF self = current THEN { -- invert name region
[] ← Graphics.SetPaintMode[context, invert];
Graphics.DrawBox[context, [box.xmin+2, 2, 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];
END;
ClassNotify: ViewerClasses.NotifyProc = BEGIN
FOR input ← input, input.rest WHILE input # NIL DO
SELECT input.first FROM
$OpenDesktop => FlyTo[self];
$SaveDesktop => Save[self];
$ResetDesktop => Reset[];
ENDCASE;
ENDLOOP;
END;
******* basic procedures *******
ClearIcons: PUBLIC PROC = BEGIN
list: LIST OF Viewer;
LockedClear: PROC = BEGIN
AddIconic: ViewerOps.EnumProc = {
IF v.iconic AND UneditedDocument[v] THEN list ← CONS[v, list]};
ViewerOps.EnumerateViewers[AddIconic];
AddViewers[MRUCache, list];
FOR list ← list, list.rest WHILE list # NIL DO
ViewerOps.DestroyViewer[list.first];
ENDLOOP;
END;
IF MRUCache = NIL OR MRUCache.destroyed THEN {
MRUCache ← Containers.Create[info: [name: "MRU Cache", column: right, iconic: TRUE]];
ViewerOps.AddProp[MRUCache, $ViewerSetData,
NEW[ViewerSetDataRec ← [cacheLimit: 25]]]};
IF MRUCache.offDeskTop THEN ViewerOps.ChangeColumn[MRUCache, right];
ViewerLocks.CallUnderViewerTreeLock[LockedClear ! ViewerLocks.Wedged => CONTINUE];
END;
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 desktop = NIL THEN desktop ← ViewerOps.CreateViewer[flavor: $DeskTop,
info: [name: name, column: right, iconic: TRUE]];
IF desktop.offDeskTop THEN ViewerOps.ChangeColumn[desktop, right];
END;
Save: PROC[desktop: Viewer] = BEGIN
LockedSave: PROC = {
AddToList: ViewerOps.EnumProc = {
IF v.column # static THEN list ← CONS[v, list]};
data: ViewerSetData;
list: LIST OF Viewer;
data ← NARROW[ViewerOps.FetchProp[desktop, $ViewerSetData]];
data.openLeftWidth ← ViewerSpecs.openLeftWidth;
data.openBottomY ← ViewerSpecs.openBottomY;
ViewerOps.EnumerateViewers[AddToList];
[] ← EraseDeskTop[desktop, save, TRUE];
AddViewers[desktop, list];
data.iconBitmap ← NIL; -- so new bitmap will be created
};
IF desktop = NIL OR desktop.class.flavor # $DeskTop THEN RETURN;
ViewerLocks.CallUnderViewerTreeLock[LockedSave ! ViewerLocks.Wedged => CONTINUE];
END;
FlyTo: PUBLIC PROC[desktop: Viewer] = BEGIN
LockedFlyTo: PROC = {
OpenItems: ViewerOps.EnumProc = {OpenItem[button: v, all: TRUE]};
SetHeights: ViewerOps.EnumProc = {SetHeight[v]};
data: ViewerSetData = NARROW[ViewerOps.FetchProp[desktop, $ViewerSetData]];
MakeCurrent[desktop];
-- ViewerOps.GreyScreen[0, 0, 9999, 9999, FALSE, FALSE];
IF data # NIL THEN IF data.openLeftWidth # ViewerSpecs.openLeftWidth
OR data.openBottomY # ViewerSpecs.openBottomY
THEN ViewerOps.MoveBoundary[data.openLeftWidth, data.openBottomY];
ViewerOps.EnumerateChildren[desktop, OpenItems];
ViewerOps.EnumerateChildren[desktop, SetHeights]};
IF desktop = NIL OR desktop.class.flavor # $DeskTop THEN RETURN;
ViewerLocks.CallUnderViewerTreeLock[LockedFlyTo ! ViewerLocks.Wedged => CONTINUE];
ViewerOps.PaintEverything[];
END;
MakeCurrent: ENTRY PROC[desktop: Viewer] = BEGIN
data: ViewerSetData;
old: Viewer = current;
IF old = desktop THEN {ClearScreen[offDeskTop: FALSE]; RETURN};
current ← desktop;
data ← NARROW[ViewerOps.FetchProp[current, $ViewerSetData]];
data.iconBitmap ← NIL; -- so new bitmap will be created
IF old = NIL THEN RETURN;
Save[old];
ClearScreen[offDeskTop: TRUE];
IF old.iconic THEN ViewerOps.PaintViewer[old, all]; -- Save changed the icon
END;
ClearScreen: PROC[offDeskTop: BOOLEANTRUE] = BEGIN
MoveOffDeskTop: ViewerOps.EnumProc = {
IF v.column = static THEN RETURN;
IF ~v.iconic THEN ViewerOps.CloseViewer[v, FALSE];
IF ~offDeskTop THEN RETURN;
IF v.class.flavor = $DeskTop THEN RETURN;
-- IF v.class.flavor = $Clock THEN RETURN;
-- IF v.class.flavor = $FileTool THEN RETURN;
-- IF Rope.Equal[v.name, "EditTool"] THEN RETURN;
-- IF Rope.Equal[v.name, "Watch"] THEN RETURN;
IF Rope.Equal[v.name, "Work Area A: Executive"] THEN RETURN;
ViewerOps.ChangeColumn[v, static]};
ViewerOps.EnumerateViewers[MoveOffDeskTop];
END;
EraseDeskTop: 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 = NARROW[ViewerOps.FetchProp[list.first, $Entry]];
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;
******* detail procedures *******
MoveViewer: PUBLIC PROC[viewer, desktop: Viewer] = BEGIN
column: ViewerClasses.Column;
IF viewer = NIL OR desktop = NIL THEN RETURN;
WHILE viewer.parent # NIL DO viewer ← viewer.parent; ENDLOOP;
column ← viewer.column;
IF ~viewer.iconic THEN ViewerOps.CloseViewer[viewer, FALSE];
ViewerOps.ComputeColumn[column];
AddViewers[desktop, LIST[viewer]];
ViewerOps.ChangeColumn[viewer, static];
END;
AddViewers: PROCEDURE [desktop: Viewer, list: LIST OF Viewer] = BEGIN
data: ViewerSetData = NARROW[ViewerOps.FetchProp[desktop, $ViewerSetData]];
added: INTEGER;
count: INTEGER ← 0;
NewEntry: PROCEDURE [v: Viewer] RETURNS [Entry] = {RETURN[NEW[EntryRec ←
[v.name, v.class.flavor, v, v.column, v.iconic, v.position, v.openHeight, v.wy, v.wh]]]};
CountViewers: PROC[list: LIST OF Viewer] RETURNS[c: INTEGER ← 0] = INLINE {
FOR v: LIST OF Viewer ← list, v.rest WHILE v # NIL DO c ← c + 1; ENDLOOP};
EliminateDuplicates: ViewerOps.EnumProc = BEGIN
last: LIST OF Viewer;
entry: Entry = NARROW[ViewerOps.FetchProp[v, $Entry]];
IF entry = NIL OR entry.viewer = NIL OR entry.viewer.destroyed THEN RETURN;
FOR temp: LIST OF Viewer ← list, temp.rest WHILE temp # NIL DO
IF temp.first # entry.viewer THEN {last ← temp; LOOP};
IF last = NIL THEN list ← temp.rest ELSE last.rest ← temp.rest;
EXIT; ENDLOOP;
END;
MoveOrDestroy: ViewerOps.EnumProc = BEGIN
IF v.class.flavor # $Button THEN RETURN;
IF data # NIL AND data.cacheLimit > 0 AND
v.wy - firstButton + added*buttonHeight >= data.cacheLimit*buttonHeight
THEN ViewerOps.DestroyViewer[v, FALSE]
ELSE ViewerOps.MoveViewer[v, v.wx, v.wy+added*buttonHeight, v.ww, v.wh, FALSE];
END;
ViewerOps.EnumerateChildren[desktop, EliminateDuplicates];
added ← CountViewers[list];
[] ← desktop.class.scroll[desktop, thumb, 0];
ViewerOps.EnumerateChildren[desktop, MoveOrDestroy];
FOR l: LIST OF Viewer ← list, l.rest WHILE l # NIL DO
entry: Entry ← NewEntry[l.first];
name: Rope.ROPE ← Rope.Cat[Atom.GetPName[entry.flavor], ": ", entry.name];
button: Viewer ← Buttons.Create[
info: [name: name, parent: desktop, wy: firstButton + count*buttonHeight],
proc: MyButtonProc, paint: FALSE];
ViewerOps.AddProp[button, $Entry, entry];
count ← count + 1;
ENDLOOP;
ViewerOps.PaintViewer[desktop, all];
END;
MyButtonProc: Buttons.ButtonProc = BEGIN
LockedOpenItem: PROC = {IF control THEN RemoveItem[NARROW[parent]]
ELSE OpenItem[NARROW[parent], FALSE, shift]};
ViewerLocks.CallUnderViewerTreeLock[LockedOpenItem ! ViewerLocks.Wedged => CONTINUE];
END;
OpenItem: PROC[button: Viewer, all, grow: BOOLFALSE] = BEGIN
e: Entry = NARROW[ViewerOps.FetchProp[button, $Entry]];
iconic: BOOLEAN ← all AND e.iconic;
moved: BOOLEANFALSE;
IF 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.name,
iconic: iconic, column: e.column], paint: ~all];
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 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;
SetHeight: PROC[button: Viewer] = INLINE BEGIN
e: Entry ← NARROW[ViewerOps.FetchProp[button, $Entry]];
IF e.iconic THEN RETURN;
e.viewer.position ← e.position;
e.viewer.openHeight ← e.height;
END;
RemoveItem: PROC[button: Viewer] = BEGIN
parent: Viewer = button.parent;
entry: Entry = NARROW[ViewerOps.FetchProp[button, $Entry]];
MoveUp: ViewerOps.EnumProc = {IF v.wy > button.wy THEN
ViewerOps.MoveViewer[v, v.wx, v.wy-buttonHeight, v.ww, v.wh, FALSE]};
IF entry.viewer # NIL AND ~entry.viewer.destroyed AND entry.viewer.offDeskTop THEN
ViewerOps.ChangeColumn[entry.viewer, entry.column];
ViewerOps.EnumerateChildren[parent, MoveUp];
ViewerOps.DestroyViewer[button, FALSE];
ViewerOps.PaintViewer[parent, all];
END;
UneditedDocument: PROC[v: Viewer] RETURNS[BOOLEAN] = INLINE {
RETURN[v # NIL AND v.class.flavor = $Text AND ~v.newVersion AND ~v.newFile]};
[] ← ViewerEvents.RegisterEventProc[EraseDeskTop, destroy, $DeskTop];
Init[];
END.