0 /*
1 * resizer.c - resizable window borders
2 */
3
4 /*
5 * Copyright 2006-2008 Johan Veenhuizen
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 */
25
26 #include <assert.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <X11/cursorfont.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34
35 #include "global.h"
36 #include "lib.h"
37 #include "menu.h"
38 #include "resizer.h"
39 #include "window.h"
40
41 #define PADDING MAX(1, (font->ascent + font->descent) / 4)
42
43 static Cursor c1, c2, c3, c4, c5, c6, c7, c8;
44
45 struct sizewin { struct widget widget;
46 struct window *window;
47 GC gc;
48 };
49
50 static void update_sizewin(struct sizewin *sizewin, int xdim, int ydim)
51 {
52 static char buf[256];
53 struct widget *widget;
54 struct window *win;
55 int width, height;
56
57 widget = (struct widget *)sizewin;
58 win = sizewin->window;
59
60 sprintf(buf, "%!d(MISSING)x%!d(MISSING)", xdim, ydim);
61 width = 2 * (PADDING + font->descent) + stringwidth(buf);
62 height = 2 * PADDING + font->ascent + font->descent;
63 moveresize_widget(widget,
64 WIDGET_X(win) + WIDGET_WIDTH(win) / 2 - (width + 2) / 2,
65 WIDGET_Y(win) + (border_width + button_size)*win->decoration
66 + (WIDGET_HEIGHT(win) - 2*border_width - button_size) / 2 * win->decoration
67 - (height + 2) / 2,
68 width, height);
69 if (!WIDGET_MAPPED(sizewin))
70 map_widget(&sizewin->widget);
71 XClearWindow(display, WIDGET_XWINDOW(sizewin));
72 XSetForeground(display, sizewin->gc, color_title_active_fg.normal);
73 XDrawString(display, WIDGET_XWINDOW(sizewin), sizewin->gc,
74 PADDING + font->descent, PADDING + font->ascent,
75 buf, strlen(buf));
76 drawraised(WIDGET_XWINDOW(sizewin), sizewin->gc,
77 &color_title_active_bg,
78 0, 0, WIDGET_WIDTH(sizewin), WIDGET_HEIGHT(sizewin));
79 }
80
81 static struct sizewin *create_sizewin(struct window *win, int xdim, int ydim)
82 {
83 XSetWindowAttributes attr;
84 XGCValues gcval;
85 struct sizewin *sizewin;
86
87 sizewin = MALLOC(sizeof *sizewin);
88 create_widget(&sizewin->widget, WIDGET_SIZEWIN,
89 root, InputOutput, 0, 0, 1, 1);
90 XSetWindowBackground(display, WIDGET_XWINDOW(sizewin),
91 color_title_active_bg.normal);
92 attr.save_under = True;
93 XChangeWindowAttributes(display, WIDGET_XWINDOW(sizewin),
94 CWSaveUnder, &attr);
95 gcval.font = font->fid;
96 sizewin->gc = XCreateGC(display, WIDGET_XWINDOW(sizewin),
97 GCFont, &gcval);
98 sizewin->window = win;
99 update_sizewin(sizewin, xdim, ydim);
100 return sizewin;
101 }
102
103 static void destroy_sizewin(struct sizewin *sizewin)
104 {
105 XFreeGC(display, sizewin->gc);
106 destroy_widget(&sizewin->widget);
107 FREE(sizewin);
108 }
109
110 static void press(struct resizer *resizer, XButtonEvent *ep)
111 {
112 struct window *win = resizer->window;
113 int xdim, ydim;
114
115 if (ep->button == Button1) {
116 set_active_window(win);
117 window_calcsize(win, WIDGET_WIDTH(win), WIDGET_HEIGHT(win),
118 NULL, NULL, &xdim, &ydim);
119 if (resizer->sizewin != NULL)
120 destroy_sizewin(resizer->sizewin);
121 resizer->sizewin = create_sizewin(win, xdim, ydim);
122 } else if (ep->button == Button3) {
123 if (resizer->sizewin != NULL) {
124 destroy_sizewin(resizer->sizewin);
125 resizer->sizewin = NULL;
126 }
127 show_menu(winmenu, ep->x_root, ep->y_root, ep->button);
128 }
129 }
130
131 static void release(struct resizer *resizer, XButtonEvent *ep)
132 {
133 if (ep->button == Button1 && resizer->sizewin != NULL) {
134 destroy_sizewin(resizer->sizewin);
135 resizer->sizewin = NULL;
136 }
137 }
138
139 static void motion(struct resizer *resizer, XMotionEvent *ep)
140 {
141 struct window *win = resizer->window;
142 int x, y;
143 int width, height;
144 int rwidth, rheight;
145 int xdim, ydim;
146
147 if (resizer->sizewin == NULL)
148 return;
149
150 switch (resizer->dir) {
151 case NORTHWEST:
152 width = WIDGET_X(win) + WIDGET_WIDTH(win) - ep->x_root;
153 height = WIDGET_Y(win) + WIDGET_HEIGHT(win) - ep->y_root;
154 break;
155 case NORTH:
156 if(win->maximized) {
157 return;
158 }
159 width = WIDGET_WIDTH(win);
160 height = WIDGET_Y(win) + WIDGET_HEIGHT(win) - ep->y_root;
161 break;
162 case NORTHEAST:
163 width = 1 + ep->x_root - WIDGET_X(win);
164 height = WIDGET_Y(win) + WIDGET_HEIGHT(win) - ep->y_root;
165 break;
166 case WEST:
167 width = WIDGET_X(win) + WIDGET_WIDTH(win) - ep->x_root;
168 height = WIDGET_HEIGHT(win);
169 break;
170 case EAST:
171 width = 1 + ep->x_root - WIDGET_X(win);
172 height = WIDGET_HEIGHT(win);
173 break;
174 case SOUTHWEST:
175 width = WIDGET_X(win) + WIDGET_WIDTH(win) - ep->x_root;
176 height = 1 + ep->y_root - WIDGET_Y(win);
177 break;
178 case SOUTH:
179 width = WIDGET_WIDTH(win);
180 height = 1 + ep->y_root - WIDGET_Y(win);
181 break;
182 case SOUTHEAST:
183 width = 1 + ep->x_root - WIDGET_X(win);
184 height = 1 + ep->y_root - WIDGET_Y(win);
185 break;
186 default:
187 abort();
188 break;
189 }
190
191 window_calcsize(win, width, height, &rwidth, &rheight, &xdim, &ydim);
192
193 switch (resizer->dir) {
194 case NORTHWEST:
195 x = WIDGET_X(win) + WIDGET_WIDTH(win) - rwidth;
196 y = WIDGET_Y(win) + WIDGET_HEIGHT(win) - rheight;
197 break;
198 case NORTHEAST:
199 x = WIDGET_X(win);
200 y = WIDGET_Y(win) + WIDGET_HEIGHT(win) - rheight;
201 break;
202 case NORTH:
203 x = WIDGET_X(win);
204 y = WIDGET_Y(win) + WIDGET_HEIGHT(win) - rheight;
205 break;
206 case WEST:
207 x = WIDGET_X(win) + WIDGET_WIDTH(win) - rwidth;
208 y = WIDGET_Y(win);
209 break;
210 case SOUTHWEST:
211 x = WIDGET_X(win) + WIDGET_WIDTH(win) - rwidth;
212 y = WIDGET_Y(win);
213 break;
214 default:
215 x = WIDGET_X(win);
216 y = WIDGET_Y(win);
217 break;
218 }
219
220 if (rwidth != WIDGET_WIDTH(win) || rheight != WIDGET_HEIGHT(win)) {
221 resizer->window->maximized = 0;
222 moveresize_window(resizer->window, x, y, rwidth, rheight);
223 update_sizewin(resizer->sizewin, xdim, ydim);
224 }
225 }
226
227 static void resizerevent(struct widget *widget, XEvent *ep)
228 {
229 switch (ep->type) {
230 case ButtonPress:
231 press((struct resizer *)widget, &ep->xbutton);
232 break;
233 case ButtonRelease:
234 release((struct resizer *)widget, &ep->xbutton);
235 break;
236 case MotionNotify:
237 motion((struct resizer *)widget, &ep->xmotion);
238 break;
239 }
240 }
241
242 struct resizer *create_resizer(struct window *win, int dir)
243 {
244 if (win->maximized&&dir==NORTH) return NULL;
245
246 struct resizer *resizer;
247 static int initialized = 0;
248
249 if (!initialized) {
250 initialized = 1;
251 c1 = XCreateFontCursor(display, XC_top_left_corner);
252 c2 = XCreateFontCursor(display, XC_top_side);
253 c3 = XCreateFontCursor(display, XC_top_right_corner);
254 c4 = XCreateFontCursor(display, XC_left_side);
255 c5 = XCreateFontCursor(display, XC_right_side);
256 c6 = XCreateFontCursor(display, XC_bottom_left_corner);
257 c7 = XCreateFontCursor(display, XC_bottom_side);
258 c8 = XCreateFontCursor(display, XC_bottom_right_corner);
259 }
260
261 resizer = MALLOC(sizeof (struct resizer));
262 create_widget(&resizer->widget, WIDGET_RESIZER,
263 WIDGET_XWINDOW(win), InputOnly, 0, 0, 1, 1);
264 resizer->widget.event = resizerevent;
265 XSelectInput(display, WIDGET_XWINDOW(resizer),
266 ButtonPressMask | ButtonReleaseMask | ButtonMotionMask);
267 resizer->window = win;
268 resizer->sizewin = NULL;
269 resizer->dir = dir;
270 fit_resizer(resizer);
271
272 switch (resizer->dir) {
273 case NORTHWEST:
274 XDefineCursor(display, WIDGET_XWINDOW(resizer), c1);
275 break;
276 case NORTH:
277 XDefineCursor(display, WIDGET_XWINDOW(resizer), c2);
278 break;
279 case NORTHEAST:
280 XDefineCursor(display, WIDGET_XWINDOW(resizer), c3);
281 break;
282 case WEST:
283 XDefineCursor(display, WIDGET_XWINDOW(resizer), c4);
284 break;
285 case EAST:
286 XDefineCursor(display, WIDGET_XWINDOW(resizer), c5);
287 break;
288 case SOUTHWEST:
289 XDefineCursor(display, WIDGET_XWINDOW(resizer), c6);
290 break;
291 case SOUTH:
292 XDefineCursor(display, WIDGET_XWINDOW(resizer), c7);
293 break;
294 case SOUTHEAST:
295 XDefineCursor(display, WIDGET_XWINDOW(resizer), c8);
296 break;
297 default:
298 abort();
299 }
300
301 XLowerWindow(display, WIDGET_XWINDOW(resizer));
302 map_widget(&resizer->widget);
303 return resizer;
304 }
305
306 void destroy_resizer(struct resizer *resizer)
307 {
308 if (resizer->sizewin != NULL)
309 destroy_sizewin(resizer->sizewin);
310 destroy_widget(&resizer->widget);
311 FREE(resizer);
312 }
313
314 void fit_resizer(struct resizer *resizer)
315 {
316 struct window *window = resizer->window;
317 int x, y;
318 int ux, uy;
319 int width, height;
320
321 ux = MIN(button_size, WIDGET_WIDTH(window) / 3);
322 uy = MIN(button_size, WIDGET_HEIGHT(window) / 3);
323
324 switch (resizer->dir) {
325 case NORTHWEST:
326 x = 0;
327 y = 0;
328 width = ux;
329 height = uy;
330 break;
331 case NORTH:
332 x = ux;
333 y = 0;
334 width = WIDGET_WIDTH(window) - 2 * ux;
335 height = border_width;
336 break;
337 case NORTHEAST:
338 x = WIDGET_WIDTH(window) - ux;
339 y = 0;
340 width = ux;
341 height = uy;
342 break;
343 case WEST:
344 x = 0;
345 y = uy;
346 width = border_width;
347 height = WIDGET_HEIGHT(window) - 2 * uy;
348 break;
349 case EAST:
350 x = WIDGET_WIDTH(window) - border_width;
351 y = uy;
352 width = border_width;
353 height = WIDGET_HEIGHT(window) - 2 * uy;
354 break;
355 case SOUTHWEST:
356 x = 0;
357 y = WIDGET_HEIGHT(window) - uy;
358 width = ux;
359 height = uy;
360 break;
361 case SOUTH:
362 x = ux;
363 y = WIDGET_HEIGHT(window) - border_width;
364 width = WIDGET_WIDTH(window) - 2 * ux;
365 height = border_width;
366 break;
367 case SOUTHEAST:
368 x = WIDGET_WIDTH(window) - ux;
369 y = WIDGET_HEIGHT(window) - uy;
370 width = ux;
371 height = uy;
372 break;
373 default:
374 abort();
375 }
376 moveresize_widget(&resizer->widget, x, y, width, height);
377 }
378