1 | # ---------------------------------------------------------------------- |
---|
2 | # COMPONENT: grab - improved version of the Tk grab command |
---|
3 | # |
---|
4 | # This version of "grab" keeps a stack of grab windows, so one |
---|
5 | # window can steal and release the grab, and the grab will revert |
---|
6 | # to the previous window. If things get jammed up, you can press |
---|
7 | # <Escape> three times to release the grab. |
---|
8 | # ====================================================================== |
---|
9 | # AUTHOR: Michael McLennan, Purdue University |
---|
10 | # Copyright (c) 2004-2005 Purdue Research Foundation |
---|
11 | # |
---|
12 | # See the file "license.terms" for information on usage and |
---|
13 | # redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
---|
14 | # ====================================================================== |
---|
15 | package require BLT |
---|
16 | |
---|
17 | namespace eval Rappture { # forward declaration } |
---|
18 | namespace eval Rappture::grab { |
---|
19 | variable state "" ;# local ("") or global ("-global") grab |
---|
20 | variable stack "" ;# stack of grab windows |
---|
21 | } |
---|
22 | |
---|
23 | proc Rappture::grab::init {} { # used for autoloading this module } |
---|
24 | |
---|
25 | bind all <Escape><Escape><Escape> Rappture::grab::reset |
---|
26 | |
---|
27 | # ---------------------------------------------------------------------- |
---|
28 | # USAGE: grab ?-global? <window> |
---|
29 | # USAGE: grab set ?-global? <window> |
---|
30 | # USAGE: grab release <window> |
---|
31 | # USAGE: grab current ?<window>? |
---|
32 | # USAGE: grab status <window> |
---|
33 | # |
---|
34 | # This is a replacement for the usual Tk grab command. It works |
---|
35 | # exactly the same way, but supports a stack of grab windows, so |
---|
36 | # one window can steal grab from another, and then give it back |
---|
37 | # later. |
---|
38 | # ---------------------------------------------------------------------- |
---|
39 | rename grab _tk_grab |
---|
40 | proc grab {args} { |
---|
41 | set op [lindex $args 0] |
---|
42 | if {[winfo exists $op]} { |
---|
43 | set op "set" |
---|
44 | } elseif {$op == "-global" && [winfo exists [lindex $args end]]} { |
---|
45 | set op "set" |
---|
46 | } |
---|
47 | |
---|
48 | if {$op == "set"} { |
---|
49 | # |
---|
50 | # Handle GRAB SET specially. |
---|
51 | # Add the new grab window to the grab stack. |
---|
52 | # |
---|
53 | set state $::Rappture::grab::state |
---|
54 | set window [lindex $args end] |
---|
55 | if {[lsearch -exact $args -global] >= 0} { |
---|
56 | set state "-global" |
---|
57 | } |
---|
58 | |
---|
59 | if {"" != $state} { |
---|
60 | # if it's a global grab, store the -global flag away for later |
---|
61 | set window [linsert $window 0 $state] |
---|
62 | |
---|
63 | # all grabs from now on are global |
---|
64 | set ::Rappture::grab::state "-global" |
---|
65 | } |
---|
66 | |
---|
67 | # if the window is already on the stack, then skip it |
---|
68 | if {[string equal [lindex $::Rappture::grab::stack 0] $window]} { |
---|
69 | return $window |
---|
70 | } |
---|
71 | |
---|
72 | # add the current configuration to the grab stack |
---|
73 | set ::Rappture::grab::stack \ |
---|
74 | [linsert $::Rappture::grab::stack 0 $window] |
---|
75 | |
---|
76 | return [eval _grabset $window] |
---|
77 | |
---|
78 | } elseif {$op == "release"} { |
---|
79 | # |
---|
80 | # Handle GRAB RELEASE specially. |
---|
81 | # Release the current grab and grab the next window on the stack. |
---|
82 | # Note that the current grab is on the top of the stack. The |
---|
83 | # next one down is the one we want to revert to. |
---|
84 | # |
---|
85 | set window [lindex $::Rappture::grab::stack 1] |
---|
86 | set ::Rappture::grab::stack [lrange $::Rappture::grab::stack 1 end] |
---|
87 | |
---|
88 | # release the current grab |
---|
89 | eval _tk_grab $args |
---|
90 | |
---|
91 | # and set the next one |
---|
92 | if {"" != $window} { |
---|
93 | if {[lindex $window 0] != "-global"} { |
---|
94 | # no more global grabs -- resume local grabs |
---|
95 | set ::Rappture::grab::state "" |
---|
96 | } |
---|
97 | eval _grabset $window |
---|
98 | } |
---|
99 | return "" |
---|
100 | } |
---|
101 | |
---|
102 | # perform any other grab operation as usual... |
---|
103 | return [eval _tk_grab $args] |
---|
104 | } |
---|
105 | |
---|
106 | proc _grabset {args} { |
---|
107 | # give it 3 tries, if necessary |
---|
108 | for {set i 0} {$i < 3} {incr i} { |
---|
109 | set status [catch {eval _tk_grab set $args} result] |
---|
110 | if {$status == 0} { |
---|
111 | return $result |
---|
112 | } |
---|
113 | after 100; update |
---|
114 | } |
---|
115 | # oh well, we tried... |
---|
116 | return "" |
---|
117 | } |
---|
118 | |
---|
119 | # ---------------------------------------------------------------------- |
---|
120 | # USAGE: Rappture::grab::reset |
---|
121 | # |
---|
122 | # Used internally to reset the grab whenever the user presses |
---|
123 | # Escape a bunch of times to break out of the grab. |
---|
124 | # ---------------------------------------------------------------------- |
---|
125 | proc Rappture::grab::reset {} { |
---|
126 | set w [_tk_grab current] |
---|
127 | if {"" != $w} { |
---|
128 | _tk_grab release $w |
---|
129 | } |
---|
130 | set Rappture::grab::stack "" |
---|
131 | set Rappture::grab::state "" |
---|
132 | |
---|
133 | foreach win [blt::busy windows] { |
---|
134 | blt::busy release $win |
---|
135 | } |
---|
136 | } |
---|