don@BRILLIG.UMD.EDU (Don Hopkins) (03/07/89)
%!
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 by Don Hopkins. All rights reserved.
% This program is provided for unrestricted use, provided that this
% copyright message is preserved. There is no warranty, and no author
% or distributer accepts responsibility for any damage caused by this
% program.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Menus that can be changed while they're popped up.
% Don Hopkins
%
% Instructions:
%
% This file defines a subclass of DefaultMenu called SoftMenu, which
% it then installs as DefaultMenu.
% It allows you to cut & paste menu keys around dynamically from menu
% to menu, using the PointButton. (The left mouse button on a Sun.)
% This should work with most menu classes. (hopefully...) Just don't
% load it in on top of itsself.
%
% While making a selection from a menu:
% PointButton
% Copy the highlighted menu item to PrimarySelection.
% (i.e. like "copy" on the Mac)
% Shift PointButton
% "Insert" the menu item from PrimarySelection before the
% highlighted item. If no items are highlighted, it's appended
% to the end of the menu.
% Control PointButton
% "Delete" the highlighted menu item.
% Meta PointButton
% "Edit" the highlighted menu item. (Coming soon. Suggestions?)
%
% To make a new menu item, you can select a string or array
% subinterval which will push a menu key and an action on the stack
% when executed, and insert it into a menu. Note that everything is
% executed in the context of the menu you're inserting into.
%
% It's your responsibility to avoid circularities!
%
% If you select a (dict,key) pointer in cyberspace, and insert it
% into a menu, the key name will be the menu key and the value
% "dict key get" will be the action.
%
% Examples: (just select the following and paste them:)
{
(--------) {} % separator
(Frame...) /FrameMenu ThisWindow send
(Hello, Sailor!) { gsave framebuffer setcanvas
currentcursorlocation
[(Nothing) (happens) (here.)] popmsg pop
grestore }
(Be Gone!) { /flipiconic ThisWindow send
/tobottom ThisWindow send }
(rootmenu) rootmenu
(psload) { (NEWSHOME) getenv (/bin/psload) forkunix }
} pop
systemdict begin
/SoftMenu DefaultMenu
dictbegin
/Changed? true def
/MenuLock null def
/ChangedEvent null def
/ChangeDelay .25 60 div def
/Control false def
/Shift false def
/Meta false def
dictend
classbegin
/new {
/new super send begin
/MenuLock createmonitor def
currentdict end
} def
/layout {
MenuLock {
/layout super send
/Changed false def
} monitor
} def
/paint {
MenuLock {
/paint super send
} monitor
} def
/insertitem {
MenuLock {
/insertitem super send
change
} monitor
} def
/deleteitem {
MenuLock {
/deleteitem super send
change
} monitor
} def
/showat {
MenuLock {
/showat super send
} monitor
} def
/change {
/Changed true def
/MenuValue null def
MenuCanvas null ne {
ChangedEvent null eq {
/ChangedEvent createevent def
ChangedEvent /Name /MenuChanged put
} {
ChangedEvent recallevent
} ifelse
ChangedEvent /Canvas MenuCanvas put
ChangedEvent /TimeStamp currenttime ChangeDelay add put
ChangedEvent sendevent
} if
} def
/makeinterests {
/makeinterests super send
/MenuInterests [
MenuInterests {
dup /Name get PointButton eq { pop } if
} forall
/MenuChanged /ChangeProc null MenuCanvas eventmgrinterest
PointButton /PointProc /DownTransition MenuCanvas eventmgrinterest
] def
} def
/ChangeProc {
ChangedEvent recallevent
Changed MenuCanvas null ne MenuEventMgr null ne and and {
gsave
framebuffer setcanvas
MenuCanvas getcanvaslocation
/MenuValue null def
layout
reshape
MenuCanvas setcanvas
movecanvas
clippath extenddamage
% /MenuEventMgr null def
% { clear MenuX MenuY MenuHeight add showat } fork pop
% currentprocess killprocess
grestore
} if
} def
/UpdateShifts {
/Shift false def /Control false def /Meta false def
CurrentEvent /KeyState get {
{ /Shift /Control /Meta }
{ 1 index eq { dup true def exit } if } forall
pop
} forall
} def
/PointProc {
UpdateShifts
Control {
PointDelete
} {
Shift {
PointInsert
} {
Meta {
PointEdit
} {
PointSelect
} ifelse
} ifelse
} ifelse
} def
/PointDelete {
% Menus break with 0 items...
MenuValue null ne MenuItems length 1 gt and {
MenuValue deleteitem
} if
} def
/PointInsert {
MenuValue dup null eq { pop MenuItems length } if % index
/PrimarySelection getselection % sel
dup null eq { pop pop } {
dup /ContentsPostScript known {
dup /ContentsPostScript get exch % obj sel
dup /SelectionStartIndex known not { pop } {
dup /SelectionLastIndex known {
dup /SelectionStartIndex get % obj sel start
exch /SelectionLastIndex get % obj start last
1 index sub 1 add % obj start len
getinterval % interval
} { % obj sel
/SelectionStartIndex get % obj index
exch 1 index get % index val
exch (%) sprintf exch % key val
2 array astore % [ key action ]
} ifelse
} ifelse
} {
/ContentsAscii get
} ifelse
{ count 1 roll
count 1 sub {pop} repeat
{ cvx exec } errored pop
count 2 ne { (Bad MenuKey) {} } if
2 array astore
} fork waitprocess
exch pop
aload pop % index key proc
insertitem %
} ifelse
} def
/PointEdit {
% Pop up dialog box?
} def
/PointSelect {
MenuValue null ne {
MenuItems MenuValue get begin
20 dict begin
/ContentsPostScript [
[ /Key load
Menu dup null eq { pop /Action load } if
] /aload cvx /pop cvx
] def
/ContentsAscii
ContentsPostScript 0 get aload pop exch (MenuItem[% %]) sprintf
def
/SelectionObjSize 1 def
/SelectionResponder null def
/Canvas MenuCanvas def
/SelectionHolder MenuEventMgr def
currentdict
end % SelectionDict
/PrimarySelection setselection
end % ItemDict
} if
} def
classend def
/setdefaultmenu { % class => -
/DefaultMenu exch store
systemdict /rootmenu known {
/rootmenu /flipstyle rootmenu send store
} if
} def
SoftMenu setdefaultmenu
end % systemdict