]> icculus.org git repositories - mikachu/openbox.git/blob - scripts/motion.py
kill the const
[mikachu/openbox.git] / scripts / motion.py
1 ############################################################################
2 ###    Functions that provide callbacks for motion events to move and    ###
3 ###    resize windows.                                                   ###
4 ############################################################################
5
6 def move(data):
7     """Moves the window interactively. This should only be used with
8        MouseAction.Motion events. If 'Coords Popup for Moving' or 'Rubberband
9        Mode for Moving' is enabled, then the end_move function needs to be
10        bound as well."""
11     _move(data)
12
13 def end_move(data):
14     """Complete the interactive move of a window."""
15     _end_move(data)
16
17 def resize(data):
18     """Resizes the window interactively. This should only be used with
19        MouseMotion events. If 'Coords Popup for Resizing' or 'Rubberband Mode
20        for Resizing' is enabled, then the end_resize function needs to be
21        bound as well."""
22     _resize(data)
23
24 def end_resize(data):
25     """Complete the interactive resize of a window."""
26     _end_resize(data)
27
28 export_functions = move, end_move, resize, end_resize
29
30 #############################################################################
31
32 import config
33
34 config.add('motion',
35            'edge_resistance',
36            'Edge Resistance',
37            "The amount of resistance to provide to moving a window past a " + \
38            "screen boundary. Specify a value of 0 to disable edge resistance.",
39            'integer',
40            10,
41            min = 0)
42 config.add('motion',
43            'popup_in_window',
44            'Coords Popup In Window',
45            "When this is true, the coordinates popups will be placed " + \
46            "relative to the window being moved/resized. When false, they " + \
47            "will appear relative to the entire screen.",
48            'boolean',
49            0)
50 config.add('motion',
51            'popup_centered',
52            'Coords Popup Centered',
53            "When this is true, the coordinates popups will be centered " + \
54            "relative to the window or screen (see 'Coords Popup In " + \
55            "Window'). When false, they will be placed based upon the " + \
56            "'Coords Popup Position' options.",
57            'boolean',
58            1)
59 config.add('motion',
60            'popup_coords_x',
61            'Coords Popup Position - X',
62            "When 'Coords Popup Centered' is false, this position will be " + \
63            "used to place the coordinates popups. The popups will be " + \
64            "placed relative to the window or the screen (see 'Coords " + \
65            "Popup In Window'). A value of 0 would place it at the left " + \
66            "edge, while a value of -1 would place it at the right edge. " + \
67            "This value behaves similarly to those passed to the -geometry " + \
68            "flag of many applications.",
69            'integer',
70            0)
71 config.add('motion',
72            'popup_coords_y',
73            'Coords Popup Position - Y',
74            "When 'Coords Popup Centered' is false, this position will be " + \
75            "used to place the coordinates popups. The popups will be " + \
76            "placed relative to the window or the screen (see 'Coords Popup " +\
77            "In Window'). A value of 0 would place it at the top edge, " + \
78            "while a value of -1 would place it at the bottom edge. This " + \
79            "value behaves similarly to those passed to the -geometry flag " + \
80            "of many applications.",
81            'integer',
82            0)
83 config.add('motion',
84            'move_popup',
85            'Coords Popup for Moving',
86            "Option to display a coordinates popup when moving windows.",
87            'boolean',
88            1)
89 config.add('motion',
90            'move_rubberband',
91            'Rubberband Mode for Moving',
92            "NOT IMPLEMENTED (yet?)\n"+\
93            "Display an outline while moving instead of moving the actual " + \
94            "window, until the move is completed. Good for slower systems.",
95            'boolean',
96            0)
97 config.add('motion',
98            'resize_popup',
99            'Coords Popup for Resizing',
100            "Option to display a coordinates popup when resizing windows.",
101            'boolean',
102            1)
103 config.add('motion',
104            'resize_rubberband',
105            'Rubberband Mode for Resizing',
106            "NOT IMPLEMENTED (yet?)\n"+\
107            "Display an outline while resizing instead of resizing the " + \
108            "actual window, until the resize is completed. Good for slower " + \
109            "systems.",
110            'boolean',
111            0)
112 config.add('motion',
113            'resize_nearest',
114            'Resize Nearest Corner',
115            "When true, resizing will occur from the corner nearest where " + \
116            "the mouse is. When false resizing will always occur from the " + \
117            "bottom right corner.",
118            'boolean',
119            1)
120
121 ###########################################################################
122 ###      Internal stuff, should not be accessed outside the module.     ###
123 ###########################################################################
124
125 import ob
126 import otk
127
128 _popwidget = 0
129
130 # motion state
131 _inmove = 0
132 _inresize = 0
133
134 # last motion data
135 _cx = 0
136 _cy = 0
137 _cw = 0
138 _ch = 0
139 _px = 0
140 _py = 0
141 _dx = 0
142 _dy = 0
143 _client = 0
144 _screen = 0
145
146 _motion_mask = 0
147
148 def _place_popup():
149     if config.get('motion', 'popup_in_window'):
150         # use the actual client's area, not the frame's
151         area = _client.frame.area()
152         size = _client.frame.size()
153         area = otk.Rect(area.x() + size.left, area.y() + size.top,
154                         area.width() - size.left - size.right,
155                         area.height() - size.top - size.bottom)
156     else:
157         area = otk.Rect(otk.Point(0, 0), ob.openbox.screen(_screen).size())
158     size = _popwidget.minSize()
159     if config.get('motion', 'popup_centered'):
160         x = area.position().x() + (area.size().width() - size.width()) / 2
161         y = area.position().y() + (area.size().height() - size.height()) / 2
162     else:
163         x = config.get('motion', 'popup_coords_x')
164         y = config.get('motion', 'popup_coords_y')
165         if x < 0: x += area.width() - size.width() + 1
166         if y < 0: y += area.width() - size.height() + 1
167         x += area.position().x()
168         y += area.position().y()
169     _popwidget.moveresize(otk.Rect(x, y, size.width(), size.height()))
170
171 def _motion_grab(data):
172     global _motion_mask, _inmove, _inresize;
173
174     # are all the modifiers this started with still pressed?
175     if not _motion_mask & data.state:
176         if _inmove:
177             _end_move(data)
178         elif _inresize:
179             _end_resize(data)
180         else:
181             raise RuntimeError
182
183 _last_x = 0
184 _last_y = 0
185
186 def _do_move(final):
187     global _screen, _client, _cx, _cy, _dx, _dy
188
189     # get destination x/y for the *frame*
190     x = _cx + _dx + _client.frame.area().x() - _client.area().x()
191     y = _cy + _dy + _client.frame.area().y() - _client.area().y()
192
193     global _last_x, _last_y
194     resist = config.get('motion', 'edge_resistance')
195     if resist:
196         fs = _client.frame.size()
197         w = _client.area().width() + fs.left + fs.right
198         h = _client.area().height() + fs.top + fs.bottom
199         # use the area based on the struts
200         area = ob.openbox.screen(_screen).area(_client.desktop())
201         l = area.left()
202         r = area.right() - w + 1
203         t = area.top()
204         b = area.bottom() - h + 1
205         # left screen edge
206         if _last_x > x and x < l and x >= l - resist:
207             x = l
208         # right screen edge
209         if _last_x < x and x > r and x <= r + resist:
210             x = r
211         # top screen edge
212         if _last_y > y and y < t and y >= t - resist:
213             y = t
214         # right screen edge
215         if _last_y < y and y > b and y <= b + resist:
216             y = b
217
218     global _inmove
219     if not _inmove:
220         _last_x = 0
221         _last_y = 0
222     else:
223         _last_x = x
224         _last_y = y
225
226     if not final and config.get('motion', 'move_rubberband'):
227         # XXX draw the outline ...
228         pass
229     else:
230         _client.move(x, y, final)
231
232     if config.get('motion', 'move_popup'):
233         global _popwidget
234         text = "X: " + str(x) + " Y: " + str(y)
235         if not _popwidget:
236             _popwidget = otk.Label(_screen, ob.openbox)
237             _popwidget.setHighlighted(1)
238         _popwidget.setText(text)
239         _place_popup()
240         _popwidget.show()
241
242 def _move(data):
243     if not data.client: return
244
245     # not-normal windows dont get moved
246     if not data.client.normal(): return
247
248     global _screen, _client, _cx, _cy, _dx, _dy, _motion_mask
249     _screen = data.screen
250     _client = data.client
251     _cx = data.press_clientx
252     _cy = data.press_clienty
253     _dx = data.xroot - data.pressx
254     _dy = data.yroot - data.pressy
255     _motion_mask = data.state
256     _do_move(0)
257     global _inmove
258     if not _inmove:
259         ob.kgrab(_screen, _motion_grab)
260         _inmove = 1
261
262 def _end_move(data):
263     global _inmove, _popwidget
264     if _inmove:
265         _do_move(1)
266         _inmove = 0
267     _popwidget = 0
268     ob.kungrab()
269
270 def _do_resize(final):
271     global _screen, _client, _cx, _cy, _cw, _ch, _px, _py, _dx, _dy
272
273     dx = _dx
274     dy = _dy
275     
276     # pick a corner to anchor
277     if not (config.get('motion', 'resize_nearest') or
278             _context == ob.MouseContext.Grip):
279         corner = ob.Client.TopLeft
280     else:
281         x = _px - _cx
282         y = _py - _cy
283         if y < _ch / 2:
284             if x < _cw / 2:
285                 corner = ob.Client.BottomRight
286                 dx *= -1
287             else:
288                 corner = ob.Client.BottomLeft
289             dy *= -1
290         else:
291             if x < _cw / 2:
292                 corner = ob.Client.TopRight
293                 dx *= -1
294             else:
295                 corner = ob.Client.TopLeft
296
297     w = _cw + dx
298     h = _ch + dy
299
300     if not final and config.get('motion', 'resize_rubberband'):
301         # XXX draw the outline ...
302         pass
303     else:
304         _client.resize(corner, w, h)
305
306     if config.get('motion', 'resize_popup'):
307         global _popwidget
308         ls = _client.logicalSize()
309         text = "W: " + str(ls.width()) + " H: " + str(ls.height())
310         if not _popwidget:
311             _popwidget = otk.Label(_screen, ob.openbox)
312             _popwidget.setHighlighted(1)
313         _popwidget.setText(text)
314         _place_popup()
315         _popwidget.show()
316
317 def _resize(data):
318     if not data.client: return
319
320     # not-normal windows dont get resized
321     if not data.client.normal(): return
322
323     global _screen, _client, _cx, _cy, _cw, _ch, _px, _py, _dx, _dy
324     global _motion_mask
325     _screen = data.screen
326     _client = data.client
327     _cx = data.press_clientx
328     _cy = data.press_clienty
329     _cw = data.press_clientwidth
330     _ch = data.press_clientheight
331     _px = data.pressx
332     _py = data.pressy
333     _dx = data.xroot - _px
334     _dy = data.yroot - _py
335     _motion_mask = data.state
336     _do_resize(0)
337     global _inresize
338     if not _inresize:
339         ob.kgrab(_screen, _motion_grab)
340         _inresize = 1
341
342 def _end_resize(data):
343     global _inresize, _popwidget
344     if _inresize:
345         _do_resize(1)
346         _inresize = 0
347     _popwidget = 0
348     ob.kungrab()