sheffler@gomez.mitre.org (Thomas J. Sheffler) (02/11/89)
I started wondering how a collection of desktops could be managed under NeWS. A desktop would be simply a set of windows. Switching from one desk to the other should be easy and should allow an arbitrary grouping of windows. My first attempt at this is presented below. The application window has a number of buttons. The "ToDeskX" buttons bring up one of three desktops. The "All" button brings up ALL windows. Sorting windows into the desktops is done by pressing "NextWindow". This selects a window and hilites the one selected by flashing it (you'll be able to tell). Put it in one or more desks with the "InDeskX" buttons, or in all three with "InAll". Because the creation of new windows is not caught and the exit of other windows is not noticed, there is a "RESET" button. This releases all windows from the desks and allows you to start over. The main reason this is needed is to release zombie canvases. When a window application exits but is in a desktop, a reference to that canvas is still in the desktop structure. The canvas will not disappear until the reference is released: "reset" does this. No guarantees come with this software. As a hint, if something goes wrong, use the "AllWindows" rootmenu menu to send "Open" or "Close" to all windows to get them back. This package achieves its effect by selectively mapping and unmapping windows -- maybe there's a better way. Any comments/improvements would be appreciated. Send them to sheffler@gateway.mitre.org -Tom ===================== C U T H E R E ====================================== #! /usr/NeWS/bin/psh % Provide a multi-desktop capability for NeWS % Upon startup, all existing windows are located and placed in a list. % Pressing the "NextWindow" button causes the selection of the next % window in the list - it is hilited by flashing it on the screen and % bringing it to the top. The window may be placed in one of the % three desktops with the "InDesk" buttons, or in all three desktops % with the "In All" button. After windows have been placed in desks, % a desktop may be selected with the "ToDesk" buttons, or the "All" % button to re-map all windows. % Because the desktop manager doesn't know about windows dying or starting % up, the "Reset" button must be used to relocate windows. This % destroys desktop lists. % Tom Sheffler % MITRE % February 1989 systemdict /DeskWin known not { % only load the class if not known systemdict begin /DeskWin DefaultWindow dictbegin % instance variables dictend classbegin % class variables first /MaxWin 50 def % max number of windows? /Desk1 MaxWin dict def % dictionaries for desk-tops /Desk2 MaxWin dict def /Desk3 MaxWin dict def /Every MaxWin dict def % list EVERY window found (w/ AllWin) /UniqueCycle 0 def % for cycling through windows /currentWin null def % used with function /NextWindow /controlWin null def % keep track of the ONE main window % Clear one entry of a dict in a /forall loop /clearone { % key value => - pop null store } def % Clear all entries in the dicts (to return to VM mgr) /clearall { /currentWin null def % release this one /Desk1 null def % release all storage /Desk2 null def /Desk3 null def /Desk1 MaxWin dict def % and create new /Desk2 MaxWin dict def /Desk3 MaxWin dict def } def % Go thru the dicts clearing out DEAD windows. % Treat the buttons as frame controls. /CreateFrameControls { /CreateFrameControls super send /b1 (ToDesk1) {/ToDesk1 DeskWin send} FrameCanvas /new ButtonItem send 20 -60 /move 3 index send def /b2 (ToDesk2) {/ToDesk2 DeskWin send} FrameCanvas /new ButtonItem send 90 -60 /move 3 index send def /b3 (ToDesk3) {/ToDesk3 DeskWin send} FrameCanvas /new ButtonItem send 160 -60 /move 3 index send def /b4 (All ) {/ToAll DeskWin send} FrameCanvas /new ButtonItem send 230 -60 /move 3 index send def /b5 (Next Window) {/HiliteNext DeskWin send} FrameCanvas /new ButtonItem send 70 -110 /move 3 index send def % /b6 (Find) {/Find DeskWin send} FrameCanvas /new ButtonItem send % 110 -110 /move 3 index send def /b7 (Reset) {/Reset DeskWin send} FrameCanvas /new ButtonItem send 180 -110 /move 3 index send def /b8 (InDesk1) {/PutDesk1 DeskWin send} FrameCanvas /new ButtonItem send 20 -160 /move 3 index send def /b9 (InDesk2) {/PutDesk2 DeskWin send} FrameCanvas /new ButtonItem send 90 -160 /move 3 index send def /b10 (InDesk3) {/PutDesk3 DeskWin send} FrameCanvas /new ButtonItem send 160 -160 /move 3 index send def /b11 (In All) {/PutAll DeskWin send} FrameCanvas /new ButtonItem send 230 -160 /move 3 index send def % Resize them all for the first time [b1 b2 b3 b4 b5 b7 b8 b9 b10 b11] { { location ObjectWidth ObjectHeight reshape paint } exch send } forall % Start processes for listening to buttone [b1 b2 b3 b4 b5 b7 b8 b9 b10 b11] forkitems } def % Repaint the buttons whenever frame controls repainted /PaintFrameControls { /PaintFrameControls super send [b1 b2 b3 b4 b5 b7 b8 b9 b10 b11] { { paint } exch send } forall } def % The /new method for this class only allows one /contolWin to Exist /new { % only allow one to exist! controlWin null eq { /new super send % get the window dup /controlWin exch def % keep track of this one begin % open the new dict end 500 500 310 200 /reshape controlWin send /map controlWin send % make visible } if DeskWin /currentWin controlWin put % an initial value } def % Try to release all VM (so no zombie canvases) /DestroyClient { % reset /controlWin DeskWin /Every null put clearall % clear the rest of them DeskWin /currentWin null put DeskWin /controlWin null put % before "super send" ?? % else get ZOMBIE canvas /DestroyClient super send } def % Reset: clear everything and start over /Reset { {/map self send} AllWin % just in case! /Every null def clearall % clear the rest of them /currentWin controlWin def FindWindows } def % Add a window to /Every. Each window is a key in a dict, the % value assoc w/ the key is an index. These indices are incremented % when the win is accessed thru /NextWindow. When no more values in the % dict match /UniqueCycle, a new cycle begins. /AddWin { % window => - % dup % => win win % [ exch ] (Add Window:%\n) exch dbgprintf % => win Every exch UniqueCycle put % put window in /Every dict } def % Find every window and place in every /FindWindows { % - => - % (IN FindWindows!\n) [ ] dbgprintf /Every null def % free VM /Every MaxWin dict def % a new dict % save each window {self /AddWin DeskWin send} AllWin } def % For debugging only, print all windows in dict /Every /PrintWindows { Every { [ exch ] (Value:%) exch dbgprintf [ exch ] (Key:%\n) exch dbgprintf } forall } def % Check if a window (kept in a list) is a ZOMBIE, return null if it is /CheckZombie { % win => win/null dup % win win /FrameEventMgr get % win val/null? % (In ZOMBIE:) [] dbgprintf % dup [exch] (%\n) exch dbgprintf null eq {pop null} if % replace w/ null if eq null } def % This function finds a window in the current cycle, or null if % there are none. A result is returned AND /currentWin is left % the value too. /NextInWindowCycle { /currentWin null def % set to null Every { % => win value UniqueCycle eq { dup % => win win Every exch UniqueCycle % => win dict win unique 1 add put % => win /currentWin exch def exit % exit the loop } { pop % suck up the win } ifelse } forall currentWin % the return value } def % Move on to the next cycle if cycling thru windows and none % found with NextWindow /NextCycle { /UniqueCycle UniqueCycle 1 add def } def % This function continually returns a new window each time called. /NextWindow { % => win NextInWindowCycle dup null eq { pop % throw away null?? NextCycle NextInWindowCycle% begin next cycle } if % otherwise, return the win } def % Hilite Window by bringing to top, hiliting /HiliteWindow { % win => - dup /totop exch send % bring to top dup /unmap exch send % unmap it /map exch send % map it } def % Hilite next window and select it /HiliteNext { % - => win NextWindow HiliteWindow } def % Given a dict of windows, map only those /MapDesk { % dict => - % Can't use 'AllWin' because it blocks Every { pop % get rid of value CheckZombie % see if win is a zombie dup null eq % => win/null f/t {pop} {/unmap exch send} ifelse } forall /map controlWin send % map the control window % DEBUG % dup % dup the dict % {[exch] (Value:%) exch dbgprintf % [exch] (Key:%\n) exch dbgprintf % } forall % (dict is still on the stack) { pop % get rid of value CheckZombie dup null eq {pop} {/map exch send} ifelse } forall } def /MapAll { % this one's easy {/map self send} AllWin } def % Put the current window, currentWin, in a desk /PutDesk { % dict => - currentWin % => dict currentWin UniqueCycle put } def % The methods called from the button's notify procs /ToDesk1 {Desk1 MapDesk} def /ToDesk2 {Desk2 MapDesk} def /ToDesk3 {Desk3 MapDesk} def /ToAll { {/map self send} AllWin } def /PutDesk1 {Desk1 PutDesk} def /PutDesk2 {Desk2 PutDesk} def /PutDesk3 {Desk3 PutDesk} def /PutAll {Desk1 PutDesk Desk2 PutDesk Desk3 PutDesk} def classend def end % of systemdict! } if % matches "known not" at beginning framebuffer /new DeskWin send pop % throw away? /FindWindows DeskWin send