add setup_fallback_focus() to handle focus when nothing is left focused
[mikachu/openbox.git] / scripts / builtins.py
1 ###########################################################################
2 ### Functions that can be used as callbacks for mouse/keyboard bindings ###
3 ###########################################################################
4
5 def state_above(data, add=2):
6     """Toggles, adds or removes the 'above' state on a window."""
7     if not data.client: return
8     send_client_msg(OBDisplay_screenInfo(data.screen).rootWindow(),
9                     OBProperty.net_wm_state, data.client.window(), add,
10                     openbox.property().atom(OBProperty.net_wm_state_above))
11     
12 def state_below(data, add=2):
13     """Toggles, adds or removes the 'below' state on a window."""
14     if not data.client: return
15     send_client_msg(OBDisplay_screenInfo(data.screen).rootWindow(),
16                     OBProperty.net_wm_state, data.client.window(), add,
17                     openbox.property().atom(OBProperty.net_wm_state_below))
18     
19 def state_shaded(data, add=2):
20     """Toggles, adds or removes the 'shaded' state on a window."""
21     if not data.client: return
22     send_client_msg(OBDisplay_screenInfo(data.screen).rootWindow(),
23                     OBProperty.net_wm_state, data.client.window(), add,
24                     openbox.property().atom(OBProperty.net_wm_state_shaded))
25     
26 def close(data):
27     """Closes the window on which the event occured"""
28     if not data.client: return
29     send_client_msg(OBDisplay_screenInfo(data.screen).rootWindow(),
30                     OBProperty.net_close_window, data.client.window(), 0)
31
32 def focus(data):
33     """Focuses the window on which the event occured"""
34     if not data.client: return
35     # !normal windows dont get focus from window enter events
36     if data.action == EventEnterWindow and not data.client.normal():
37         return
38     data.client.focus()
39
40 def move(data):
41     """Moves the window interactively. This should only be used with
42        MouseMotion events"""
43     if not data.client: return
44
45     # !normal windows dont get moved
46     if not data.client.normal(): return
47
48     dx = data.xroot - data.pressx
49     dy = data.yroot - data.pressy
50     data.client.move(data.press_clientx + dx, data.press_clienty + dy)
51
52 def resize(data):
53     """Resizes the window interactively. This should only be used with
54        MouseMotion events"""
55     if not data.client: return
56
57     # !normal windows dont get moved
58     if not data.client.normal(): return
59
60     px = data.pressx
61     py = data.pressy
62     dx = data.xroot - px
63     dy = data.yroot - py
64
65     # pick a corner to anchor
66     if not (resize_nearest or data.context == MC_Grip):
67         corner = OBClient.TopLeft
68     else:
69         x = px - data.press_clientx
70         y = py - data.press_clienty
71         if y < data.press_clientheight / 2:
72             if x < data.press_clientwidth / 2:
73                 corner = OBClient.BottomRight
74                 dx *= -1
75             else:
76                 corner = OBClient.BottomLeft
77             dy *= -1
78         else:
79             if x < data.press_clientwidth / 2:
80                 corner = OBClient.TopRight
81                 dx *= -1
82             else:
83                 corner = OBClient.TopLeft
84
85     data.client.resize(corner,
86                        data.press_clientwidth + dx,
87                        data.press_clientheight + dy);
88
89 def restart(data):
90     """Restarts openbox"""
91     openbox.restart("")
92
93 def raise_win(data):
94     """Raises the window on which the event occured"""
95     if not data.client: return
96     openbox.screen(data.screen).restack(1, data.client)
97
98 def lower_win(data):
99     """Lowers the window on which the event occured"""
100     if not data.client: return
101     openbox.screen(data.screen).restack(0, data.client)
102
103 def toggle_shade(data):
104     """Toggles the shade status of the window on which the event occured"""
105     state_shaded(data)
106
107 def shade(data):
108     """Shades the window on which the event occured"""
109     state_shaded(data, 1)
110
111 def unshade(data):
112     """Unshades the window on which the event occured"""
113     state_shaded(data, 0)
114
115 def change_desktop(data, num):
116     """Switches to a specified desktop"""
117     root = OBDisplay_screenInfo(data.screen).rootWindow()
118     send_client_msg(root, OBProperty.net_current_desktop, root, num)
119
120 def next_desktop(data, no_wrap=0):
121     """Switches to the next desktop, optionally (by default) cycling around to
122        the first when going past the last."""
123     screen = openbox.screen(data.screen)
124     d = screen.desktop()
125     n = screen.numDesktops()
126     if (d < (n-1)):
127         d = d + 1
128     elif not no_wrap:
129         d = 0
130     change_desktop(data, d)
131     
132 def prev_desktop(data, no_wrap=0):
133     """Switches to the previous desktop, optionally (by default) cycling around
134        to the last when going past the first."""
135     screen = openbox.screen(data.screen)
136     d = screen.desktop()
137     n = screen.numDesktops()
138     if (d > 0):
139         d = d - 1
140     elif not no_wrap:
141         d = n - 1
142     change_desktop(data, d)
143
144 def send_to_desktop(data, num):
145     """Sends a client to a specified desktop"""
146     if not data.client: return
147     send_client_msg(OBDisplay_screenInfo(data.screen).rootWindow(),
148                     OBProperty.net_wm_desktop, data.client.window(), num)
149
150 def send_to_next_desktop(data, no_wrap=0, follow=1):
151     """Sends a window to the next desktop, optionally (by default) cycling
152        around to the first when going past the last. Also optionally moving to
153        the new desktop after sending the window."""
154     if not data.client: return
155     screen = openbox.screen(data.screen)
156     d = screen.desktop()
157     n = screen.numDesktops()
158     if (d < (n-1)):
159         d = d + 1
160     elif not no_wrap:
161         d = 0
162     send_to_desktop(data, d)
163     if follow:
164         change_desktop(data, d)
165     
166 def send_to_prev_desktop(data, no_wrap=0, follow=1):
167     """Sends a window to the previous desktop, optionally (by default) cycling
168        around to the last when going past the first. Also optionally moving to
169        the new desktop after sending the window."""
170     if not data.client: return
171     screen = openbox.screen(data.screen)
172     d = screen.desktop()
173     n = screen.numDesktops()
174     if (d > 0):
175         d = d - 1
176     elif not no_wrap:
177         d = n - 1
178     send_to_desktop(data, d)
179     if follow:
180         change_desktop(data, d)
181
182 #########################################
183 ### Convenience functions for scripts ###
184 #########################################
185
186 def execute(bin, screen = 0):
187     """Executes a command on the specified screen. It is recommended that you
188        use this call instead of a python system call. If the specified screen
189        is beyond your range of screens, the default is used instead."""
190     openbox.execute(screen, bin)
191
192 def setup_click_focus(click_raise = 1):
193     """Sets up for focusing windows by clicking on or in the window.
194        Optionally, clicking on or in a window can raise the window to the
195        front of its stacking layer."""
196     mbind("1", MC_Titlebar, MousePress, focus)
197     mbind("1", MC_Handle, MousePress, focus)
198     mbind("1", MC_Grip, MousePress, focus)
199     mbind("1", MC_Window, MousePress, focus)
200     if click_raise:
201         mbind("1", MC_Titlebar, MousePress, raise_win)
202         mbind("1", MC_Handle, MousePress, raise_win)
203         mbind("1", MC_Grip, MousePress, raise_win)
204         mbind("1", MC_Window, MousePress, raise_win)
205
206 def setup_sloppy_focus(click_focus = 1, click_raise = 0):
207     """Sets up for focusing windows when the mouse pointer enters them.
208        Optionally, clicking on or in a window can focus it if your pointer
209        ends up inside a window without focus. Also, optionally, clicking on or
210        in a window can raise the window to the front of its stacking layer."""
211     ebind(EventEnterWindow, focus)
212     if click_focus:
213         setup_click_focus(click_raise)
214
215 def setup_window_clicks():
216     """Sets up the default bindings for various mouse buttons for various
217        contexts.
218        This includes:
219         * Alt-left drag anywhere on a window will move it
220         * Alt-right drag anywhere on a window will resize it
221         * Left drag on a window's titlebar/handle will move it
222         * Left drag on a window's handle grips will resize it
223         * Alt-left press anywhere on a window's will raise it to the front of
224           its stacking layer.
225         * Left press on a window's titlebar/handle will raise it to the front
226           of its stacking layer.
227         * Alt-middle click anywhere on a window's will lower it to the bottom
228           of its stacking layer.
229         * Middle click on a window's titlebar/handle will lower it to the
230           bottom of its stacking layer.
231         * Double-left click on a window's titlebar will toggle shading it
232     """
233     mbind("A-1", MC_Frame, MouseMotion, move)
234     mbind("1", MC_Titlebar, MouseMotion, move)
235     mbind("1", MC_Handle, MouseMotion, move)
236
237     mbind("A-3", MC_Frame, MouseMotion, resize)
238     mbind("1", MC_Grip, MouseMotion, resize)
239
240     mbind("1", MC_Titlebar, MousePress, raise_win)
241     mbind("1", MC_Handle, MousePress, raise_win)
242     mbind("A-1", MC_Frame, MousePress, raise_win)
243     mbind("A-2", MC_Frame, MouseClick, lower_win)
244     mbind("2", MC_Titlebar, MouseClick, lower_win)
245     mbind("2", MC_Handle, MouseClick, lower_win)
246
247     mbind("1", MC_Titlebar, MouseDoubleClick, toggle_shade)
248
249 def setup_window_buttons():
250     """Sets up the default behaviors for the buttons in the window titlebar."""
251     mbind("1", MC_CloseButton, MouseClick, close)
252
253 def setup_scroll():
254     """Sets up the default behaviors for the mouse scroll wheel.
255        This includes:
256         * scrolling on a window titlebar will shade/unshade it
257         * alt-scrolling anywhere will switch to the next/previous desktop
258         * control-alt-scrolling on a window will send it to the next/previous
259           desktop, and switch to the desktop with the window
260     """
261     mbind("4", MC_Titlebar, MouseClick, shade)
262     mbind("5", MC_Titlebar, MouseClick, unshade)
263
264     mbind("A-4", MC_Frame, MouseClick, next_desktop)
265     mbind("A-4", MC_Root, MouseClick, next_desktop)
266     mbind("A-5", MC_Frame, MouseClick, prev_desktop)
267     mbind("A-5", MC_Root, MouseClick, prev_desktop)
268
269     mbind("C-A-4", MC_Frame, MouseClick, send_to_next_desktop)
270     mbind("C-A-5", MC_Frame, MouseClick, send_to_prev_desktop)
271
272 focus_stack = []
273 def setup_fallback_focus():
274     """Sets up a focus fallback routine so that when no windows are focused,
275        the last window on the desktop that had focus will be focused."""
276     def focused(data):
277         global focus_stack
278         if data.client:
279             window = data.client.window()
280             # add to front the stack
281             if window in focus_stack:
282                 focus_stack.remove(window)
283             focus_stack.insert(0, window)
284         else:
285             # pass around focus
286             desktop = openbox.screen(data.screen).desktop()
287             l = len(focus_stack)
288             i = 0
289             while i < l:
290                 w = focus_stack[i]
291                 client = openbox.findClient(w)
292                 if not client: # window is gone, remove it
293                     focus_stack.pop(i)
294                     l = l - 1
295                 elif client.desktop() == desktop and \
296                          client.normal() and client.focus():
297                     break
298                 else:
299                     i = i + 1
300
301     ebind(EventFocus, focused)
302
303     
304 ############################################################################
305 ### Window placement algorithms, choose one of these and ebind it to the ###
306 ### EventPlaceWindow action.                                             ###
307 ############################################################################
308
309 ob_rand = None
310 import random
311 def placewindows_random(data):
312     if not data.client: return
313     client_area = data.client.area()
314     screen = OBDisplay_screenInfo(data.screen)
315     width = screen.width() - client_area.width()
316     height = screen.height() - client_area.height()
317     global ob_rand
318     if not ob_rand: ob_rand = random.Random()
319     x = ob_rand.randrange(0, width-1)
320     y = ob_rand.randrange(0, height-1)
321     data.client.move(x, y)
322
323
324 print "Loaded builtins.py"