Define TITLEBAR_HEIGHT in default configuration file
[wmaker-crm.git] / src / placement.c
blobcfdc67d472eaeb44330d0b63d2312c8e4ca07e7b
1 /* placement.c - window and icon placement on screen
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 * USA.
23 #include "wconfig.h"
25 #include <X11/Xlib.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <limits.h>
31 #include "WindowMaker.h"
32 #include "wcore.h"
33 #include "framewin.h"
34 #include "window.h"
35 #include "icon.h"
36 #include "appicon.h"
37 #include "actions.h"
38 #include "funcs.h"
39 #include "application.h"
40 #include "appicon.h"
41 #include "dock.h"
42 #include "xinerama.h"
44 extern WPreferences wPreferences;
46 #define X_ORIGIN WMAX(usableArea.x1,\
47 wPreferences.window_place_origin.x)
49 #define Y_ORIGIN WMAX(usableArea.y1,\
50 wPreferences.window_place_origin.y)
53 * interactive window placement is in moveres.c
56 extern void InteractivePlaceWindow(WWindow * wwin, int *x_ret, int *y_ret, unsigned width, unsigned height);
59 * Returns True if it is an icon and is in this workspace.
61 static Bool
62 iconPosition(WCoreWindow * wcore, int sx1, int sy1, int sx2, int sy2, int workspace, int *retX, int *retY)
64 void *parent;
65 int ok = 0;
67 parent = wcore->descriptor.parent;
69 /* if it is an application icon */
70 if (wcore->descriptor.parent_type == WCLASS_APPICON && !((WAppIcon *) parent)->docked) {
71 *retX = ((WAppIcon *) parent)->x_pos;
72 *retY = ((WAppIcon *) parent)->y_pos;
74 ok = 1;
75 } else if (wcore->descriptor.parent_type == WCLASS_MINIWINDOW &&
76 (((WIcon *) parent)->owner->frame->workspace == workspace
77 || IS_OMNIPRESENT(((WIcon *) parent)->owner)
78 || wPreferences.sticky_icons)
79 && ((WIcon *) parent)->mapped) {
81 *retX = ((WIcon *) parent)->owner->icon_x;
82 *retY = ((WIcon *) parent)->owner->icon_y;
84 ok = 1;
85 } else if (wcore->descriptor.parent_type == WCLASS_WINDOW
86 && ((WWindow *) parent)->flags.icon_moved
87 && (((WWindow *) parent)->frame->workspace == workspace || IS_OMNIPRESENT((WWindow *) parent)
88 || wPreferences.sticky_icons)) {
89 *retX = ((WWindow *) parent)->icon_x;
90 *retY = ((WWindow *) parent)->icon_y;
92 ok = 1;
96 * Check if it is inside the screen.
98 if (ok) {
99 if (*retX < sx1 - wPreferences.icon_size)
100 ok = 0;
101 else if (*retX > sx2)
102 ok = 0;
104 if (*retY < sy1 - wPreferences.icon_size)
105 ok = 0;
106 else if (*retY > sy2)
107 ok = 0;
110 return ok;
113 void PlaceIcon(WScreen * scr, int *x_ret, int *y_ret, int head)
115 int pf; /* primary axis */
116 int sf; /* secondary axis */
117 int fullW;
118 int fullH;
119 char *map;
120 int pi, si;
121 WCoreWindow *obj;
122 int sx1, sx2, sy1, sy2; /* screen boundary */
123 int sw, sh;
124 int xo, yo;
125 int xs, ys;
126 int x, y;
127 int isize = wPreferences.icon_size;
128 int done = 0;
129 WMBagIterator iter;
130 WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
132 /* Find out screen boundaries. */
134 /* Allows each head to have miniwindows */
136 sx1 = area.x1;
137 sy1 = area.y1;
138 sx2 = area.x2;
139 sy2 = area.y2;
140 sw = sx2 - sx1;
141 sh = sy2 - sy1;
143 #if 0
144 if (scr->dock) {
145 if (scr->dock->on_right_side)
146 sx2 -= isize + DOCK_EXTRA_SPACE;
147 else
148 sx1 += isize + DOCK_EXTRA_SPACE;
150 #endif
152 sw = isize * (sw / isize);
153 sh = isize * (sh / isize);
154 fullW = (sx2 - sx1) / isize;
155 fullH = (sy2 - sy1) / isize;
157 /* icon yard boundaries */
158 if (wPreferences.icon_yard & IY_VERT) {
159 pf = fullH;
160 sf = fullW;
161 } else {
162 pf = fullW;
163 sf = fullH;
165 if (wPreferences.icon_yard & IY_RIGHT) {
166 xo = sx2 - isize;
167 xs = -1;
168 } else {
169 xo = sx1;
170 xs = 1;
172 if (wPreferences.icon_yard & IY_TOP) {
173 yo = sy1;
174 ys = 1;
175 } else {
176 yo = sy2 - isize;
177 ys = -1;
181 * Create a map with the occupied slots. 1 means the slot is used
182 * or at least partially used.
183 * The slot usage can be optimized by only marking fully used slots
184 * or slots that have most of it covered.
185 * Space usage is worse than the fvwm algorithm (used in the old version)
186 * but complexity is much better (faster) than it.
188 map = wmalloc((sw + 2) * (sh + 2));
189 memset(map, 0, (sw + 2) * (sh + 2));
191 #define INDEX(x,y) (((y)+1)*(sw+2) + (x) + 1)
193 WM_ETARETI_BAG(scr->stacking_list, obj, iter) {
195 while (obj) {
196 int x, y;
198 if (iconPosition(obj, sx1, sy1, sx2, sy2, scr->current_workspace, &x, &y)) {
199 int xdi, ydi; /* rounded down */
200 int xui, yui; /* rounded up */
202 xdi = x / isize;
203 ydi = y / isize;
204 xui = (x + isize / 2) / isize;
205 yui = (y + isize / 2) / isize;
206 map[INDEX(xdi, ydi)] = 1;
207 map[INDEX(xdi, yui)] = 1;
208 map[INDEX(xui, ydi)] = 1;
209 map[INDEX(xui, yui)] = 1;
211 obj = obj->stacking->under;
215 * Default position
217 *x_ret = 0;
218 *y_ret = 0;
221 * Look for an empty slot
223 for (si = 0; si < sf; si++) {
224 for (pi = 0; pi < pf; pi++) {
225 if (wPreferences.icon_yard & IY_VERT) {
226 x = xo + xs * (si * isize);
227 y = yo + ys * (pi * isize);
228 } else {
229 x = xo + xs * (pi * isize);
230 y = yo + ys * (si * isize);
232 if (!map[INDEX(x / isize, y / isize)]) {
233 *x_ret = x;
234 *y_ret = y;
235 done = 1;
236 break;
239 if (done)
240 break;
243 wfree(map);
247 * This function calculates the length of the intersection of two
248 * line sections. (Hey, is that english?)
250 static int calcIntersectionLength(int p1, int l1, int p2, int l2)
252 int isect;
253 int tmp;
255 if (p1 > p2) {
256 tmp = p1;
257 p1 = p2;
258 p2 = tmp;
259 tmp = l1;
260 l1 = l2;
261 l2 = tmp;
264 if (p1 + l1 < p2)
265 isect = 0;
266 else if (p2 + l2 < p1 + l1)
267 isect = l2;
268 else
269 isect = p1 + l1 - p2;
271 return isect;
275 * This function calculates the area of the intersection of two rectangles.
278 int calcIntersectionArea(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2)
280 return calcIntersectionLength(x1, w1, x2, w2)
281 * calcIntersectionLength(y1, h1, y2, h2);
284 static int calcSumOfCoveredAreas(WWindow * wwin, int x, int y, int w, int h)
286 int sum_isect = 0;
287 WWindow *test_window;
288 int tw, tx, ty, th;
290 test_window = wwin->screen_ptr->focused_window;
291 for (; test_window != NULL && test_window->prev != NULL;)
292 test_window = test_window->prev;
294 for (; test_window != NULL; test_window = test_window->next) {
295 if (test_window->frame->core->stacking->window_level < WMNormalLevel) {
296 continue;
298 #if 0
299 tw = test_window->client.width;
300 if (test_window->flags.shaded)
301 th = test_window->frame->top_width;
302 else
303 th = test_window->client.height + extra_height;
304 #else
305 tw = test_window->frame->core->width;
306 th = test_window->frame->core->height;
307 #endif
308 tx = test_window->frame_x;
309 ty = test_window->frame_y;
311 if (test_window->flags.mapped || (test_window->flags.shaded &&
312 test_window->frame->workspace == wwin->screen_ptr->current_workspace &&
313 !(test_window->flags.miniaturized || test_window->flags.hidden))) {
314 sum_isect += calcIntersectionArea(tx, ty, tw, th, x, y, w, h);
318 return sum_isect;
321 static void set_width_height(WWindow *wwin, unsigned int *width, unsigned int *height)
323 if (wwin->frame) {
324 *height += wwin->frame->top_width + wwin->frame->bottom_width;
325 } else {
326 if (HAS_TITLEBAR(wwin))
327 *height += TITLEBAR_HEIGHT;
328 if (HAS_RESIZEBAR(wwin))
329 *height += RESIZEBAR_HEIGHT;
331 if (HAS_BORDER(wwin)) {
332 *height += 2 * FRAME_BORDER_WIDTH;
333 *width += 2 * FRAME_BORDER_WIDTH;
337 static void
338 smartPlaceWindow(WWindow * wwin, int *x_ret, int *y_ret, unsigned int width, unsigned int height, WArea usableArea)
340 int test_x = 0, test_y = Y_ORIGIN;
341 int from_x, to_x, from_y, to_y;
342 int sx;
343 int min_isect, min_isect_x, min_isect_y;
344 int sum_isect;
346 set_width_height(wwin, &width, &height);
348 sx = X_ORIGIN;
349 min_isect = INT_MAX;
350 min_isect_x = sx;
351 min_isect_y = test_y;
353 while (((test_y + height) < usableArea.y2)) {
354 test_x = sx;
355 while ((test_x + width) < usableArea.x2) {
356 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y, width, height);
358 if (sum_isect < min_isect) {
359 min_isect = sum_isect;
360 min_isect_x = test_x;
361 min_isect_y = test_y;
364 test_x += PLACETEST_HSTEP;
366 test_y += PLACETEST_VSTEP;
369 from_x = min_isect_x - PLACETEST_HSTEP + 1;
370 from_x = WMAX(from_x, X_ORIGIN);
371 to_x = min_isect_x + PLACETEST_HSTEP;
372 if (to_x + width > usableArea.x2)
373 to_x = usableArea.x2 - width;
375 from_y = min_isect_y - PLACETEST_VSTEP + 1;
376 from_y = WMAX(from_y, Y_ORIGIN);
377 to_y = min_isect_y + PLACETEST_VSTEP;
378 if (to_y + height > usableArea.y2)
379 to_y = usableArea.y2 - height;
381 for (test_x = from_x; test_x < to_x; test_x++) {
382 for (test_y = from_y; test_y < to_y; test_y++) {
383 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y, width, height);
385 if (sum_isect < min_isect) {
386 min_isect = sum_isect;
387 min_isect_x = test_x;
388 min_isect_y = test_y;
393 *x_ret = min_isect_x;
394 *y_ret = min_isect_y;
397 static Bool
398 autoPlaceWindow(WWindow * wwin, int *x_ret, int *y_ret,
399 unsigned int width, unsigned int height, int tryCount, WArea usableArea)
401 WScreen *scr = wwin->screen_ptr;
402 int test_x = 0, test_y = Y_ORIGIN;
403 int loc_ok = False, tw, tx, ty, th;
404 int swidth, sx;
405 WWindow *test_window;
407 set_width_height(wwin, &width, &height);
408 swidth = usableArea.x2 - usableArea.x1;
409 sx = X_ORIGIN;
411 /* this was based on fvwm2's smart placement */
412 while (((test_y + height) < (usableArea.y2 - usableArea.y1)) && !loc_ok) {
413 test_x = sx;
415 while (((test_x + width) < swidth) && (!loc_ok)) {
417 loc_ok = True;
418 test_window = scr->focused_window;
420 while ((test_window != NULL) && (loc_ok == True)) {
422 if (test_window->frame->core->stacking->window_level
423 < WMNormalLevel && tryCount > 0) {
424 test_window = test_window->next;
425 continue;
427 #if 0
428 tw = test_window->client.width;
429 if (test_window->flags.shaded)
430 th = test_window->frame->top_width;
431 else
432 th = test_window->client.height + extra_height;
433 #else
434 tw = test_window->frame->core->width;
435 th = test_window->frame->core->height;
436 #endif
437 tx = test_window->frame_x;
438 ty = test_window->frame_y;
440 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
441 (ty < (test_y + height)) && ((ty + th) > test_y) &&
442 (test_window->flags.mapped ||
443 (test_window->flags.shaded &&
444 test_window->frame->workspace == scr->current_workspace &&
445 !(test_window->flags.miniaturized || test_window->flags.hidden)))) {
447 loc_ok = False;
449 test_window = test_window->next;
452 test_window = scr->focused_window;
454 while ((test_window != NULL) && (loc_ok == True)) {
456 if (test_window->frame->core->stacking->window_level
457 < WMNormalLevel && tryCount > 0) {
458 test_window = test_window->prev;
459 continue;
461 #if 0
462 tw = test_window->client.width;
463 if (test_window->flags.shaded)
464 th = test_window->frame->top_width;
465 else
466 th = test_window->client.height + extra_height;
467 #else
468 tw = test_window->frame->core->width;
469 th = test_window->frame->core->height;
470 #endif
471 tx = test_window->frame_x;
472 ty = test_window->frame_y;
474 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
475 (ty < (test_y + height)) && ((ty + th) > test_y) &&
476 (test_window->flags.mapped ||
477 (test_window->flags.shaded &&
478 test_window->frame->workspace == scr->current_workspace &&
479 !(test_window->flags.miniaturized || test_window->flags.hidden)))) {
481 loc_ok = False;
483 test_window = test_window->prev;
485 if (loc_ok == True) {
486 *x_ret = test_x;
487 *y_ret = test_y;
488 break;
490 test_x += PLACETEST_HSTEP;
492 test_y += PLACETEST_VSTEP;
495 return loc_ok;
498 static void
499 cascadeWindow(WScreen * scr, WWindow * wwin, int *x_ret, int *y_ret,
500 unsigned int width, unsigned int height, int h, WArea usableArea)
502 set_width_height(wwin, &width, &height);
504 *x_ret = h * scr->cascade_index + X_ORIGIN;
505 *y_ret = h * scr->cascade_index + Y_ORIGIN;
507 if (width + *x_ret > usableArea.x2 || height + *y_ret > usableArea.y2) {
508 scr->cascade_index = 0;
509 *x_ret = X_ORIGIN;
510 *y_ret = Y_ORIGIN;
514 static void
515 randomPlaceWindow(WScreen * scr, WWindow * wwin, int *x_ret, int *y_ret,
516 unsigned int width, unsigned int height, WArea usableArea)
518 int w, h;
520 set_width_height(wwin, &width, &height);
522 w = ((usableArea.x2 - X_ORIGIN) - width);
523 h = ((usableArea.y2 - Y_ORIGIN) - height);
524 if (w < 1)
525 w = 1;
526 if (h < 1)
527 h = 1;
528 *x_ret = X_ORIGIN + rand() % w;
529 *y_ret = Y_ORIGIN + rand() % h;
532 void PlaceWindow(WWindow * wwin, int *x_ret, int *y_ret, unsigned width, unsigned height)
534 WScreen *scr = wwin->screen_ptr;
535 int h = WMFontHeight(scr->title_font) + (wPreferences.window_title_clearance + TITLEBAR_EXTEND_SPACE) * 2;
536 WArea usableArea = wGetUsableAreaForHead(scr,
537 wGetHeadForPointerLocation(scr),
538 NULL, True);
540 switch (wPreferences.window_placement) {
541 case WPM_MANUAL:
542 InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
543 break;
545 case WPM_SMART:
546 smartPlaceWindow(wwin, x_ret, y_ret, width, height, usableArea);
547 break;
549 case WPM_AUTO:
550 if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 0, usableArea)) {
551 break;
552 } else if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 1, usableArea)) {
553 break;
555 /* there isn't a break here, because if we fail, it should fall
556 through to cascade placement, as people who want tiling want
557 automagicness aren't going to want to place their window */
559 case WPM_CASCADE:
560 if (wPreferences.window_placement == WPM_AUTO)
561 scr->cascade_index++;
563 cascadeWindow(scr, wwin, x_ret, y_ret, width, height, h, usableArea);
565 if (wPreferences.window_placement == WPM_CASCADE)
566 scr->cascade_index++;
567 break;
569 case WPM_RANDOM:
570 randomPlaceWindow(scr, wwin, x_ret, y_ret, width, height, usableArea);
571 break;
573 #ifdef DEBUG
574 default:
575 puts("Invalid window placement!!!");
576 *x_ret = 0;
577 *y_ret = 0;
578 #endif
582 * clip to usableArea instead of full screen
583 * this will also take dock/clip etc.. into account
584 * aswell as being xinerama friendly
586 if (*x_ret + width > usableArea.x2)
587 *x_ret = usableArea.x2 - width;
588 if (*x_ret < usableArea.x1)
589 *x_ret = usableArea.x1;
591 if (*y_ret + height > usableArea.y2)
592 *y_ret = usableArea.y2 - height;
593 if (*y_ret < usableArea.y1)
594 *y_ret = usableArea.y1;