move the otk swig module into its own lib/directory
[mikachu/openbox.git] / scripts / motion.py
1 ############################################################################
2 ###    Functions that provide callbacks for motion events to move and    ###
3 ###    resize windows.                                                   ###
4 ############################################################################
5
6 #############################################################################
7 ###   Options that can be modified to change the functions' behaviors.    ###
8 #############################################################################
9 EDGE_RESISTANCE = 10
10 """The amount of resistance to provide to moving a window past a screen
11    boundary. Specify a value of 0 to disable edge resistance."""
12 MOVE_POPUP = 1
13 """Display a coordinates popup when moving windows."""
14 MOVE_RUBBERBAND = 0
15 """NOT IMPLEMENTED (yet?)
16    Display an outline while moving instead of moving the actual window,
17    until the move is completed. Good for slower systems."""
18 RESIZE_POPUP = 1
19 """Display a size popup when resizing windows."""
20 RESIZE_RUBBERBAND = 0
21 """NOT IMPLEMENTED (yet?)
22    Display an outline while resizing instead of resizing the actual
23    window, until the resize is completed. Good for slower systems."""
24 RESIZE_NEAREST = 1
25 """Non-zero to resize from the corner nearest where the mouse is, 0 to
26    resize always from the bottom right corner."""
27 #############################################################################
28
29 def move(data):
30     """Moves the window interactively. This should only be used with
31        MouseAction.Motion events. If MOVE_POPUP or MOVE_RUBBERBAND is enabled,
32        then the end_move function needs to be bound as well."""
33     _move(data)
34
35 def end_move(data):
36     """Complete the interactive move of a window."""
37     _end_move(data)
38
39 def resize(data):
40     """Resizes the window interactively. This should only be used with
41        MouseMotion events. If RESIZE_POPUP or RESIZE_RUBBERBAND is enabled,
42        then the end_resize function needs to be bound as well."""
43     _resize(data)
44
45 def end_resize(data):
46     """Complete the interactive resize of a window."""
47     _end_resize(data)
48
49 ###########################################################################
50 ###########################################################################
51
52 ###########################################################################
53 ###      Internal stuff, should not be accessed outside the module.     ###
54 ###########################################################################
55
56 import ob
57 import otk
58
59 _popwidget = 0
60 _poplabel = 0
61
62 # motion state
63 _inmove = 0
64 _inresize = 0
65
66 # last motion data
67 _cx = 0
68 _cy = 0
69 _cw = 0
70 _ch = 0
71 _px = 0
72 _py = 0
73 _dx = 0
74 _dy = 0
75 _client = 0
76 _screen = 0
77
78 _motion_mask = 0
79
80 def _motion_grab(data):
81     global _motion_mask, _inmove, _inresize;
82
83     if data.action == ob.KeyAction.Release:
84         # have all the modifiers this started with been released?
85         if not _motion_mask & data.state:
86             if _inmove:
87                 _end_move(data)
88             elif _inresize:
89                 _end_resize(data)
90             else:
91                 raise RuntimeError
92
93 _last_x = 0
94 _last_y = 0
95
96 def _do_move():
97     global _screen, _client, _cx, _cy, _dx, _dy
98
99     # get destination x/y for the *frame*
100     x = _cx + _dx + _client.frame.rect().x() - _client.area().x()
101     y = _cy + _dy + _client.frame.rect().y() - _client.area().y()
102
103     global _last_x, _last_y
104     if EDGE_RESISTANCE:
105         fs = _client.frame.size()
106         w = _client.area().width() + fs.left + fs.right
107         h = _client.area().height() + fs.top + fs.bottom
108         # use the area based on the struts
109         area = ob.openbox.screen(_screen).area()
110         l = area.left()
111         r = area.right() - w + 1
112         t = area.top()
113         b = area.bottom() - h + 1
114         # left screen edge
115         if _last_x > x and x < l and x >= l - EDGE_RESISTANCE:
116             x = l
117         # right screen edge
118         if _last_x < x and x > r and x <= r + EDGE_RESISTANCE:
119             x = r
120         # top screen edge
121         if _last_y > y and y < t and y >= t - EDGE_RESISTANCE:
122             y = t
123         # right screen edge
124         if _last_y < y and y > b and y <= b + EDGE_RESISTANCE:
125             y = b
126
127     global _inmove
128     if not _inmove:
129         _last_x = 0
130         _last_y = 0
131     else:
132         _last_x = x
133         _last_y = y
134
135     if MOVE_RUBBERBAND:
136         # draw the outline ...
137         f=0
138     else:
139         _client.move(x, y)
140
141     if MOVE_POPUP:
142         global _popwidget, _poplabel
143         style = ob.openbox.screen(_screen).style()
144         font = style.labelFont()
145         text = "X: " + str(x) + " Y: " + str(y)
146         length = font.measureString(text)
147         if not _popwidget:
148             _popwidget = otk.Widget(ob.openbox, style,
149                                     otk.Widget.Horizontal, 0,
150                                     style.bevelWidth(), 1)
151             _popwidget.setTexture(style.titlebarFocusBackground())
152             _poplabel = otk.FocusLabel(_popwidget)
153             _poplabel.focus()
154         _poplabel.fitString(text)
155         _poplabel.setText(text)
156         _popwidget.update() 
157         area = otk.display.screenInfo(_screen).rect()
158         _popwidget.move(area.x() + (area.width() -
159                                     _popwidget.width()) / 2,
160                         area.y() + (area.height() -
161                                     _popwidget.height()) / 2)
162         _popwidget.show(1)
163
164 def _move(data):
165     if not data.client: return
166
167     # not-normal windows dont get moved
168     if not data.client.normal(): return
169
170     global _screen, _client, _cx, _cy, _dx, _dy
171     _screen = data.screen
172     _client = data.client
173     _cx = data.press_clientx
174     _cy = data.press_clienty
175     _dx = data.xroot - data.pressx
176     _dy = data.yroot - data.pressy
177     _do_move()
178     global _inmove
179     if not _inmove:
180         ob.kgrab(_screen, _motion_grab)
181         _inmove = 1
182
183 def _end_move(data):
184     global MOVE_RUBBERBAND
185     global _inmove, _popwidget, _poplabel
186     if _inmove:
187         r = MOVE_RUBBERBAND
188         MOVE_RUBBERBAND = 0
189         _do_move()
190         MOVE_RUBBERBAND = r
191         _inmove = 0
192     _poplabel = 0
193     _popwidget = 0
194     ob.kungrab()
195
196 def _do_resize():
197     global _screen, _client, _cx, _cy, _cw, _ch, _px, _py, _dx, _dy
198
199     dx = _dx
200     dy = _dy
201     
202     # pick a corner to anchor
203     if not (RESIZE_NEAREST or _context == ob.MouseContext.Grip):
204         corner = ob.Client.TopLeft
205     else:
206         x = _px - _cx
207         y = _py - _cy
208         if y < _ch / 2:
209             if x < _cw / 2:
210                 corner = ob.Client.BottomRight
211                 dx *= -1
212             else:
213                 corner = ob.Client.BottomLeft
214             dy *= -1
215         else:
216             if x < _cw / 2:
217                 corner = ob.Client.TopRight
218                 dx *= -1
219             else:
220                 corner = ob.Client.TopLeft
221
222     w = _cw + dx
223     h = _ch + dy
224
225     if RESIZE_RUBBERBAND:
226         # draw the outline ...
227         f=0
228     else:
229         _client.resize(corner, w, h)
230
231     if RESIZE_POPUP:
232         global _popwidget, _poplabel
233         style = ob.openbox.screen(_screen).style()
234         ls = _client.logicalSize()
235         text = "W: " + str(ls.x()) + " H: " + str(ls.y())
236         if not _popwidget:
237             _popwidget = otk.Widget(ob.openbox, style,
238                                     otk.Widget.Horizontal, 0,
239                                     style.bevelWidth(), 1)
240             _popwidget.setTexture(style.titlebarFocusBackground())
241             _poplabel = otk.FocusLabel(_popwidget)
242             _poplabel.focus()
243         _poplabel.fitString(text)
244         _poplabel.setText(text)
245         area = otk.display.screenInfo(_screen).rect()
246         _popwidget.update() 
247         _popwidget.move(area.x() + (area.width() -
248                                     _popwidget.width()) / 2,
249                         area.y() + (area.height() -
250                                     _popwidget.height()) / 2)
251         _popwidget.show(1)
252
253 def _resize(data):
254     if not data.client: return
255
256     # not-normal windows dont get resized
257     if not data.client.normal(): return
258
259     global _screen, _client, _cx, _cy, _cw, _ch, _px, _py, _dx, _dy
260     _screen = data.screen
261     _client = data.client
262     _cx = data.press_clientx
263     _cy = data.press_clienty
264     _cw = data.press_clientwidth
265     _ch = data.press_clientheight
266     _px = data.pressx
267     _py = data.pressy
268     _dx = data.xroot - _px
269     _dy = data.yroot - _py
270     _do_resize()
271     global _inresize
272     if not _inresize:
273         ob.kgrab(_screen, _motion_grab)
274         _inresize = 1
275
276 def _end_resize(data):
277     global RESIZE_RUBBERBAND, _inresize
278     global _popwidget, _poplabel
279     if _inresize:
280         r = RESIZE_RUBBERBAND
281         RESIZE_RUBBERBAND = 0
282         _do_resize()
283         RESIZE_RUBBERBAND = r
284         _inresize = 0
285     _poplabel = 0
286     _popwidget = 0
287     ob.kungrab()