adds option to have the popup centered above the window instead of centered, and...
[dana/openbox.git] / openbox / moveresize.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    moveresize.c for the Openbox window manager
4    Copyright (c) 2003        Ben Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "grab.h"
20 #include "framerender.h"
21 #include "screen.h"
22 #include "prop.h"
23 #include "client.h"
24 #include "frame.h"
25 #include "openbox.h"
26 #include "resist.h"
27 #include "popup.h"
28 #include "moveresize.h"
29 #include "config.h"
30 #include "render/render.h"
31 #include "render/theme.h"
32
33 #include <X11/Xlib.h>
34 #include <glib.h>
35
36 gboolean moveresize_in_progress = FALSE;
37 ObClient *moveresize_client = NULL;
38
39 static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */
40
41 static gint start_x, start_y, start_cx, start_cy, start_cw, start_ch;
42 static gint cur_x, cur_y;
43 static guint button;
44 static guint32 corner;
45 static ObCorner lockcorner;
46
47 static ObPopup *popup = NULL;
48
49 static void client_dest(ObClient *client, gpointer data)
50 {
51     if (moveresize_client == client)
52         moveresize_end(TRUE);    
53 }
54
55 void moveresize_startup(gboolean reconfig)
56 {
57     popup = popup_new(FALSE);
58
59     if (!reconfig)
60         client_add_destructor(client_dest, NULL);
61 }
62
63 void moveresize_shutdown(gboolean reconfig)
64 {
65     if (!reconfig) {
66         if (moveresize_in_progress)
67             moveresize_end(FALSE);
68         client_remove_destructor(client_dest);
69     }
70
71     popup_free(popup);
72     popup = NULL;
73 }
74
75 static void popup_coords(ObClient *c, gchar *format, gint a, gint b)
76 {
77     gchar *text;
78
79     text = g_strdup_printf(format, a, b);
80     if (config_resize_popup_pos == 1) /* == "Top" */
81         popup_position(popup, SouthGravity,
82                        c->frame->area.x
83                      + c->frame->area.width/2,
84                        c->frame->area.y);
85     else /* == "Center" */
86         popup_position(popup, CenterGravity,
87                        c->frame->area.x + c->frame->size.left +
88                        c->area.width / 2,
89                        c->frame->area.y + c->frame->size.top +
90                        c->area.height / 2);
91     popup_show(popup, text);
92     g_free(text);
93 }
94
95 void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
96 {
97     ObCursor cur;
98
99     moving = (cnr == prop_atoms.net_wm_moveresize_move ||
100               cnr == prop_atoms.net_wm_moveresize_move_keyboard);
101
102     if (moveresize_in_progress || !c->frame->visible ||
103         !(moving ?
104           (c->functions & OB_CLIENT_FUNC_MOVE) :
105           (c->functions & OB_CLIENT_FUNC_RESIZE)))
106         return;
107
108     moveresize_client = c;
109     start_cx = c->frame->area.x;
110     start_cy = c->frame->area.y;
111     /* these adjustments for the size_inc make resizing a terminal more
112        friendly. you essentially start the resize in the middle of the
113        increment instead of at 0, so you have to move half an increment
114        either way instead of a full increment one and 1 px the other. and this
115        is one large mother fucking comment. */
116     start_cw = c->area.width + c->size_inc.width / 2;
117     start_ch = c->area.height + c->size_inc.height / 2;
118     start_x = x;
119     start_y = y;
120     corner = cnr;
121     button = b;
122
123     /*
124       have to change start_cx and start_cy if going to do this..
125     if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
126         corner == prop_atoms.net_wm_moveresize_size_keyboard)
127         XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
128                      c->area.width / 2, c->area.height / 2);
129     */
130
131     if (moving) {
132         cur_x = start_cx;
133         cur_y = start_cy;
134     } else {
135         cur_x = start_cw;
136         cur_y = start_ch;
137     }
138
139     moveresize_in_progress = TRUE;
140
141     if (corner == prop_atoms.net_wm_moveresize_size_topleft)
142         cur = OB_CURSOR_NORTHWEST;
143     else if (corner == prop_atoms.net_wm_moveresize_size_top)
144         cur = OB_CURSOR_NORTH;
145     else if (corner == prop_atoms.net_wm_moveresize_size_topright)
146         cur = OB_CURSOR_NORTHEAST;
147     else if (corner == prop_atoms.net_wm_moveresize_size_right)
148         cur = OB_CURSOR_EAST;
149     else if (corner == prop_atoms.net_wm_moveresize_size_bottomright)
150         cur = OB_CURSOR_SOUTHEAST;
151     else if (corner == prop_atoms.net_wm_moveresize_size_bottom)
152         cur = OB_CURSOR_SOUTH;
153     else if (corner == prop_atoms.net_wm_moveresize_size_bottomleft)
154         cur = OB_CURSOR_SOUTHWEST;
155     else if (corner == prop_atoms.net_wm_moveresize_size_left)
156         cur = OB_CURSOR_WEST;
157     else if (corner == prop_atoms.net_wm_moveresize_size_keyboard)
158         cur = OB_CURSOR_SOUTHEAST;
159     else if (corner == prop_atoms.net_wm_moveresize_move)
160         cur = OB_CURSOR_MOVE;
161     else if (corner == prop_atoms.net_wm_moveresize_move_keyboard)
162         cur = OB_CURSOR_MOVE;
163     else
164         g_assert_not_reached();
165
166     grab_pointer(TRUE, cur);
167     grab_keyboard(TRUE);
168 }
169
170 void moveresize_end(gboolean cancel)
171 {
172     grab_keyboard(FALSE);
173     grab_pointer(FALSE, OB_CURSOR_NONE);
174
175     popup_hide(popup);
176
177     if (moving) {
178         client_move(moveresize_client,
179                     (cancel ? start_cx : cur_x),
180                     (cancel ? start_cy : cur_y));
181     } else {
182         client_configure(moveresize_client, lockcorner,
183                          moveresize_client->area.x,
184                          moveresize_client->area.y,
185                          (cancel ? start_cw : cur_x),
186                          (cancel ? start_ch : cur_y), TRUE, TRUE);
187     }
188
189     moveresize_in_progress = FALSE;
190     moveresize_client = NULL;
191 }
192
193 static void do_move(gboolean resist)
194 {
195     if (resist)
196         resist_move_windows(moveresize_client, &cur_x, &cur_y);
197     resist_move_monitors(moveresize_client, &cur_x, &cur_y);
198
199     /* get where the client should be */
200     frame_frame_gravity(moveresize_client->frame, &cur_x, &cur_y);
201     client_configure(moveresize_client, OB_CORNER_TOPLEFT, cur_x, cur_y,
202                      moveresize_client->area.width,
203                      moveresize_client->area.height, TRUE, FALSE);
204     if (config_resize_popup_show == 2) /* == "Always" */
205         popup_coords(moveresize_client, "%d x %d",
206                 moveresize_client->frame->area.x,
207                 moveresize_client->frame->area.y);
208 }
209
210 static void do_resize(gboolean resist)
211 {
212     /* resist_size_* needs the frame size */
213     cur_x += moveresize_client->frame->size.left +
214         moveresize_client->frame->size.right;
215     cur_y += moveresize_client->frame->size.top +
216         moveresize_client->frame->size.bottom;
217
218     if (resist)
219         resist_size_windows(moveresize_client, &cur_x, &cur_y, lockcorner);
220     resist_size_monitors(moveresize_client, &cur_x, &cur_y, lockcorner);
221
222     cur_x -= moveresize_client->frame->size.left +
223         moveresize_client->frame->size.right;
224     cur_y -= moveresize_client->frame->size.top +
225         moveresize_client->frame->size.bottom;
226  
227     client_configure(moveresize_client, lockcorner, 
228                      moveresize_client->area.x, moveresize_client->area.y,
229                      cur_x, cur_y, TRUE, FALSE);
230
231     /* this would be better with a fixed width font ... XXX can do it better
232        if there are 2 text boxes */
233     if (config_resize_popup_show == 2 || /* == "Always" */
234             (config_resize_popup_show == 1 && /* == "Nonpixel" */
235                 (moveresize_client->size_inc.width > 1 ||
236                  moveresize_client->size_inc.height > 1))
237         )
238         popup_coords(moveresize_client, "%d x %d",
239                      moveresize_client->logical_size.width,
240                      moveresize_client->logical_size.height);
241 }
242
243 void moveresize_event(XEvent *e)
244 {
245     g_assert(moveresize_in_progress);
246
247     if (e->type == ButtonPress) {
248         if (!button) {
249             start_x = e->xbutton.x_root;
250             start_y = e->xbutton.y_root;
251             button = e->xbutton.button; /* this will end it now */
252         }
253     } else if (e->type == ButtonRelease) {
254         if (!button || e->xbutton.button == button) {
255             moveresize_end(FALSE);
256         }
257     } else if (e->type == MotionNotify) {
258         if (moving) {
259             cur_x = start_cx + e->xmotion.x_root - start_x;
260             cur_y = start_cy + e->xmotion.y_root - start_y;
261             do_move(TRUE);
262         } else {
263             if (corner == prop_atoms.net_wm_moveresize_size_topleft) {
264                 cur_x = start_cw - (e->xmotion.x_root - start_x);
265                 cur_y = start_ch - (e->xmotion.y_root - start_y);
266                 lockcorner = OB_CORNER_BOTTOMRIGHT;
267             } else if (corner == prop_atoms.net_wm_moveresize_size_top) {
268                 cur_x = start_cw;
269                 cur_y = start_ch - (e->xmotion.y_root - start_y);
270                 lockcorner = OB_CORNER_BOTTOMRIGHT;
271             } else if (corner == prop_atoms.net_wm_moveresize_size_topright) {
272                 cur_x = start_cw + (e->xmotion.x_root - start_x);
273                 cur_y = start_ch - (e->xmotion.y_root - start_y);
274                 lockcorner = OB_CORNER_BOTTOMLEFT;
275             } else if (corner == prop_atoms.net_wm_moveresize_size_right) { 
276                 cur_x = start_cw + (e->xmotion.x_root - start_x);
277                 cur_y = start_ch;
278                 lockcorner = OB_CORNER_BOTTOMLEFT;
279             } else if (corner ==
280                        prop_atoms.net_wm_moveresize_size_bottomright) {
281                 cur_x = start_cw + (e->xmotion.x_root - start_x);
282                 cur_y = start_ch + (e->xmotion.y_root - start_y);
283                 lockcorner = OB_CORNER_TOPLEFT;
284             } else if (corner == prop_atoms.net_wm_moveresize_size_bottom) {
285                 cur_x = start_cw;
286                 cur_y = start_ch + (e->xmotion.y_root - start_y);
287                 lockcorner = OB_CORNER_TOPLEFT;
288             } else if (corner ==
289                        prop_atoms.net_wm_moveresize_size_bottomleft) {
290                 cur_x = start_cw - (e->xmotion.x_root - start_x);
291                 cur_y = start_ch + (e->xmotion.y_root - start_y);
292                 lockcorner = OB_CORNER_TOPRIGHT;
293             } else if (corner == prop_atoms.net_wm_moveresize_size_left) {
294                 cur_x = start_cw - (e->xmotion.x_root - start_x);
295                 cur_y = start_ch;
296                 lockcorner = OB_CORNER_TOPRIGHT;
297             } else if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
298                 cur_x = start_cw + (e->xmotion.x_root - start_x);
299                 cur_y = start_ch + (e->xmotion.y_root - start_y);
300                 lockcorner = OB_CORNER_TOPLEFT;
301             } else
302                 g_assert_not_reached();
303
304             do_resize(TRUE);
305         }
306     } else if (e->type == KeyPress) {
307         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
308             moveresize_end(TRUE);
309         else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
310             moveresize_end(FALSE);
311         else {
312             if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
313                 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
314
315                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
316                     dx = MAX(4, moveresize_client->size_inc.width);
317                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
318                     dx = -MAX(4, moveresize_client->size_inc.width);
319                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
320                     dy = MAX(4, moveresize_client->size_inc.height);
321                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
322                     dy = -MAX(4, moveresize_client->size_inc.height);
323                 else
324                     return;
325
326                 cur_x += dx;
327                 cur_y += dy;
328                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
329                 /* steal the motion events this causes */
330                 XSync(ob_display, FALSE);
331                 {
332                     XEvent ce;
333                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
334                 }
335
336                 do_resize(FALSE);
337
338                 /* because the cursor moves even though the window does
339                    not nessesarily (resistance), this adjusts where the curor
340                    thinks it started so that it keeps up with where the window
341                    actually is */
342                 start_x += dx - (cur_x - ox);
343                 start_y += dy - (cur_y - oy);
344             } else if (corner == prop_atoms.net_wm_moveresize_move_keyboard) {
345                 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
346                 gint opx, px, opy, py;
347
348                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
349                     dx = 4;
350                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
351                     dx = -4;
352                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
353                     dy = 4;
354                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
355                     dy = -4;
356                 else
357                     return;
358
359                 cur_x += dx;
360                 cur_y += dy;
361                 screen_pointer_pos(&opx, &opy);
362                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
363                 /* steal the motion events this causes */
364                 XSync(ob_display, FALSE);
365                 {
366                     XEvent ce;
367                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
368                 }
369                 screen_pointer_pos(&px, &py);
370
371                 do_move(FALSE);
372
373                 /* because the cursor moves even though the window does
374                    not nessesarily (resistance), this adjusts where the curor
375                    thinks it started so that it keeps up with where the window
376                    actually is */
377                 start_x += (px - opx) - (cur_x - ox);
378                 start_y += (py - opy) - (cur_y - oy);
379             }
380         }
381     }
382 }