]> icculus.org git repositories - dana/openbox.git/blob - scripts/focus.py
typos
[dana/openbox.git] / scripts / focus.py
1 ###########################################################################
2 ###          Functions for helping out with your window focus.          ###
3 ###########################################################################
4
5 ###########################################################################
6 ###         Options that affect the behavior of the focus module.       ###
7 ###                                                                     ###
8 # cycle_raise - raise the window also when it is focused                ###
9 cycle_raise = 1                                                         ###
10 # avoid_skip_taskbar - Don't focus windows which have requested to not  ###
11 ###                    be displayed in taskbars. You will still be able ###
12 ###                    to focus the windows, but not through cycling,   ###
13 ###                    and they won't be focused as a fallback if       ###
14 ###                    'fallback' is enabled.                           ###
15 avoid_skip_taskbar = 1                                                  ###
16 # stacked_cycle_raise - raise as you cycle in stacked mode              ###
17 stacked_cycle_raise = 0                                                 ###
18 # stacked_cycle_popup_list - show a pop-up list of windows while
19 ###                          cycling                                    ###
20 stacked_cycle_popup_list = 1                                            ###
21 # send focus somewhere when nothing is left with the focus, if possible ###
22 fallback = 0                                                            ###
23 ###                                                                     ###
24 ###                                                                     ###
25 # Provides:                                                             ###
26 # def focus_next_stacked(data, forward=1):                              ###
27 #   """Focus the next (or previous, with forward=0) window in a stacked ###
28 #      order."""                                                        ###
29 # def focus_prev_stacked(data):                                         ###
30 #   """Focus the previous window in a stacked order."""                 ###
31 # def focus_next(data, num=1, forward=1):                               ###
32 #   """Focus the next (or previous, with forward=0) window in a linear  ###
33 #      order."""                                                        ###
34 # def focus_prev(data, num=1):                                          ###
35 #   """Focus the previous window in a linear order."""                  ###
36 ###                                                                     ###
37 # All of these functions call be used as callbacks for bindings         ###
38 # directly.                                                             ###
39 ###                                                                     ###
40 ###########################################################################
41
42 import otk
43 import ob
44
45 # maintain a list of clients, stacked in focus order
46 _clients = []
47 # maintaint he current focused window
48 _doing_stacked = 0
49
50 def _new_win(data):
51     global _clients
52     global _doing_stacked
53     global _cyc_w;
54
55     if _doing_stacked:
56         _clients.insert(_clients.index(_cyc_w), data.client.window())
57         _create_popup_list(data)
58         _hilite_popup_list(data)
59     else:
60         if not len(_clients):
61             _clients.append(data.client.window())
62         else:
63             _clients.insert(1, data.client.window()) # insert in 2nd slot
64
65 def _close_win(data):
66     global _clients
67     global _cyc_w;
68     global _doing_stacked
69
70     if not _doing_stacked:
71         # not in the middle of stacked cycling, so who cares
72         _clients.remove(data.client.window())
73     else:
74         # have to fix the cycling if we remove anything
75         win = data.client.window()
76         if _cyc_w == win:
77             _do_stacked_cycle(data, 1) # cycle off the window first, forward
78         _clients.remove(win)
79         _create_popup_list(data)
80
81 def _focused(data):
82     global _clients
83     global _doing_stacked
84     global _cyc_w
85     
86     if data.client:
87         if not _doing_stacked: # only move the window when we're not cycling
88             win = data.client.window()
89             # move it to the top
90             _clients.remove(win)
91             _clients.insert(0, win)
92         else: # if we are cycling, then update our pointer
93             _cyc_w = data.client.window()
94             _hilite_popup_list(data)
95     elif fallback: 
96         # pass around focus
97         desktop = ob.openbox.screen(_cyc_screen).desktop()
98         for w in _clients:
99             client = ob.openbox.findClient(w)
100             if client and not (avoid_skip_taskbar and client.skipTaskbar()) \
101                    and (client.desktop() == desktop or
102                         client.desktop() == 0xffffffff) \
103                         and client.normal() and client.focus():
104                 break
105         if _doing_stacked:
106             _cyc_w = 0
107             _hilite_popup_list(data)
108
109 _cyc_mask = 0
110 _cyc_key = 0
111 _cyc_w = 0 # last window cycled to
112 _cyc_screen = 0
113
114 def _do_stacked_cycle(data, forward):
115     global _cyc_w
116     global stacked_cycle_raise
117     global _clients
118
119     clients = _clients[:] # make a copy
120
121     if not forward:
122         clients.reverse()
123
124     try:
125         i = clients.index(_cyc_w) + 1
126     except ValueError:
127         i = 1
128     clients = clients[i:] + clients[:i]
129         
130     desktop = ob.openbox.screen(data.screen).desktop()
131     for w in clients:
132         client = ob.openbox.findClient(w)
133                    
134         if client and not (avoid_skip_taskbar and client.skipTaskbar()) and \
135                (client.desktop() == desktop or client.desktop() == 0xffffffff)\
136                 and client.normal() and client.focus():
137             if stacked_cycle_raise:
138                 ob.openbox.screen(data.screen).raiseWindow(client)
139             return
140
141 def _focus_stacked_ungrab(data):
142     global _cyc_mask;
143     global _cyc_key;
144     global _doing_stacked;
145
146     if data.action == ob.KeyAction.Release:
147         # have all the modifiers this started with been released?
148         if not _cyc_mask & data.state:
149             _destroy_popup_list()
150             ob.kungrab()
151             ob.mungrab()
152             _doing_stacked = 0;
153             if cycle_raise:
154                 client = ob.openbox.findClient(_cyc_w)
155                 if client:
156                     ob.openbox.screen(data.screen).raiseWindow(client)
157
158 _list_widget = 0
159 _list_labels = []
160 _list_windows = []
161
162 def _hilite_popup_list(data):
163     global _cyc_w, _doing_stacked
164     global _list_widget, _list_labels, _list_windows
165     found = 0
166
167     if not _list_widget and _doing_stacked:
168         _create_popup_list(data)
169     
170     if _list_widget:
171         i = 0
172         for w in _list_windows:
173             if w == _cyc_w:
174                 _list_labels[i].focus()
175                 found = 1
176             else:
177                 _list_labels[i].unfocus()
178             i += 1
179     if not found:
180         _create_popup_list(data)
181
182 def _destroy_popup_list():
183     global _list_widget, _list_labels, _list_windows
184     if _list_widget:
185         _list_windows = []
186         _list_labels = []
187         _list_widget = 0
188     
189 def _create_popup_list(data):
190     global avoid_skip_taskbar
191     global _list_widget, _list_labels, _list_windows, _clients
192
193     if _list_widget:
194         _destroy_popup_list()
195     
196     style = ob.openbox.screen(data.screen).style()
197     _list_widget = otk.Widget(ob.openbox, style,
198                               otk.Widget.Vertical, 0,
199                               style.bevelWidth(), 1)
200     t = style.titlebarFocusBackground()
201     _list_widget.setTexture(t)
202
203     titles = []
204     font = style.labelFont()
205     height = font.height()
206     longest = 0
207     for c in _clients:
208         client = ob.openbox.findClient(c)
209         desktop = ob.openbox.screen(data.screen).desktop()
210         if client and not (avoid_skip_taskbar and client.skipTaskbar()) and \
211                (client.desktop() == desktop or client.desktop() == 0xffffffff)\
212                and client.normal() and (client.canFocus() or
213                                         client.focusNotify()):
214             t = client.title()
215             if len(t) > 50: # limit the length of titles
216                 t = t[:24] + "..." + t[-24:]
217             titles.append(t)
218             _list_windows.append(c)
219             l = font.measureString(t)
220             if l > longest: longest = l
221     if len(titles) > 1:
222         for t in titles:
223             w = otk.FocusLabel(_list_widget)
224             w.fitSize(longest, height)
225             w.setText(t)
226             w.unfocus()
227             _list_labels.append(w)
228         _list_widget.update()
229         area = otk.display.screenInfo(data.screen).rect()
230         _list_widget.move(area.x() + (area.width() -
231                                       _list_widget.width()) / 2,
232                           area.y() + (area.height() -
233                                       _list_widget.height()) / 2)
234         _list_widget.show(1)
235     else:
236         _destroy_popup_list() # nothing (or only 1) to list
237
238 def focus_next_stacked(data, forward=1):
239     """Focus the next (or previous, with forward=0) window in a stacked
240        order."""
241     global _cyc_mask
242     global _cyc_key
243     global _cyc_w
244     global _cyc_screen
245     global _doing_stacked
246
247     if _doing_stacked:
248         if _cyc_key == data.key:
249             _do_stacked_cycle(data,forward)
250     else:
251         _cyc_mask = data.state
252         _cyc_key = data.key
253         _cyc_w = 0
254         _cyc_screen = data.screen
255         _doing_stacked = 1
256
257         global stacked_cycle_popup_list
258         if stacked_cycle_popup_list:
259             _create_popup_list(data)
260
261         ob.kgrab(data.screen, _focus_stacked_ungrab)
262         # the pointer grab causes pointer events during the keyboard grab to
263         # go away, which means we don't get enter notifies when the popup
264         # disappears, screwing up the focus
265         ob.mgrab(data.screen)
266         focus_next_stacked(data, forward) # start with the first press
267
268 def focus_prev_stacked(data):
269     """Focus the previous window in a stacked order."""
270     focus_next_stacked(data, forward=0)
271
272 def focus_next(data, num=1, forward=1):
273     """Focus the next (or previous, with forward=0) window in a linear
274        order."""
275     global avoid_skip_taskbar
276
277     screen = ob.openbox.screen(data.screen)
278     count = screen.clientCount()
279
280     if not count: return # no clients
281     
282     target = 0
283     if data.client:
284         client_win = data.client.window()
285         found = 0
286         r = range(count)
287         if not forward:
288             r.reverse()
289         for i in r:
290             if found:
291                 target = i
292                 found = 2
293                 break
294             elif screen.client(i).window() == client_win:
295                 found = 1
296         if found == 1: # wraparound
297             if forward: target = 0
298             else: target = count - 1
299
300     t = target
301     curdesk = screen.desktop()
302     while 1:
303         client = screen.client(t)
304         if not (avoid_skip_taskbar and client.skipTaskbar()) and \
305                client.normal() and (client.desktop() == curdesk or
306                                     client.desktop() == 0xffffffff)\
307                                     and client.focus():
308             if cycle_raise:
309                 screen.raiseWindow(client)
310             return
311         if forward:
312             t += num
313             if t >= count: t -= count
314         else:
315             t -= num
316             if t < 0: t += count
317         if t == target: return # nothing to focus
318
319 def focus_prev(data, num=1):
320     """Focus the previous window in a linear order."""
321     focus_next(data, num, forward=0)
322
323
324 ob.ebind(ob.EventAction.NewWindow, _new_win)
325 ob.ebind(ob.EventAction.CloseWindow, _close_win)
326 ob.ebind(ob.EventAction.Focus, _focused)
327
328 print "Loaded focus.py"