all new stacked cycling code. so much sexy.
[mikachu/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 avoid_skip_taskbar = 1
9 """Don't focus windows which have requested to not be displayed in taskbars.
10    You will still be able to focus the windows, but not through cycling, and
11    they won't be focused as a fallback if 'fallback' is enabled."""
12 raise_window = 1
13 """When cycling focus, raise the window chosen as well as focusing it. This
14    does not affect fallback focusing behavior."""
15 fallback = 0
16 """Send focus somewhere when nothing is left with the focus, if possible."""
17 ###########################################################################
18
19 def next(data, num=1):
20     """Focus the next window."""
21     _cycle(data, num, 1)
22
23 def previous(data, num=1):
24     """Focus the previous window."""
25     _cycle(data, num, 0)
26
27 ###########################################################################
28 ###########################################################################
29
30 ###########################################################################
31 ###      Internal stuff, should not be accessed outside the module.     ###
32 ###########################################################################
33
34 import otk
35 import ob
36
37 # maintain a list of clients, stacked in focus order
38 _clients = []
39 _disable = 0
40
41 def _focusable(client, desktop):
42     if not client.normal(): return 0
43     if not (client.canFocus() or client.focusNotify()): return 0
44     if avoid_skip_taskbar and client.skipTaskbar(): return 0
45
46     desk = client.desktop()
47     if not (desk == 0xffffffff or desk == desktop): return 0
48
49     return 1
50
51 def _focused(data):
52     global _clients
53
54     if _disable: return
55
56     if data.client:
57         win = data.client.window()
58         # move it to the top
59         _clients.remove(win)
60         _clients.insert(0, win)
61     elif fallback:
62         # pass around focus
63         desktop = ob.openbox.screen(data.screen).desktop()
64         for w in _clients:
65             client = ob.openbox.findClient(w)
66             if client and _focusable(client, desktop) and client.focus():
67                 break
68
69 def _newwindow(data):
70     _clients.append(data.client.window())
71         
72 def _closewindow(data):
73     try:
74         focus._clients.remove(data.client.window())
75     except ValueError: pass
76
77 ob.ebind(ob.EventAction.NewWindow, _newwindow)
78 ob.ebind(ob.EventAction.CloseWindow, _closewindow)
79 ob.ebind(ob.EventAction.Focus, _focused)
80
81 def _cycle(data, num, forward):
82     global avoid_skip_taskbar
83     
84     screen = ob.openbox.screen(data.screen)
85     count = screen.clientCount()
86
87     if not count: return # no clients
88     
89     target = 0
90     if data.client:
91         client_win = data.client.window()
92         found = 0
93         r = range(count)
94         if not forward:
95             r.reverse()
96         for i in r:
97             if found:
98                 target = i
99                 found = 2
100                 break
101             elif screen.client(i).window() == client_win:
102                 found = 1
103         if found == 1: # wraparound
104             if forward: target = 0
105             else: target = count - 1
106
107         t = target
108         desktop = screen.desktop()
109         while 1:
110             client = screen.client(t)
111             if client and _focusable(client, desktop) and client.focus():
112                 if cycle_raise:
113                     screen.raiseWindow(client)
114                 return
115             if forward:
116                 t += num
117                 if t >= count: t -= count
118             else:
119                 t -= num
120                 if t < 0: t += count
121             if t == target: return # nothing to focus
122             
123 print "Loaded focus.py"