DIRECTORY Atom, HelpStrings, Process, Rope, Xl, XlDispatch, XlTQOps, XTk, XTkFriends, XTkHelpStrings, XTkHelpShells, XTkOps, XTkPopUps, XTkWidgets; XTkPopUpsImpl: CEDAR MONITOR IMPORTS Atom, HelpStrings, Process, Rope, Xl, XlDispatch, XlTQOps, XTk, XTkOps, XTkFriends, XTkHelpShells, XTkHelpStrings, XTkWidgets EXPORTS XTkPopUps SHARES XlDispatch ~ BEGIN OPEN XTkPopUps; dynamicPopUpFlag: XTk.WidgetFlagKey ~ wf3; myShellKey: REF ~ NEW[INT]; --used on pop shell widgets; value is REF DynamicRec myBaseKey: REF ~ NEW[INT]; --used on base button widgets; value is REF StaticRec myChoiceKey: REF ~ NEW[INT]; --used on choice widgets buttonReleaseFilter: Xl.EventFilter ~ Xl.CreateEventFilter[buttonRelease]; filter: Xl.EventFilter ~ Xl.CreateEventFilter[buttonPress, buttonRelease, enterNotify, leaveNotify]; IsDynamicPopShell: PROC [w: XTk.Widget] RETURNS [BOOL] = { RETURN [w#NIL AND XTk.GetWidgetFlag[w, dynamicPopUpFlag]] }; PositionAndScreen: PROC [connection: Xl.Connection] RETURNS [pos: Xl.Point, s: Xl.Screen] = { pointerQuery: Xl.PointerReply ¬ Xl.QueryPointer[connection, Xl.nullWindow]; pos ¬ pointerQuery.pos; s ¬ Xl.QueryScreen[connection, pointerQuery.root]; IF s=NIL THEN s ¬ Xl.FirstScreen[connection] }; RemovePriviledgedMatch: PROC [c: Xl.Connection, dyn: REF DynamicRec] = { m: Xl.Match ~ dyn.priviledgedMatch; IF m#NIL THEN { dyn.priviledgedMatch ¬ NIL; IF Xl.Alive[c] THEN XlDispatch.RemovePriviledgedMatch[c, m]; }; }; RemovePops: XTk.WidgetNotifyProc = { dyn: REF DynamicRec ~ NARROW[registerData]; dyn.popShell ¬ NIL; DestroyIncludingCascaded[dyn]; }; TearDown: PUBLIC PROC [carrier: PopCarrier] = { WITH carrier SELECT FROM static: REF StaticRec => DestroyIncludingAncestorsAndCascaded[static.currentDynamic]; w: XTk.Widget => { root: XTk.Widget ~ XTk.RootWidget[w]; WITH XTk.GetWidgetProp[w, myBaseKey] SELECT FROM static: REF StaticRec => DestroyIncludingAncestorsAndCascaded[static.currentDynamic]; ENDCASE => {}; IF IsDynamicPopShell[root] THEN DestroyIncludingAncestorsAndCascaded[DynamicFromShell[root]]; }; dyn: REF DynamicRec => DestroyIncludingAncestorsAndCascaded[dyn]; ENDCASE => {} }; StaticRec: TYPE = RECORD [ --representant of PseudoBase; 1 per latent pop up menu currentDynamic: REF DynamicRec ¬ NIL, --monitored enteredBase: BOOL ¬ FALSE, <> createClosure: WidgetCreateClosure, registerData: REF ¬ NIL, image: REF ¬ NIL, internalTQ: Xl.TQ, --proposal for dyn.rootTQ base: XTk.Widget, help: Rope.ROPE ¬ NIL, helpHandle: HelpStrings.Handle ¬ NIL, header: Rope.ROPE ¬ NIL ]; DynamicRec: TYPE = RECORD [ --dynamic; new each time it pops up enteredShell: BOOL ¬ FALSE, destructionStarted: BOOL ¬ FALSE, static: REF StaticRec, rootTQ: Xl.TQ, popShell: XTk.Widget, cascadedChild: REF DynamicRec ¬ NIL, parent: REF DynamicRec ¬ NIL, image: REF ¬ NIL, priviledgedMatch: Xl.Match ¬ NIL ]; NotifyClosure: TYPE = RECORD [ w: XTk.Widget, notifyTQ: Xl.TQ, notify: XTk.WidgetNotifyProc, registerData, callData: REF ]; BuildDynamic: ENTRY PROC [static: REF StaticRec, parent: REF DynamicRec, image: REF] RETURNS [dyn: REF DynamicRec ¬ NIL] = { IF parent#NIL AND parent.destructionStarted THEN RETURN; IF static#NIL THEN { oldD: REF DynamicRec ¬ static.currentDynamic; IF oldD#NIL THEN { IF oldD.static=static AND ~oldD.destructionStarted THEN RETURN [NIL]; InternalDestroyIncludingCascaded[oldD]; }; dyn ¬ NEW[DynamicRec]; dyn.static ¬ static; dyn.image ¬ image; dyn.rootTQ ¬ static.internalTQ; IF parent#NIL THEN { dyn.parent ¬ parent; InternalDestroyIncludingCascaded[parent.cascadedChild]; parent.cascadedChild ¬ dyn; IF dyn.rootTQ=NIL THEN dyn.rootTQ ¬ parent.rootTQ; }; IF dyn.rootTQ=NIL THEN dyn.rootTQ ¬ Xl.CreateTQ[order: XTk.rootLockingOrder]; static.currentDynamic ¬ dyn; }; }; DestroyIncludingCascaded: ENTRY PROC [dyn: REF DynamicRec] = { IF dyn#NIL THEN InternalDestroyIncludingCascaded[dyn]; }; InternalDestroyIncludingCascaded: INTERNAL PROC [dyn: REF DynamicRec] = { WHILE dyn#NIL DO next: REF DynamicRec ~ dyn.cascadedChild; parent: REF DynamicRec ~ dyn.parent; static: REF StaticRec ~ dyn.static; dyn.destructionStarted ¬ TRUE; dyn.cascadedChild ¬ NIL; dyn.parent ¬ NIL; IF parent#NIL AND parent.cascadedChild=dyn THEN { parent.cascadedChild ¬ NIL; }; IF static.currentDynamic=dyn THEN static.currentDynamic ¬ NIL; ForkDestroy[dyn]; dyn ¬ next; ENDLOOP; }; DestroyIncludingAncestorsAndCascaded: ENTRY PROC [dyn: REF DynamicRec] = { IF dyn#NIL THEN { WHILE dyn.parent#NIL DO dyn ¬ dyn.parent ENDLOOP; InternalDestroyIncludingCascaded[dyn]; }; }; ForkDestroy: INTERNAL PROC [dyn: REF DynamicRec] = { shell: XTk.Widget ~ dyn.popShell; dyn.popShell ¬ NIL; IF IsDynamicPopShell[shell] THEN Xl.Enqueue[dyn.rootTQ <>, Destroy, shell]; }; Destroy: Xl.EventProcType = { shell: XTk.Widget ~ NARROW[clientData]; IF IsDynamicPopShell[shell] THEN XTkWidgets.DestroyShell[shell]; }; DoNotify: PROC [nc: REF NotifyClosure, ev: Xl.Event] = { IF nc.notifyTQ=NIL THEN TRUSTED {Process.Detach[FORK QueuedDoNotify[event: ev, clientData: nc, tq: NIL] ]} ELSE Xl.Enqueue[tq: nc.notifyTQ, proc: QueuedDoNotify, data: nc, event: ev]; }; QueuedDoNotify: <> Xl.EventProcType = { nc: REF NotifyClosure ~ NARROW[clientData]; nc.notify[event: event, widget: nc.w, registerData: nc.registerData, callData: nc.callData]; }; ActualPopUp: PROC [dyn: REF DynamicRec, ev: Xl.Event, point: Xl.Point ¬ [-1, -1], screen: Xl.Screen ¬ NIL, dynamic: BOOL ¬ TRUE, helpHandle: REF ¬ NIL] = { h: HelpStrings.Handle; static: REF StaticRec ~ dyn.static; base: XTk.Widget ¬ static.base; createClosure: WidgetCreateClosure ¬ static.createClosure; shell, child: XTkWidgets.Widget; szh: REF XTkWidgets.ICCCMHints; compensate: Xl.Size ¬ [0, 0]; --compensate for window manager decorations IF dyn.destructionStarted OR createClosure=NIL THEN RETURN; shell ¬ XTkWidgets.CreateShell[dontQueryGeometry: TRUE, rootTQ: dyn.rootTQ, windowHeader: static.header, iconName: static.header, standardMigration: ~dynamic]; szh ¬ XTkWidgets.GetHints[shell]; XTk.PutWidgetProp[shell, myShellKey, dyn]; IF screen=NIL AND ev=NIL THEN { XTkWidgets.BindScreenShell[shell, NIL]; screen ¬ shell.screenDepth.screen; } ELSE { c: Xl.Connection ¬ IF screen#NIL THEN c ¬ screen.connection ELSE c ¬ ev.connection; IF screen=NIL OR point.x<0 OR point.y<0 THEN { p: Xl.Point; [p, screen] ¬ PositionAndScreen[c]; --no damage if screen switched IF point.x<0 THEN point.x ¬ p.x; IF point.y<0 THEN point.y ¬ p.y; }; XTkWidgets.BindScreenShell[shell, c, screen.root]; }; IF point.x>=0 AND point.y>=0 THEN { szh.wmNormalHints.userPos ¬ szh.wmNormalHints.clientPos ¬ TRUE; }; dyn.popShell ¬ shell; IF base#NIL THEN { h ¬ XTkHelpStrings.GetHandle[base]; szh.transientFor ¬ XTk.RootWidget[base]; }; WITH helpHandle SELECT FROM handle: HelpStrings.Handle => h ¬ handle ENDCASE => {}; IF dynamic THEN { parent: REF DynamicRec ¬ dyn.parent; IF parent=NIL OR ~IsDynamicPopShell[parent.popShell] THEN { m: Xl.Match ¬ NEW[Xl.MatchRep ¬ [proc: PriviledgedUpEvent, handles: buttonReleaseFilter, tq: dyn.rootTQ, data: dyn]]; dyn.priviledgedMatch ¬ m; XlDispatch.AddPriviledgedMatch[screen.connection, m]; } ELSE { IF h=NIL THEN h ¬ XTkHelpShells.CreateHelpWithPopShell[parent.popShell]; }; XTk.SetWidgetFlag[shell, dynamicPopUpFlag, TRUE]; XTk.AddPermanentMatch[ shell, [proc: PopShellEvent, handles: filter, tq: dyn.rootTQ, data: dyn], [buttonPress: TRUE, buttonRelease: TRUE, enterWindow: TRUE, leaveWindow: TRUE] ]; shell.attributes.overrideRedirect ¬ true; shell.attributes.saveUnder ¬ true; shell.s.geometry.borderWidth ¬ 3; } ELSE { compensate ¬ [-1, -10] }; IF h=NIL THEN h ¬ XTkHelpShells.CreateHelpWithPopShell[shell]; XTkHelpStrings.SetHandle[shell, h]; XTk.RegisterNotifier[shell, XTk.postStopFastAccessKey, RemovePops, dyn]; child ¬ createClosure.creator[parent: shell, closureData: createClosure.closureData, registerData: static.registerData, image: dyn.image, event: ev]; XTkWidgets.SetShellChild[shell, child]; BEGIN preferred: Xl.Geometry ¬ XTkFriends.PreferredSizeLR[shell]; --not yet realized! szh.wmNormalHints.userSize ¬ szh.wmNormalHints.clientSize ¬ TRUE; point.x ¬ MAX[MIN[point.x, screen.sizeInPixels.width - preferred.size.width - compensate.width], 0]; point.y ¬ MAX[MIN[point.y, screen.sizeInPixels.height - preferred.size.height - compensate.height], 0]; shell.s.geometry.pos ¬ point; szh.wmNormalHints.obsoletePos ¬ point; szh.wmNormalHintsChanged ¬ TRUE; szh.transientForChanged ¬ TRUE; END; IF dyn.destructionStarted THEN RETURN; XTkWidgets.RealizeShell[shell]; HelpStrings.MakeVisible[h, shell]; }; PopUp: PUBLIC PROC [pseudoBase: PseudoBase, event: XTk.Event, parentCarrier: PopCarrier ¬ NIL, image: REF ¬ NIL, pos: Xl.Point ¬ [-1, -1]] = { time: Xl.TimeStamp ¬ Xl.currentTime; rootWindow: Xl.Window ¬ Xl.nullWindow; screen: Xl.Screen ¬ NIL; WITH event SELECT FROM bp: Xl.ButtonPressEvent => { time ¬ bp.timeStamp; IF bp.sameScreen THEN rootWindow ¬ bp.root; }; en: Xl.EnterNotifyEvent => { time ¬ en.timeStamp; IF en.sameScreen THEN rootWindow ¬ en.root; }; ENDCASE => ERROR; IF Xl.SetButtonGrabOwner[event.connection, time, pseudoBase]=succeeded THEN { static: REF StaticRec ~ NARROW[pseudoBase]; dyn: REF DynamicRec; parent: REF DynamicRec ¬ NIL; WITH parentCarrier SELECT FROM static: REF StaticRec => parent ¬ static.currentDynamic; w: XTk.Widget => { root: XTk.Widget ¬ XTk.RootWidget[w]; IF IsDynamicPopShell[root] THEN parent ¬ DynamicFromShell[root]; }; dyn: REF DynamicRec => parent ¬ dyn; ENDCASE => {}; dyn ¬ BuildDynamic[static, parent, image]; IF dyn#NIL THEN { IF rootWindow#Xl.nullWindow THEN screen ¬ Xl.QueryScreen[event.connection, rootWindow.drawable]; ActualPopUp[dyn: dyn, ev: event, point: pos, screen: screen, dynamic: TRUE]; }; }; }; DynamicFromShell: PROC [root: XTk.Widget] RETURNS [REF DynamicRec ¬ NIL] = { WITH XTk.GetWidgetProp[root, myShellKey] SELECT FROM dyn: REF DynamicRec => RETURN[dyn]; ENDCASE => {}; }; SimplePopUpWithRegularShell: PUBLIC PROC [list: ChoiceList, defaultNotify: XTk.WidgetNotifyProc, notifyTQ: XTk.TQ ¬ NIL, screen: Xl.Screen ¬ NIL, pos: Xl.Point ¬ [-1, -1], registerData: REF ¬ NIL, event: Xl.Event ¬ NIL, header: Rope.ROPE ¬ NIL, helpHandle: REF ¬ NIL] = { createMenu: WidgetCreateClosure ~ WidgetCreateClosureFromChoiceList[list, defaultNotify, notifyTQ]; PopUpWithRegularShell[createMenu, screen, pos, registerData, NIL, event, header, helpHandle] }; PopUpWithRegularShell: PUBLIC PROC [createMenu: WidgetCreateClosure, screen: Xl.Screen, pos: Xl.Point ¬ [-1, -1], registerData: REF ¬ NIL, image: REF ¬ NIL, event: Xl.Event ¬ NIL, header: Rope.ROPE ¬ NIL, helpHandle: REF ¬ NIL] = { dyn: REF DynamicRec; static: REF StaticRec ¬ NewPopStatic[]; static.image ¬ image; static.registerData ¬ registerData; static.createClosure ¬ createMenu; static.header ¬ header; dyn ¬ BuildDynamic[static, NIL, image]; IF dyn#NIL THEN { ActualPopUp[dyn: dyn, screen: screen, point: pos, ev: event, dynamic: FALSE, helpHandle: helpHandle]; }; }; TestUpnessNow: ENTRY Xl.EventProcType = { dyn: REF DynamicRec ¬ NARROW[clientData]; WHILE dyn#NIL AND ~dyn.enteredShell AND ~dyn.static.enteredBase AND dyn.cascadedChild=NIL DO parent: REF DynamicRec ¬ dyn.parent; IF parent=NIL THEN RETURN; IF ~IsDynamicPopShell[parent.popShell] THEN RETURN; InternalDestroyIncludingCascaded[dyn]; dyn ¬ parent; ENDLOOP }; TestUpnessVerySoon: Xl.EventProcType = { dyn: REF DynamicRec ~ NARROW[clientData]; IF dyn#NIL AND ~dyn.destructionStarted AND dyn.parent#NIL AND ~dyn.enteredShell AND ~dyn.static.enteredBase AND dyn.cascadedChild=NIL THEN XlTQOps.EnqueueSoon[150, dyn.rootTQ <>, TestUpnessNow, dyn] }; TestUpnessSoon: PROC [dyn: REF DynamicRec] = { IF dyn#NIL THEN XlTQOps.EnqueueSoon[150, dyn.rootTQ <>, TestUpnessVerySoon, dyn] }; SetStyle: PROC [base: XTk.Widget, style: ATOM] = { IF base#NIL AND XTk.HasClassKey[base, $Label] THEN XTkWidgets.SetStyleKey[base, style]; }; BaseButtonEvent: <> Xl.EventProcType = { static: REF StaticRec ~ NARROW[clientData]; base: XTk.Widget ~ static.base; SELECT event.type FROM buttonPress => { SetStyle[base, $BlackOnGray]; PopUp[static, event, base, static.image]; }; buttonRelease => { DestroyIncludingAncestorsAndCascaded[static.currentDynamic]; IF base#NIL THEN { IF static.enteredBase THEN { static.enteredBase ¬ FALSE; SelectOnly[base, event]; }; SetStyle[base, NIL]; }; }; leaveNotify => { lne: Xl.LeaveNotifyEvent ~ NARROW[event]; SetStyle[base, NIL]; SELECT lne.detail FROM ancestor, virtual, nonlinear, nonlinearVirtual => { static.enteredBase ¬ FALSE; TestUpnessSoon[static.currentDynamic]; }; inferior => {}; ENDCASE => {}; HelpStrings.Clear[static.helpHandle, static.help]; }; enterNotify => { ene: Xl.EnterNotifyEvent ~ NARROW[event]; state: Xl.SetOfKeyButMask ~ ene.state; IF base#NIL AND (state.button1 OR state.button2 OR state.button3 OR state.button4 OR state.button5) THEN { root: XTk.Widget ~ XTk.RootWidget[base]; static.enteredBase ¬ TRUE; IF IsDynamicPopShell[root] THEN { rightShift: NAT ~ 32; width: INT ~ base.actual.size.width; pos: Xl.Point ¬ ene.rootP; IF ene.eventP.x>=0 AND ene.eventP.x=0 AND ene.eventP.y {}; }; PopShellEvent: <> Xl.EventProcType = { dyn: REF DynamicRec ~ NARROW[clientData]; SELECT event.type FROM leaveNotify => { lne: Xl.LeaveNotifyEvent ~ NARROW[event]; SELECT lne.detail FROM ancestor, virtual, nonlinear, nonlinearVirtual => { dyn.enteredShell ¬ FALSE; TestUpnessSoon[dyn]; }; inferior => {}; ENDCASE => {} }; enterNotify => dyn.enteredShell ¬ TRUE; buttonRelease => DestroyIncludingAncestorsAndCascaded[dyn]; ENDCASE => {}; }; PriviledgedUpEvent: <> Xl.EventProcType = { dyn: REF DynamicRec ~ NARROW[clientData]; SELECT event.type FROM buttonRelease => { RemovePriviledgedMatch[event.connection, dyn]; DestroyIncludingAncestorsAndCascaded[dyn]; }; ENDCASE => {}; }; NewPopStatic: PROC [internalTQ: Xl.TQ ¬ NIL] RETURNS [REF StaticRec] = { static: REF StaticRec ~ NEW[StaticRec]; IF internalTQ=NIL THEN internalTQ ¬ Xl.CreateTQ[order: XTk.rootLockingOrder]; static.internalTQ ¬ internalTQ; RETURN [static]; }; CreatePseudoBase: PUBLIC PROC [registerData: REF ¬ NIL] RETURNS [pseudoBase: PseudoBase] = { static: REF StaticRec ¬ NewPopStatic[]; static.registerData ¬ registerData; RETURN [static]; }; AssertBase: ENTRY PROC [base: XTk.Widget] RETURNS [REF StaticRec¬NIL] = { IF base#NIL THEN WITH XTk.GetWidgetProp[base, myBaseKey] SELECT FROM static: REF StaticRec => RETURN [static]; ENDCASE => { static: REF StaticRec ~ NewPopStatic[]; XTk.AddPermanentMatch[base, [proc: BaseButtonEvent, handles: filter, tq: static.internalTQ, data: static], [ownerGrabButton: TRUE, buttonPress: TRUE, buttonRelease: TRUE, enterWindow: TRUE, leaveWindow: TRUE]]; static.base ¬ base; XTk.PutWidgetProp[base, myBaseKey, static]; XTkOps.CallAndRegisterOnPostRealize[base, PostRealized, static]; RETURN [static]; }; }; PostRealized: XTk.WidgetNotifyProc = { static: REF StaticRec ~ NARROW[registerData]; static.helpHandle ¬ XTkHelpStrings.GetHandle[widget]; }; MakeItBase: PUBLIC PROC [base: XTk.Widget, registerData: REF ¬ NIL, createMenu: WidgetCreateClosure ¬ NIL, image: REF ¬ NIL, help: Rope.ROPE ¬ NIL] = { static: REF StaticRec ~ AssertBase[base]; static.registerData ¬ registerData; static.createClosure ¬ createMenu; static.image ¬ image; static.help ¬ help; }; CreateSimplePopUpButton: PUBLIC PROC [text: Rope.ROPE ¬ NIL, list: ChoiceList, defaultNotify: XTk.WidgetNotifyProc, notifyTQ: XTk.TQ ¬ NIL, registerData: REF ¬ NIL, help: Rope.ROPE ¬ NIL] RETURNS [button: XTk.Widget] = { createMenu: WidgetCreateClosure ~ WidgetCreateClosureFromChoiceList[list, defaultNotify, notifyTQ]; button ¬ CreatePopUpButton[text, createMenu, registerData, NIL, help]; }; CreatePopUpButton: PUBLIC PROC [text: Rope.ROPE ¬ NIL, createMenu: WidgetCreateClosure, registerData: REF ¬ NIL, image: REF ¬ NIL, help: Rope.ROPE ¬ NIL] RETURNS [button: XTk.Widget] = { button ¬ XTkWidgets.CreateLabel[text: text]; MakeItBase[button, registerData, createMenu, image]; }; GetPopStatic: PROC [carrier: PopCarrier] RETURNS [REF StaticRec ¬ NIL] = { WITH carrier SELECT FROM static: REF StaticRec => RETURN [static]; w: XTk.Widget => { WITH XTk.GetWidgetProp[w, myBaseKey] SELECT FROM static: REF StaticRec => RETURN [static]; ENDCASE => {}; }; ENDCASE => {} }; SetMenuCreator: PUBLIC PROC [carrier: PopCarrier, createMenu: WidgetCreateClosure] = { static: REF StaticRec ~ GetPopStatic[carrier]; static.createClosure ¬ createMenu; --ok to crash if static=NIL }; SetHelp: PUBLIC PROC [carrier: PopCarrier, help: Rope.ROPE] = { static: REF StaticRec ~ GetPopStatic[carrier]; static.help ¬ help; --ok to crash if static=NIL }; RegisterBaseSelection: PUBLIC PROC [base: XTk.Widget, notify: XTk.WidgetNotifyProc, registerData, callData: REF ¬ NIL, notifyTQ: XTk.TQ ¬ NIL] = { notifyClosure: REF NotifyClosure ¬ NIL; IF notify#NIL THEN notifyClosure ¬ NEW[NotifyClosure ¬ [ w: base, notifyTQ: notifyTQ, notify: notify, registerData: registerData, callData: callData ]]; XTk.PutWidgetProp[base, myChoiceKey, notifyClosure]; }; ToRope: PROC [x: REF ANY, default: Rope.ROPE ¬ NIL] RETURNS [Rope.ROPE] = { WITH x SELECT FROM r: Rope.ROPE => RETURN [r]; r: REF TEXT => RETURN [Rope.FromRefText[r]]; a: ATOM => RETURN [Atom.GetPName[a]]; ENDCASE => RETURN [default] }; ChoiceListData: TYPE = RECORD [defaultNotify: XTk.WidgetNotifyProc, cl: ChoiceList, notifyTQ: XTk.TQ ¬ NIL, registerData: REF]; ChoiceListCreateContents: CreateWidgetProc = { container: XTkWidgets.Widget ~ XTkWidgets.CreateYStack[]; WITH closureData SELECT FROM cld: REF ChoiceListData => { FOR cl: ChoiceList ¬ cld.cl, cl.rest WHILE cl#NIL DO child: XTkWidgets.Widget ¬ NIL; notify: XTk.WidgetNotifyProc ¬ cl.first.notify; impl: REF ¬ cl.first.impl; key: REF ¬ cl.first.key; text: Rope.ROPE ¬ ToRope[cl.first.image, NIL]; IF notify=NIL THEN notify ¬ cld.defaultNotify; IF impl=NIL THEN { help: Rope.ROPE ¬ ToRope[cl.first.help, NIL]; IF key=NIL THEN key ¬ cl.first.image; child ¬ XTkWidgets.CreateButton[text: text, hitProc: notify, registerData: registerData, callData: key, tq: cld.notifyTQ, help: help]; } ELSE WITH impl SELECT FROM cl2: ChoiceList => { closure: WidgetCreateClosure ~ WidgetCreateClosureFromChoiceList[list: cl2, defaultNotify: notify, notifyTQ: cld.notifyTQ]; IF text=NIL THEN text ¬ "more..."; child ¬ CreatePopUpButton[text: text, createMenu: closure, registerData: registerData, image: cl.first.image]; IF key#NIL THEN RegisterBaseSelection[base: child, notify: cld.defaultNotify, notifyTQ: cld.notifyTQ, registerData: registerData, callData: key]; }; wcc: WidgetCreateClosure => { IF wcc.creator=ChoiceListCreateContents THEN { IF text=NIL THEN text ¬ "more..."; child ¬ CreatePopUpButton[text: text, createMenu: wcc, registerData: registerData, image: cl.first.image]; } ELSE { IF key=NIL THEN key ¬ cl.first.image; child ¬ wcc.creator[parent: container, closureData: wcc.closureData, registerData: registerData, image: image]; IF child#NIL THEN XTk.PutWidgetProp[child, myChoiceKey, NEW[NotifyClosure ¬ [w: child, notifyTQ: cld.notifyTQ, notify: notify, registerData: registerData, callData: key]]] }; }; ENDCASE => {}; IF child#NIL THEN XTkWidgets.AppendChild[container, child]; ENDLOOP; }; ENDCASE => ERROR; RETURN [container]; }; WidgetCreateClosureFromChoiceList: PUBLIC PROC [list: ChoiceList, defaultNotify: XTk.WidgetNotifyProc, notifyTQ: XTk.TQ ¬ NIL] RETURNS [closure: WidgetCreateClosure] = { cld: REF ChoiceListData ~ NEW[ChoiceListData ¬ [defaultNotify: defaultNotify, cl: list, notifyTQ: notifyTQ]]; closure ¬ NEW[WidgetCreateClosureRec ¬ [ChoiceListCreateContents, cld]]; }; Select: PUBLIC PROC [choice: XTk.Widget, event: XTk.Event] = { root: XTk.Widget ¬ XTk.RootWidget[choice]; TearDown[root]; SelectOnly[choice, event]; }; SelectOnly: PROC [choice: XTk.Widget, event: XTk.Event] = { WITH XTk.GetWidgetProp[choice, myChoiceKey] SELECT FROM nc: REF NotifyClosure => DoNotify[nc, event]; ENDCASE => {}; }; NullNotify: PUBLIC XTk.WidgetNotifyProc = {}; END. XTkPopUpsImpl.mesa Copyright Ó 1991, 1992 by Xerox Corporation. All rights reserved. Created by Christian Jacobi, October 14, 1991 9:31:44 pm PDT Christian Jacobi, October 15, 1992 3:09 pm PDT Make sure we re-enable poping, even on widget destruction due to external causes. --only base widgets have the "myBaseKey" property Sets up the data structure and the tq's but not yet the widget --Data structure maintenace is monitored --Widget destruction is forked --Fork destruction so it may be initialized from within monitored region --Do set window manager flags anyway, for the case that callback might have undone overrideRedirect --this is root thread, so realization will not conflict with destruction --only shells will have the "myShellKey" property --Check inbetween to reduce chance of tearing it down. An occasional miss doesn't matter as the user will certainly cause other levenotifyEvents --put it to the right a little --put it at the same height --Re-registers pop up callback. --Select an entry without calling TearDown Êt–(cedarcode) style•NewlineDelimiter ˜codešœ™Kšœ Ïeœ7™BKšœ<™˜]K˜—Kšœžœ9˜AKšžœ˜ —Kšœ˜—K˜šœ žœžœ 6˜QKšœžœžœ  ˜1Kšœ žœžœ˜8Kšœ#˜#Kšœžœžœ˜Kšœžœžœ˜Kšœžœ ˜,Kšœ˜Kšœ žœžœ˜Kšœ!žœ˜%Kšœ žœž˜Kšœ˜K˜—šœ žœžœ #˜@Kšœžœžœ˜Kšœžœžœ˜!Kšœžœ ˜Kšœ žœ˜Kšœ˜Kšœžœžœ˜$Kšœžœžœ˜Kšœžœžœ˜Kšœž˜ Kšœ˜K˜—šœžœžœ˜Kšœ˜Kšœ žœ˜Kšœ˜Kšœž˜Kšœ˜—K˜šŸ œžœžœ žœžœžœžœžœžœ˜|Kšœ>™>Kš žœžœžœžœžœ˜8šžœžœžœ˜Kšœžœ$˜-šžœžœžœ˜Kš žœžœžœžœžœ˜EKšœ'˜'K˜—Kšœžœ ˜Kšœ˜Kšœ˜Kšœ ˜ šžœžœžœ˜Kšœ˜Kšœ7˜7Kšœ˜Kšžœ žœžœ˜2Kšœ˜—Kšžœ žœžœ7˜MKšœ˜K˜—K˜—K˜šŸœžœžœžœ˜>Kšžœžœžœ'˜6Kšœ˜—K˜šŸ œžœžœžœ˜Išžœžœžœ˜Kšœžœ ˜)Kšœžœ˜$Kšœžœ˜#Kšœžœ˜Kšœžœžœ˜*šžœžœžœžœ˜1Kšœžœ˜K˜—Kšžœžœžœ˜?Kšœ˜Kšœ ˜ Kšžœ˜—K˜—šŸ$œžœžœžœ˜JKšœ(™(Kšœ™šžœžœžœ˜Kšžœ žœžœžœ˜1Kšœ&˜&Kšœ˜—K˜—K˜šŸ œžœžœžœ˜4J™IKšœ!˜!Kšœžœ˜KšžœžœA˜aKšœ˜—K˜šŸœ˜Kšœžœ ˜'Kšžœžœ ˜@K˜—K˜šŸœžœžœ!˜8šžœ žœ˜šžœžœžœ˜"Kšœ.žœ˜2Kšœ˜—KšžœH˜L—Kšœ˜—K˜šŸœ.˜Kšœžœ žœ ˜+Kšœ˜šžœ ž˜šœ˜Kšœ˜Kšœ)˜)K˜—šœ˜Kšœ<˜<šžœžœžœ˜šžœžœ˜Kšœžœ˜Kšœ˜Kšœ˜—Kšœžœ˜K˜—K˜—šœ˜Kšœžœ˜)Kšœžœ˜šžœ ž˜šœ3˜3Kšœžœ˜Kšœ&˜&Kšœ˜—Kšœ˜Kšžœ˜—Kšœ2˜2K˜—šœ˜Kšœžœ˜)Kšœ&˜&šžœžœžœžœžœžœžœžœ˜lKšœ(˜(Kšœžœ˜šžœžœ˜!Kšœ žœ˜Kšœžœ˜$Kšœ˜Kšœ™šžœžœžœ˜0Kšœ žœžœ˜)Kšœžœ˜(šžœžœ˜ Kšœžœ%˜0—K˜Kšœ˜—Kšœ™šžœžœ&žœ˜BKšœ˜Kšœ˜—Kšœ˜Kšœ4˜4Kšœ.˜.K˜—K˜—K˜—Kšžœ˜—K˜K˜—šŸ œ(˜5Kšœžœžœ ˜)šžœ ž˜šœ˜Kšœžœ˜)šžœ ž˜šœ3˜3Kšœžœ˜Kšœ˜Kšœ˜—Kšœ˜Kšžœ˜ —K˜—Kšœ#žœ˜(Kšœ;˜;Kšžœ˜—K˜K˜—šŸœ,˜>Kšœžœžœ ˜)šžœ ž˜šœ˜Kšœ.˜.Kšœ*˜*Kšœ˜—Kšžœ˜—K˜K˜—š Ÿ œžœžœžœžœžœ˜HKšœžœ žœ ˜'Kšžœ žœžœ7˜MKšœ˜Kšžœ ˜K˜—K˜š Ÿœžœžœžœžœžœ˜\Kšœžœ˜'Kšœ#˜#Kšžœ ˜K˜K˜—š Ÿ œžœžœžœžœ žœ˜Išžœžœžœ˜šžœ$žœž˜3Kšœžœžœ ˜)šžœ˜ Kšœžœ˜'Kš œ}žœžœžœžœžœ˜ÒKšœ˜Kšœ+˜+Kšœ@˜@Kšžœ ˜K˜———K˜—šŸ œ˜&Kšœžœ žœ˜-Kšœ5˜5Kšœ˜—K˜šŸ œžœžœ"žœžœ$žœ žœžœ žœžœ˜—Kšœžœ˜)Kšœ#˜#Kšœ$˜$Kšœ˜Kšœ˜K˜—K˜šŸœžœžœ žœžœGžœžœžœžœ žœžœžœ˜ÜKšœc˜cKšœ;žœ˜FKšœ˜K˜—šŸœžœžœ žœžœ1žœžœ žœžœ žœžœžœ˜ºKšœ,˜,Kšœ4˜4Kšœ˜—K˜š Ÿ œžœžœžœ žœ˜Jšžœ žœž˜Kšœžœžœ ˜)šœ˜šžœ!žœž˜0Kšœžœžœ ˜)Kšžœ˜—Kšœ˜—Kšžœ˜—K˜—K˜šŸœžœžœ;˜VK™!Kšœžœ#˜.Kšœ$ ˜?Kšœ˜K˜—šŸœžœžœ"žœ˜?Kšœžœ#˜.Kšœ ˜0Kšœ˜—K˜šŸœžœžœJžœžœžœžœ˜’Kšœžœžœ˜'šžœžœžœžœ˜8Kšœ[˜[Kšœ˜—Kšœ4˜4Kšœ˜—K˜šŸœžœžœžœžœžœžœžœ˜Kšžœžœž˜Kšœžœžœ˜Kšœžœžœžœ˜,Kšœžœžœ˜%Kšžœžœ ˜—Kšœ˜K˜—Kš œžœžœEžœžœžœ˜K˜šŸœ˜.Kšœ9˜9šžœ žœž˜šœžœ˜šžœ"žœžœž˜4Kšœžœ˜Kšœ/˜/Kšœžœ˜Kšœžœ˜Kšœ žœžœ˜.Kšžœžœžœ˜.šžœžœžœ˜Kšœ žœžœ˜-Kšžœžœžœ˜%Kšœ†˜†K˜—šžœžœžœž˜šœ˜Kšœ{˜{Kšžœžœžœ˜"Kšœn˜nšžœžœžœ˜Kšœ˜—K˜—šœ˜šžœ%˜'šžœ˜Kšžœžœžœ˜"Kšœj˜jK˜—šžœ˜Kšžœžœžœ˜%Kšœo˜ošžœžœžœ˜Kšœ&žœp˜™—K˜——K˜—Kšžœ˜—Kšžœžœžœ*˜;Kšžœ˜—K˜—Kšžœžœ˜—Kšžœ ˜K˜K˜—š Ÿ!œžœžœGžœžœžœ#˜©KšœžœžœP˜mKšœ žœ;˜HKšœ˜—K˜šŸœžœžœ+˜>Kšœ*˜*Kšœ˜Kšœ˜K˜K™—šŸ œžœ+˜;K™*šžœ(žœž˜7Kšœžœ&˜-Kšžœ˜—K˜K™—KšŸ œžœ˜-K˜Kšžœ˜K˜—…—Plª