code review
[wmaker-crm.git] / src / placement.c
blobb69736769acc0d0eab9fdfcc13f9f5b3c465a8db
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"
45 extern WPreferences wPreferences;
48 #define X_ORIGIN WMAX(usableArea.x1,\
49 wPreferences.window_place_origin.x)
51 #define Y_ORIGIN WMAX(usableArea.y1,\
52 wPreferences.window_place_origin.y)
56 * interactive window placement is in moveres.c
59 extern void InteractivePlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
60 unsigned width, unsigned height);
64 * Returns True if it is an icon and is in this workspace.
66 static Bool
67 iconPosition(WCoreWindow *wcore, int sx1, int sy1, int sx2, int sy2,
68 int workspace, int *retX, int *retY)
70 void *parent;
71 int ok = 0;
73 parent = wcore->descriptor.parent;
75 /* if it is an application icon */
76 if (wcore->descriptor.parent_type == WCLASS_APPICON
77 && !((WAppIcon*)parent)->docked) {
78 *retX = ((WAppIcon*)parent)->x_pos;
79 *retY = ((WAppIcon*)parent)->y_pos;
81 ok = 1;
82 } else if (wcore->descriptor.parent_type == WCLASS_MINIWINDOW &&
83 (((WIcon*)parent)->owner->frame->workspace == workspace
84 || IS_OMNIPRESENT(((WIcon*)parent)->owner)
85 || wPreferences.sticky_icons)
86 && ((WIcon*)parent)->mapped) {
88 *retX = ((WIcon*)parent)->owner->icon_x;
89 *retY = ((WIcon*)parent)->owner->icon_y;
91 ok = 1;
92 } else if (wcore->descriptor.parent_type == WCLASS_WINDOW
93 && ((WWindow*)parent)->flags.icon_moved
94 && (((WWindow*)parent)->frame->workspace == workspace
95 || IS_OMNIPRESENT((WWindow*)parent)
96 || wPreferences.sticky_icons)) {
97 *retX = ((WWindow*)parent)->icon_x;
98 *retY = ((WWindow*)parent)->icon_y;
100 ok = 1;
105 * Check if it is inside the screen.
107 if (ok) {
108 if (*retX < sx1-wPreferences.icon_size)
109 ok = 0;
110 else if (*retX > sx2)
111 ok = 0;
113 if (*retY < sy1-wPreferences.icon_size)
114 ok = 0;
115 else if (*retY > sy2)
116 ok = 0;
119 return ok;
125 void
126 PlaceIcon(WScreen *scr, int *x_ret, int *y_ret, int head)
128 int pf; /* primary axis */
129 int sf; /* secondary axis */
130 int fullW;
131 int fullH;
132 char *map;
133 int pi, si;
134 WCoreWindow *obj;
135 int sx1, sx2, sy1, sy2; /* screen boundary */
136 int sw, sh;
137 int xo, yo;
138 int xs, ys;
139 int x, y;
140 int isize = wPreferences.icon_size;
141 int done = 0;
142 WMBagIterator iter;
143 WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
145 /* Find out screen boundaries. */
147 /* Allows each head to have miniwindows */
149 sx1 = area.x1;
150 sy1 = area.y1;
151 sx2 = area.x2;
152 sy2 = area.y2;
153 sw = sx2-sx1;
154 sh = sy2-sy1;
156 #if 0
157 if (scr->dock) {
158 if (scr->dock->on_right_side)
159 sx2 -= isize + DOCK_EXTRA_SPACE;
160 else
161 sx1 += isize + DOCK_EXTRA_SPACE;
163 #endif
165 sw = isize * (sw/isize);
166 sh = isize * (sh/isize);
167 fullW = (sx2-sx1)/isize;
168 fullH = (sy2-sy1)/isize;
170 /* icon yard boundaries */
171 if (wPreferences.icon_yard & IY_VERT) {
172 pf = fullH;
173 sf = fullW;
174 } else {
175 pf = fullW;
176 sf = fullH;
178 if (wPreferences.icon_yard & IY_RIGHT) {
179 xo = sx2 - isize;
180 xs = -1;
181 } else {
182 xo = sx1;
183 xs = 1;
185 if (wPreferences.icon_yard & IY_TOP) {
186 yo = sy1;
187 ys = 1;
188 } else {
189 yo = sy2 - isize;
190 ys = -1;
194 * Create a map with the occupied slots. 1 means the slot is used
195 * or at least partially used.
196 * The slot usage can be optimized by only marking fully used slots
197 * or slots that have most of it covered.
198 * Space usage is worse than the fvwm algorithm (used in the old version)
199 * but complexity is much better (faster) than it.
201 map = wmalloc((sw+2) * (sh+2));
202 memset(map, 0, (sw+2) * (sh+2));
204 #define INDEX(x,y) (((y)+1)*(sw+2) + (x) + 1)
206 WM_ETARETI_BAG(scr->stacking_list, obj, iter) {
208 while (obj) {
209 int x, y;
211 if (iconPosition(obj, sx1, sy1, sx2, sy2, scr->current_workspace,
212 &x, &y)) {
213 int xdi, ydi; /* rounded down */
214 int xui, yui; /* rounded up */
216 xdi = x/isize;
217 ydi = y/isize;
218 xui = (x+isize/2)/isize;
219 yui = (y+isize/2)/isize;
220 map[INDEX(xdi,ydi)] = 1;
221 map[INDEX(xdi,yui)] = 1;
222 map[INDEX(xui,ydi)] = 1;
223 map[INDEX(xui,yui)] = 1;
225 obj = obj->stacking->under;
229 * Default position
231 *x_ret = 0;
232 *y_ret = 0;
235 * Look for an empty slot
237 for (si=0; si<sf; si++) {
238 for (pi=0; pi<pf; pi++) {
239 if (wPreferences.icon_yard & IY_VERT) {
240 x = xo + xs*(si*isize);
241 y = yo + ys*(pi*isize);
242 } else {
243 x = xo + xs*(pi*isize);
244 y = yo + ys*(si*isize);
246 if (!map[INDEX(x/isize, y/isize)]) {
247 *x_ret = x;
248 *y_ret = y;
249 done = 1;
250 break;
253 if (done)
254 break;
257 wfree(map);
262 * This function calculates the length of the intersection of two
263 * line sections. (Hey, is that english?)
265 static int
266 calcIntersectionLength(int p1, int l1, int p2, int l2)
268 int isect;
269 int tmp;
271 if (p1 > p2) {
272 tmp = p1;
273 p1 = p2;
274 p2 = tmp;
275 tmp = l1;
276 l1 = l2;
277 l2 = tmp;
280 if (p1 + l1 < p2)
281 isect = 0;
282 else if (p2 + l2 < p1 + l1)
283 isect = l2;
284 else
285 isect = p1 + l1 - p2;
287 return isect;
292 * This function calculates the area of the intersection of two rectangles.
296 calcIntersectionArea(int x1, int y1, int w1, int h1,
297 int x2, int y2, int w2, int h2)
299 return calcIntersectionLength(x1, w1, x2, w2)
300 * calcIntersectionLength(y1, h1, y2, h2);
304 static int
305 calcSumOfCoveredAreas(WWindow *wwin, int x, int y, int w, int h)
307 int sum_isect = 0;
308 WWindow *test_window;
309 int tw,tx,ty,th;
311 test_window = wwin->screen_ptr->focused_window;
312 for(;test_window != NULL && test_window->prev != NULL;)
313 test_window = test_window->prev;
315 for(; test_window != NULL; test_window = test_window->next) {
316 if (test_window->frame->core->stacking->window_level
317 < WMNormalLevel) {
318 continue;
321 #if 0
322 tw = test_window->client.width;
323 if (test_window->flags.shaded)
324 th = test_window->frame->top_width;
325 else
326 th = test_window->client.height + extra_height;
327 #else
328 tw = test_window->frame->core->width;
329 th = test_window->frame->core->height;
330 #endif
331 tx = test_window->frame_x;
332 ty = test_window->frame_y;
334 if (test_window->flags.mapped ||
335 (test_window->flags.shaded &&
336 !(test_window->flags.miniaturized ||
337 test_window->flags.hidden))) {
338 sum_isect += calcIntersectionArea(tx, ty, tw, th, x, y, w, h);
342 return sum_isect;
346 static void
347 smartPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
348 unsigned int width, unsigned int height,
349 WArea usableArea)
351 int test_x = 0, test_y = Y_ORIGIN;
352 int from_x, to_x, from_y, to_y;
353 int sx;
354 int min_isect, min_isect_x, min_isect_y;
355 int sum_isect;
357 if (wwin->frame) {
358 height += wwin->frame->top_width + wwin->frame->bottom_width;
359 } else {
360 if (HAS_TITLEBAR(wwin)) height += 18;
361 if (HAS_RESIZEBAR(wwin)) height += 8;
363 if (HAS_BORDER(wwin)) {
364 height += 2;
365 width += 2;
367 sx = X_ORIGIN;
369 min_isect = INT_MAX;
370 min_isect_x = sx;
371 min_isect_y = test_y;
373 while (((test_y + height) < usableArea.y2)) {
374 test_x = sx;
375 while ((test_x + width) < usableArea.x2) {
376 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
377 width, height);
379 if (sum_isect < min_isect) {
380 min_isect = sum_isect;
381 min_isect_x = test_x;
382 min_isect_y = test_y;
385 test_x += PLACETEST_HSTEP;
387 test_y += PLACETEST_VSTEP;
390 from_x = min_isect_x - PLACETEST_HSTEP + 1;
391 from_x = WMAX(from_x, X_ORIGIN);
392 to_x = min_isect_x + PLACETEST_HSTEP;
393 if (to_x + width > usableArea.x2)
394 to_x = usableArea.x2 - width;
396 from_y = min_isect_y - PLACETEST_VSTEP + 1;
397 from_y = WMAX(from_y, Y_ORIGIN);
398 to_y = min_isect_y + PLACETEST_VSTEP;
399 if (to_y + height > usableArea.y2)
400 to_y = usableArea.y2 - height;
402 for (test_x = from_x; test_x < to_x; test_x++) {
403 for (test_y = from_y; test_y < to_y; test_y++) {
404 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
405 width, height);
407 if (sum_isect < min_isect) {
408 min_isect = sum_isect;
409 min_isect_x = test_x;
410 min_isect_y = test_y;
415 *x_ret = min_isect_x;
416 *y_ret = min_isect_y;
420 static Bool
421 autoPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
422 unsigned int width, unsigned int height, int tryCount,
423 WArea usableArea)
425 WScreen *scr = wwin->screen_ptr;
426 int test_x = 0, test_y = Y_ORIGIN;
427 int loc_ok = False, tw,tx,ty,th;
428 int swidth, sx;
429 WWindow *test_window;
431 if (wwin->frame) {
432 height += wwin->frame->top_width + wwin->frame->bottom_width;
433 } else {
434 if (HAS_TITLEBAR(wwin)) height += 18;
435 if (HAS_RESIZEBAR(wwin)) height += 8;
437 if (HAS_BORDER(wwin)) {
438 height += 2;
439 width += 2;
442 swidth = usableArea.x2-usableArea.x1;
443 sx = X_ORIGIN;
445 /* this was based on fvwm2's smart placement */
447 while (((test_y + height) < (usableArea.y2 - usableArea.y1)) && !loc_ok) {
448 test_x = sx;
450 while (((test_x + width) < swidth) && (!loc_ok)) {
452 loc_ok = True;
453 test_window = scr->focused_window;
455 while ((test_window != NULL) && (loc_ok == True)) {
457 if (test_window->frame->core->stacking->window_level
458 < WMNormalLevel && tryCount > 0) {
459 test_window = test_window->next;
460 continue;
462 #if 0
463 tw = test_window->client.width;
464 if (test_window->flags.shaded)
465 th = test_window->frame->top_width;
466 else
467 th = test_window->client.height + extra_height;
468 #else
469 tw = test_window->frame->core->width;
470 th = test_window->frame->core->height;
471 #endif
472 tx = test_window->frame_x;
473 ty = test_window->frame_y;
475 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
476 (ty < (test_y + height)) && ((ty + th) > test_y) &&
477 (test_window->flags.mapped ||
478 (test_window->flags.shaded &&
479 test_window->frame->workspace==scr->current_workspace &&
480 !(test_window->flags.miniaturized ||
481 test_window->flags.hidden)))) {
483 loc_ok = False;
485 test_window = test_window->next;
488 test_window = scr->focused_window;
490 while ((test_window != NULL) && (loc_ok == True)) {
492 if (test_window->frame->core->stacking->window_level
493 < WMNormalLevel && tryCount > 0) {
494 test_window = test_window->prev;
495 continue;
497 #if 0
498 tw = test_window->client.width;
499 if (test_window->flags.shaded)
500 th = test_window->frame->top_width;
501 else
502 th = test_window->client.height + extra_height;
503 #else
504 tw = test_window->frame->core->width;
505 th = test_window->frame->core->height;
506 #endif
507 tx = test_window->frame_x;
508 ty = test_window->frame_y;
510 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
511 (ty < (test_y + height)) && ((ty + th) > test_y) &&
512 (test_window->flags.mapped ||
513 (test_window->flags.shaded &&
514 test_window->frame->workspace==scr->current_workspace &&
515 !(test_window->flags.miniaturized ||
516 test_window->flags.hidden)))) {
518 loc_ok = False;
520 test_window = test_window->prev;
522 if (loc_ok == True) {
523 *x_ret = test_x;
524 *y_ret = test_y;
525 break;
527 test_x += PLACETEST_HSTEP;
529 test_y += PLACETEST_VSTEP;
532 return loc_ok;
536 static void
537 cascadeWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
538 unsigned int width, unsigned int height, int h,
539 WArea usableArea)
541 if (wwin->frame) {
542 height += wwin->frame->top_width + wwin->frame->bottom_width;
543 } else {
544 if (HAS_TITLEBAR(wwin)) height += 18;
545 if (HAS_RESIZEBAR(wwin)) height += 8;
547 if (HAS_BORDER(wwin)) {
548 height += 2;
549 width += 2;
552 *x_ret = h * scr->cascade_index + X_ORIGIN;
553 *y_ret = h * scr->cascade_index + Y_ORIGIN;
555 if (width + *x_ret > usableArea.x2 || height + *y_ret > usableArea.y2) {
556 scr->cascade_index = 0;
557 *x_ret = X_ORIGIN;
558 *y_ret = Y_ORIGIN;
563 static void
564 randomPlaceWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
565 unsigned int width, unsigned int height,
566 WArea usableArea)
568 int w, h;
570 if (wwin->frame) {
571 height += wwin->frame->top_width + wwin->frame->bottom_width;
572 } else {
573 if (HAS_TITLEBAR(wwin)) height += 18;
574 if (HAS_RESIZEBAR(wwin)) height += 8;
576 if (HAS_BORDER(wwin)) {
577 height += 2;
578 width += 2;
581 w = ((usableArea.x2-X_ORIGIN) - width);
582 h = ((usableArea.y2-Y_ORIGIN) - height);
583 if (w<1) w = 1;
584 if (h<1) h = 1;
585 *x_ret = X_ORIGIN + rand()%w;
586 *y_ret = Y_ORIGIN + rand()%h;
591 void
592 PlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
593 unsigned width, unsigned height)
595 WScreen *scr = wwin->screen_ptr;
596 int h = WMFontHeight(scr->title_font) + (wPreferences.window_title_clearance + TITLEBAR_EXTEND_SPACE) * 2;
597 WArea usableArea = wGetUsableAreaForHead(scr,
598 wGetHeadForPointerLocation(scr),
599 NULL, True);
601 switch (wPreferences.window_placement) {
602 case WPM_MANUAL:
603 InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
604 break;
606 case WPM_SMART:
607 smartPlaceWindow(wwin, x_ret, y_ret, width, height, usableArea);
608 break;
610 case WPM_AUTO:
611 if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 0,
612 usableArea)) {
613 break;
614 } else if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 1,
615 usableArea)) {
616 break;
618 /* there isn't a break here, because if we fail, it should fall
619 through to cascade placement, as people who want tiling want
620 automagicness aren't going to want to place their window */
622 case WPM_CASCADE:
623 if (wPreferences.window_placement == WPM_AUTO)
624 scr->cascade_index++;
626 cascadeWindow(scr, wwin, x_ret, y_ret, width, height, h, usableArea);
628 if (wPreferences.window_placement == WPM_CASCADE)
629 scr->cascade_index++;
630 break;
632 case WPM_RANDOM:
633 randomPlaceWindow(scr, wwin, x_ret, y_ret, width, height, usableArea);
634 break;
636 #ifdef DEBUG
637 default:
638 puts("Invalid window placement!!!");
639 *x_ret = 0;
640 *y_ret = 0;
641 #endif
645 * clip to usableArea instead of full screen
646 * this will also take dock/clip etc.. into account
647 * aswell as being xinerama friendly
649 if (*x_ret + width > usableArea.x2)
650 *x_ret = usableArea.x2 - width;
651 if (*x_ret < usableArea.x1)
652 *x_ret = usableArea.x1;
654 if (*y_ret + height > usableArea.y2)
655 *y_ret = usableArea.y2 - height;
656 if (*y_ret < usableArea.y1)
657 *y_ret = usableArea.y1;