remove the word "sticky" from everywhere, and replace with "all desktops". change...
[dana/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(display.screenInfo(data.screen).rootWindow(),
9                     Property_atoms().net_wm_state, data.client.window(), add,
10                     Property_atoms().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(display.screenInfo(data.screen).rootWindow(),
16                     Property_atoms().net_wm_state, data.client.window(), add,
17                     Property_atoms().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(display.screenInfo(data.screen).rootWindow(),
23                     Property_atoms().net_wm_state, data.client.window(), add,
24                     Property_atoms().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(display.screenInfo(data.screen).rootWindow(),
30                     Property_atoms().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 = Client.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 = Client.BottomRight
74                 dx *= -1
75             else:
76                 corner = Client.BottomLeft
77             dy *= -1
78         else:
79             if x < data.press_clientwidth / 2:
80                 corner = Client.TopRight
81                 dx *= -1
82             else:
83                 corner = Client.TopLeft
84
85     data.client.resize(corner,
86                        data.press_clientwidth + dx,
87                        data.press_clientheight + dy);
88
89 def restart(data, other = ""):
90     """Restarts openbox, optionally starting another window manager."""
91     openbox.restart(other)
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).raiseWindow(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).lowerWindow(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 = display.screenInfo(data.screen).rootWindow()
118     send_client_msg(root, Property_atoms().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(display.screenInfo(data.screen).rootWindow(),
148                     Property_atoms().net_wm_desktop, data.client.window(), num)
149
150 def toggle_all_desktops(data):
151     """Toggles between sending a client to all desktops and to the current
152        desktop."""
153     if not data.client: return
154     if not data.client.desktop() == 0xffffffff:
155         send_to_desktop(data, 0xffffffff)
156     else:
157         send_to_desktop(data, openbox.screen(data.screen).desktop())
158     
159 def send_to_all_desktops(data):
160     """Sends a client to all desktops"""
161     if not data.client: return
162     send_to_desktop(data, 0xffffffff)
163     
164 def send_to_next_desktop(data, no_wrap=0, follow=1):
165     """Sends a window to the next desktop, optionally (by default) cycling
166        around to the first when going past the last. Also optionally moving to
167        the new desktop after sending the window."""
168     if not data.client: return
169     screen = openbox.screen(data.screen)
170     d = screen.desktop()
171     n = screen.numDesktops()
172     if (d < (n-1)):
173         d = d + 1
174     elif not no_wrap:
175         d = 0
176     send_to_desktop(data, d)
177     if follow:
178         change_desktop(data, d)
179     
180 def send_to_prev_desktop(data, no_wrap=0, follow=1):
181     """Sends a window to the previous desktop, optionally (by default) cycling
182        around to the last when going past the first. Also optionally moving to
183        the new desktop after sending the window."""
184     if not data.client: return
185     screen = openbox.screen(data.screen)
186     d = screen.desktop()
187     n = screen.numDesktops()
188     if (d > 0):
189         d = d - 1
190     elif not no_wrap:
191         d = n - 1
192     send_to_desktop(data, d)
193     if follow:
194         change_desktop(data, d)
195
196 #########################################
197 ### Convenience functions for scripts ###
198 #########################################
199
200 def execute(bin, screen = 0):
201     """Executes a command on the specified screen. It is recommended that you
202        use this call instead of a python system call. If the specified screen
203        is beyond your range of screens, the default is used instead."""
204     openbox.execute(screen, bin)
205
206 def setup_click_focus(click_raise = 1):
207     """Sets up for focusing windows by clicking on or in the window.
208        Optionally, clicking on or in a window can raise the window to the
209        front of its stacking layer."""
210     mbind("Left", MC_Titlebar, MousePress, focus)
211     mbind("Left", MC_Handle, MousePress, focus)
212     mbind("Left", MC_Grip, MousePress, focus)
213     mbind("Left", MC_Window, MousePress, focus)
214     if click_raise:
215         mbind("Left", MC_Titlebar, MousePress, raise_win)
216         mbind("Left", MC_Handle, MousePress, raise_win)
217         mbind("Left", MC_Grip, MousePress, raise_win)
218         mbind("Left", MC_Window, MousePress, raise_win)
219
220 def setup_sloppy_focus(click_focus = 1, click_raise = 0):
221     """Sets up for focusing windows when the mouse pointer enters them.
222        Optionally, clicking on or in a window can focus it if your pointer
223        ends up inside a window without focus. Also, optionally, clicking on or
224        in a window can raise the window to the front of its stacking layer."""
225     ebind(EventEnterWindow, focus)
226     if click_focus:
227         setup_click_focus(click_raise)
228
229 def setup_window_clicks():
230     """Sets up the default bindings for various mouse buttons for various
231        contexts.
232        This includes:
233         * Alt-left drag anywhere on a window will move it
234         * Alt-right drag anywhere on a window will resize it
235         * Left drag on a window's titlebar/handle will move it
236         * Left drag on a window's handle grips will resize it
237         * Alt-left press anywhere on a window's will raise it to the front of
238           its stacking layer.
239         * Left press on a window's titlebar/handle will raise it to the front
240           of its stacking layer.
241         * Alt-middle click anywhere on a window's will lower it to the bottom
242           of its stacking layer.
243         * Middle click on a window's titlebar/handle will lower it to the
244           bottom of its stacking layer.
245         * Double-left click on a window's titlebar will toggle shading it
246     """
247     mbind("A-Left", MC_Frame, MouseMotion, move)
248     mbind("Left", MC_Titlebar, MouseMotion, move)
249     mbind("Left", MC_Handle, MouseMotion, move)
250
251     mbind("A-Right", MC_Frame, MouseMotion, resize)
252     mbind("Left", MC_Grip, MouseMotion, resize)
253
254     mbind("Left", MC_Titlebar, MousePress, raise_win)
255     mbind("Left", MC_Handle, MousePress, raise_win)
256     mbind("A-Left", MC_Frame, MousePress, raise_win)
257     mbind("A-Middle", MC_Frame, MouseClick, lower_win)
258     mbind("Middle", MC_Titlebar, MouseClick, lower_win)
259     mbind("Middle", MC_Handle, MouseClick, lower_win)
260
261     mbind("Left", MC_Titlebar, MouseDoubleClick, toggle_shade)
262
263 def setup_window_buttons():
264     """Sets up the default behaviors for the buttons in the window titlebar."""
265     mbind("Left", MC_AllDesktopsButton, MouseClick, toggle_all_desktops)
266     mbind("Left", MC_CloseButton, MouseClick, close)
267
268 def setup_scroll():
269     """Sets up the default behaviors for the mouse scroll wheel.
270        This includes:
271         * scrolling on a window titlebar will shade/unshade it
272         * alt-scrolling anywhere will switch to the next/previous desktop
273         * control-alt-scrolling on a window will send it to the next/previous
274           desktop, and switch to the desktop with the window
275     """
276     mbind("Up", MC_Titlebar, MouseClick, shade)
277     mbind("Down", MC_Titlebar, MouseClick, unshade)
278
279     mbind("A-Up", MC_Frame, MouseClick, next_desktop)
280     mbind("A-Up", MC_Root, MouseClick, next_desktop)
281     mbind("A-Down", MC_Frame, MouseClick, prev_desktop)
282     mbind("A-Down", MC_Root, MouseClick, prev_desktop)
283
284     mbind("C-A-Up", MC_Frame, MouseClick, send_to_next_desktop)
285     mbind("C-A-Down", MC_Frame, MouseClick, send_to_prev_desktop)
286
287 def setup_fallback_focus():
288     """Sets up a focus fallback routine so that when no windows are focused,
289        the last window to have focus on the desktop will be focused."""
290     focus_stack = []
291     def focused(data):
292         #global focus_stack
293         if data.client:
294             window = data.client.window()
295             # add to front the stack
296             if window in focus_stack:
297                 focus_stack.remove(window)
298             focus_stack.insert(0, window)
299         else:
300             # pass around focus
301             desktop = openbox.screen(data.screen).desktop()
302             l = len(focus_stack)
303             i = 0
304             while i < l:
305                 w = focus_stack[i]
306                 client = openbox.findClient(w)
307                 if not client: # window is gone, remove it
308                     focus_stack.pop(i)
309                     l = l - 1
310                 elif client.desktop() == desktop and \
311                          client.normal() and client.focus():
312                     break
313                 else:
314                     i = i + 1
315
316     ebind(EventFocus, focused)
317
318     
319 ############################################################################
320 ### Window placement algorithms, choose one of these and ebind it to the ###
321 ### EventPlaceWindow action.                                             ###
322 ############################################################################
323
324 ob_rand = None
325 import random
326 def placewindows_random(data):
327     if not data.client: return
328     client_area = data.client.area()
329     frame_size = data.client.frame.size()
330     screen = display.screenInfo(data.screen)
331     width = screen.width() - (client_area.width() +
332                               frame_size.left + frame_size.right)
333     height = screen.height() - (client_area.height() + 
334                                 frame_size.top + frame_size.bottom)
335     global ob_rand
336     if not ob_rand: ob_rand = random.Random()
337     x = ob_rand.randrange(0, width-1)
338     y = ob_rand.randrange(0, height-1)
339     data.client.move(x, y)
340
341
342 print "Loaded builtins.py"