1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
import ob, config, hooks, focus
from input import Pointer, Keyboard
config.add('stackedcycle',
'activate_while_cycling',
'Activate While Cycling',
"If this is True then windows will be activated as they are" + \
"highlighted in the cycling list (except iconified windows).",
'boolean',
True)
config.add('stackedcycle',
'raise_window',
'Raise After Cycling',
"If this is True, the selected window will be raised as well as " +\
"focused.",
'boolean',
True)
config.add('stackedcycle',
'include_all_desktops',
'Include Windows From All Desktops',
"If this is True then windows from all desktops will be included" +\
" in the stacking list.",
'boolean',
False)
config.add('stackedcycle',
'include_icons',
'Include Icons',
"If this is True then windows which are iconified on the current" +\
" desktop will be included in the stacking list.",
'boolean',
False)
config.add('stackedcycle',
'include_icons_all_desktops',
'Include Icons From All Desktops',
"If this is True then windows which are iconified from all " +\
"desktops will be included in the stacking list (if Include Icons"+\
" is also True).",
'boolean',
True)
config.add('stackedcycle',
'include_omnipresent',
'Include Omnipresent Windows',
"If this is True then windows which are on all-desktops at once " +\
"will be included in the stacking list.",
'boolean',
True)
def next(keydata, client): _cycle(keydata, client, True)
def previous(keydata, client): _cycle(keydata, client, False)
def _shouldAdd(client):
"""Determines if a client should be added to the cycling list."""
curdesk = ob.Openbox.desktop()
desk = client.desktop()
if not (client.normal() and client.canFocus()): return False
if config.get('focus', 'avoid_skip_taskbar') and client.skipTaskbar():
return False
if client.iconic():
if config.get('stackedcycle', 'include_icons'):
if config.get('stackedcycle', 'include_icons_all_desktops'):
return True
if desk == curdesk: return True
return False
if config.get('stackedcycle', 'include_omnipresent') and \
desk == 0xffffffff: return True
if config.get('stackedcycle', 'include_all_desktops'): return True
if desk == curdesk: return True
return False
def _populateItems():
global _items
# get the list of clients, keeping iconic windows at the bottom
_items = []
iconic_clients = []
for c in focus._clients:
if _shouldAdd(c):
if c.iconic(): iconic_clients.append(c)
else: _items.append(c)
_items.extend(iconic_clients)
def _populate():
global _pos, _items
try:
current = _items[_pos]
except IndexError:
current = None
oldpos = _pos
_pos = -1
_populateItems()
i = 0
for item in _items:
# current item might have shifted after a populateItems()
# call, so we need to do this test.
if current == item:
_pos = i
i += 1
# The item we were on might be gone entirely
if _pos < 0:
# try stay at the same spot in the menu
if oldpos >= len(_items):
_pos = len(_items) - 1
else:
_pos = oldpos
def _activate(final):
"""
Activates (focuses and, if the user requested it, raises a window).
If final is True, then this is the very last window we're activating
and the user has finished cycling.
"""
print "_activate"
try:
client = _items[_pos]
except IndexError: return # empty list
print client
# move the to client's desktop if required
if not (client.iconic() or client.desktop() == 0xffffffff or \
client.desktop() == ob.Openbox.desktop()):
ob.Openbox.setDesktop(client.desktop())
if final or not client.iconic():
if final: r = config.get('stackedcycle', 'raise_window')
else: r = False
client.focus(True)
if final and client.shaded(): client.setShaded(False)
print "final", final, "raising", r
if r: client.raiseWindow()
if not final:
focus._skip += 1
def _cycle(keydata, client, forward):
global _cycling, _state, _pos, _inititem, _items
if not _cycling:
_items = [] # so it doesnt try start partway through the list
_populate()
if not _items: return # don't bother doing anything
Keyboard.grab(_grabfunc)
# the pointer grab causes pointer events during the keyboard grab
# to go away, which means we don't get enter notifies when the
# popup disappears, screwing up the focus
Pointer.grabPointer(True)
_cycling = True
_state = keydata.state
_pos = 0
_inititem = _items[_pos]
if forward:
_pos += 1
else:
_pos -= 1
# wrap around
if _pos < 0: _pos = len(_items) - 1
elif _pos >= len(_items): _pos = 0
if config.get('stackedcycle', 'activate_while_cycling'):
_activate(False) # activate, but dont deiconify/unshade/raise
def _grabfunc(keydata, client):
global _cycling
done = False
notreverting = True
# have all the modifiers this started with been released?
if not _state & keydata.state:
done = True
elif keydata.press:
# has Escape been pressed?
if keydata.keychain == "Escape":
done = True
notreverting = False
# revert
try:
_pos = _items.index(_inititem)
except:
_pos = -1
# has Enter been pressed?
elif keydata.keychain == "Return":
done = True
if done:
# activate, and deiconify/unshade/raise
_activate(notreverting)
_cycling = False
Keyboard.ungrab()
Pointer.grabPointer(False)
_cycling = False
_pos = 0
_inititem = None
_items = []
_state = 0
def _newwin(data):
if _cycling: _populate()
def _closewin(data):
if _cycling: _populate()
hooks.managed.append(_newwin)
hooks.closed.append(_closewin)
|