- Fixed crashing bug in menu.c
[wmaker-crm.git] / src / placement.c
blob130efae5b752c50d20c55476f5237c28c77787fd
1 /* placement.c - window and icon placement on screen
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 *
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(scr) WMAX((scr)->totalUsableArea.x1,\
49 wPreferences.window_place_origin.x)
51 #define Y_ORIGIN(scr) WMAX((scr)->totalUsableArea.y1,\
52 wPreferences.window_place_origin.y)
56 * interactive window placement is in moveres.c
59 extern void
60 InteractivePlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
61 unsigned width, unsigned height);
65 * Returns True if it is an icon and is in this workspace.
67 static Bool
68 iconPosition(WCoreWindow *wcore, int sx1, int sy1, int sx2, int sy2,
69 int workspace, int *retX, int *retY)
71 void *parent;
72 int ok = 0;
74 parent = wcore->descriptor.parent;
76 /* if it is an application icon */
77 if (wcore->descriptor.parent_type == WCLASS_APPICON
78 && !((WAppIcon*)parent)->docked) {
79 *retX = ((WAppIcon*)parent)->x_pos;
80 *retY = ((WAppIcon*)parent)->y_pos;
82 ok = 1;
83 } else if (wcore->descriptor.parent_type == WCLASS_MINIWINDOW &&
84 (((WIcon*)parent)->owner->frame->workspace == workspace
85 || IS_OMNIPRESENT(((WIcon*)parent)->owner)
86 || wPreferences.sticky_icons)
87 && ((WIcon*)parent)->mapped) {
89 *retX = ((WIcon*)parent)->owner->icon_x;
90 *retY = ((WIcon*)parent)->owner->icon_y;
92 ok = 1;
93 } else if (wcore->descriptor.parent_type == WCLASS_WINDOW
94 && ((WWindow*)parent)->flags.icon_moved
95 && (((WWindow*)parent)->frame->workspace == workspace
96 || IS_OMNIPRESENT((WWindow*)parent)
97 || wPreferences.sticky_icons)) {
98 *retX = ((WWindow*)parent)->icon_x;
99 *retY = ((WWindow*)parent)->icon_y;
101 ok = 1;
106 * Check if it is inside the screen.
108 if (ok) {
109 if (*retX < sx1-wPreferences.icon_size)
110 ok = 0;
111 else if (*retX > sx2)
112 ok = 0;
114 if (*retY < sy1-wPreferences.icon_size)
115 ok = 0;
116 else if (*retY > sy2)
117 ok = 0;
120 return ok;
126 void
127 PlaceIcon(WScreen *scr, int *x_ret, int *y_ret)
129 int pf; /* primary axis */
130 int sf; /* secondary axis */
131 int fullW;
132 int fullH;
133 char *map;
134 int pi, si;
135 WCoreWindow *obj;
136 int sx1, sx2, sy1, sy2; /* screen boundary */
137 int sw, sh;
138 int xo, yo;
139 int xs, ys;
140 int x, y;
141 int isize = wPreferences.icon_size;
142 int done = 0;
143 WMBagIterator iter;
145 * Find out screen boundaries.
149 * Allows each head to have miniwindows
151 WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
153 sx1 = rect.pos.x;
154 sy1 = rect.pos.y;
155 sw = rect.size.width;
156 sh = rect.size.height;
157 sx2 = sx1 + sw;
158 sy2 = sy1 + sh;
160 if (scr->dock) {
161 if (scr->dock->on_right_side)
162 sx2 -= isize + DOCK_EXTRA_SPACE;
163 else
164 sx1 += isize + DOCK_EXTRA_SPACE;
167 sw = isize * (sw/isize);
168 sh = isize * (sh/isize);
169 fullW = (sx2-sx1)/isize;
170 fullH = (sy2-sy1)/isize;
172 /* icon yard boundaries */
173 if (wPreferences.icon_yard & IY_VERT) {
174 pf = fullH;
175 sf = fullW;
176 } else {
177 pf = fullW;
178 sf = fullH;
180 if (wPreferences.icon_yard & IY_RIGHT) {
181 xo = sx2 - isize;
182 xs = -1;
183 } else {
184 xo = sx1;
185 xs = 1;
187 if (wPreferences.icon_yard & IY_TOP) {
188 yo = sy1;
189 ys = 1;
190 } else {
191 yo = sy2 - isize;
192 ys = -1;
196 * Create a map with the occupied slots. 1 means the slot is used
197 * or at least partially used.
198 * The slot usage can be optimized by only marking fully used slots
199 * or slots that have most of it covered.
200 * Space usage is worse than the fvwm algorithm (used in the old version)
201 * but complexity is much better (faster) than it.
203 map = wmalloc((sw+2) * (sh+2));
204 memset(map, 0, (sw+2) * (sh+2));
206 #define INDEX(x,y) (((y)+1)*(sw+2) + (x) + 1)
208 WM_ETARETI_BAG(scr->stacking_list, obj, iter) {
210 while (obj) {
211 int x, y;
213 if (iconPosition(obj, sx1, sy1, sx2, sy2, scr->current_workspace,
214 &x, &y)) {
215 int xdi, ydi; /* rounded down */
216 int xui, yui; /* rounded up */
218 xdi = x/isize;
219 ydi = y/isize;
220 xui = (x+isize/2)/isize;
221 yui = (y+isize/2)/isize;
222 map[INDEX(xdi,ydi)] = 1;
223 map[INDEX(xdi,yui)] = 1;
224 map[INDEX(xui,ydi)] = 1;
225 map[INDEX(xui,yui)] = 1;
227 obj = obj->stacking->under;
231 * Default position
233 *x_ret = 0;
234 *y_ret = 0;
237 * Look for an empty slot
239 for (si=0; si<sf; si++) {
240 for (pi=0; pi<pf; pi++) {
241 if (wPreferences.icon_yard & IY_VERT) {
242 x = xo + xs*(si*isize);
243 y = yo + ys*(pi*isize);
244 } else {
245 x = xo + xs*(pi*isize);
246 y = yo + ys*(si*isize);
248 if (!map[INDEX(x/isize, y/isize)]) {
249 *x_ret = x;
250 *y_ret = y;
251 done = 1;
252 break;
255 if (done)
256 break;
259 wfree(map);
264 * This function calculates the length of the intersection of two
265 * line sections. (Hey, is that english?)
267 static int
268 calcIntersectionLength(int p1, int l1, int p2, int l2)
270 int isect;
271 int tmp;
273 if (p1 > p2) {
274 tmp = p1;
275 p1 = p2;
276 p2 = tmp;
277 tmp = l1;
278 l1 = l2;
279 l2 = tmp;
282 if (p1 + l1 < p2)
283 isect = 0;
284 else if (p2 + l2 < p1 + l1)
285 isect = l2;
286 else
287 isect = p1 + l1 - p2;
289 return isect;
294 * This function calculates the area of the intersection of two rectangles.
298 calcIntersectionArea(int x1, int y1, int w1, int h1,
299 int x2, int y2, int w2, int h2)
301 return calcIntersectionLength(x1, w1, x2, w2)
302 * calcIntersectionLength(y1, h1, y2, h2);
306 static int
307 calcSumOfCoveredAreas(WWindow *wwin, int x, int y, int w, int h)
309 int sum_isect = 0;
310 WWindow *test_window;
311 int tw,tx,ty,th;
313 test_window = wwin->screen_ptr->focused_window;
314 for(;test_window != NULL && test_window->prev != NULL;)
315 test_window = test_window->prev;
317 for(; test_window != NULL; test_window = test_window->next) {
318 if (test_window->frame->core->stacking->window_level
319 < WMNormalLevel) {
320 continue;
323 #if 0
324 tw = test_window->client.width;
325 if (test_window->flags.shaded)
326 th = test_window->frame->top_width;
327 else
328 th = test_window->client.height + extra_height;
329 #else
330 tw = test_window->frame->core->width;
331 th = test_window->frame->core->height;
332 #endif
333 tx = test_window->frame_x;
334 ty = test_window->frame_y;
336 if (test_window->flags.mapped ||
337 (test_window->flags.shaded &&
338 !(test_window->flags.miniaturized ||
339 test_window->flags.hidden))) {
340 sum_isect += calcIntersectionArea(tx, ty, tw, th, x, y, w, h);
344 return sum_isect;
348 static void
349 smartPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
350 unsigned int width, unsigned int height,
351 WArea usableArea)
353 WScreen *scr = wwin->screen_ptr;
354 int test_x = 0, test_y = Y_ORIGIN(scr);
355 int from_x, to_x, from_y, to_y;
356 int sx;
357 int min_isect, min_isect_x, min_isect_y;
358 int sum_isect;
359 int extra_height;
361 if (wwin->frame)
362 extra_height = wwin->frame->top_width + wwin->frame->bottom_width;
363 else
364 extra_height = 24; /* random value */
366 sx = X_ORIGIN(scr);
368 min_isect = INT_MAX;
369 min_isect_x = sx;
370 min_isect_y = test_y;
372 height += extra_height;
374 while (((test_y + height) < usableArea.y2)) {
376 test_x = sx;
378 while ((test_x + width) < usableArea.x2) {
380 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
381 width, height);
383 if ( sum_isect < min_isect ) {
384 min_isect = sum_isect;
385 min_isect_x = test_x;
386 min_isect_y = test_y;
389 test_x += PLACETEST_HSTEP;
391 test_y += PLACETEST_VSTEP;
394 from_x = min_isect_x - PLACETEST_HSTEP + 1;
395 from_x = WMAX(from_x, X_ORIGIN(scr));
396 to_x = min_isect_x + PLACETEST_HSTEP;
397 if (to_x + width > usableArea.x2)
398 to_x = usableArea.x2 - width;
400 from_y = min_isect_y - PLACETEST_VSTEP + 1;
401 from_y = WMAX(from_y, Y_ORIGIN(scr));
402 to_y = min_isect_y + PLACETEST_VSTEP;
403 if (to_y + height > usableArea.y2)
404 to_y = usableArea.y2 - height;
406 for (test_x = from_x; test_x < to_x; test_x++) {
407 for (test_y = from_y; test_y < to_y; test_y++) {
408 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
409 width, height);
411 if ( sum_isect < min_isect ) {
412 min_isect = sum_isect;
413 min_isect_x = test_x;
414 min_isect_y = test_y;
419 *x_ret = min_isect_x;
420 *y_ret = min_isect_y;
424 static Bool
425 autoPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
426 unsigned int width, unsigned int height, int tryCount,
427 WArea usableArea)
429 WScreen *scr = wwin->screen_ptr;
430 int test_x = 0, test_y = Y_ORIGIN(scr);
431 int loc_ok = False, tw,tx,ty,th;
432 int swidth, sx;
433 WWindow *test_window;
434 int extra_height;
437 if (wwin->frame)
438 extra_height = wwin->frame->top_width + wwin->frame->bottom_width + 2;
439 else
440 extra_height = 24; /* random value */
442 swidth = usableArea.x2-usableArea.x1;
443 sx = X_ORIGIN(scr);
445 /* this was based on fvwm2's smart placement */
447 height += extra_height;
449 while (((test_y + height) < (usableArea.y2 - usableArea.y1)) && !loc_ok) {
450 test_x = sx;
452 while (((test_x + width) < swidth) && (!loc_ok)) {
454 loc_ok = True;
455 test_window = scr->focused_window;
457 while ((test_window != NULL) && (loc_ok == True)) {
459 if (test_window->frame->core->stacking->window_level
460 < WMNormalLevel && tryCount > 0) {
461 test_window = test_window->next;
462 continue;
464 #if 0
465 tw = test_window->client.width;
466 if (test_window->flags.shaded)
467 th = test_window->frame->top_width;
468 else
469 th = test_window->client.height + extra_height;
470 #else
471 tw = test_window->frame->core->width;
472 th = test_window->frame->core->height;
473 #endif
474 tx = test_window->frame_x;
475 ty = test_window->frame_y;
477 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
478 (ty < (test_y + height)) && ((ty + th) > test_y) &&
479 (test_window->flags.mapped ||
480 (test_window->flags.shaded &&
481 test_window->frame->workspace==scr->current_workspace &&
482 !(test_window->flags.miniaturized ||
483 test_window->flags.hidden)))) {
485 loc_ok = False;
487 test_window = test_window->next;
490 test_window = scr->focused_window;
492 while ((test_window != NULL) && (loc_ok == True)) {
494 if (test_window->frame->core->stacking->window_level
495 < WMNormalLevel && tryCount > 0) {
496 test_window = test_window->prev;
497 continue;
499 #if 0
500 tw = test_window->client.width;
501 if (test_window->flags.shaded)
502 th = test_window->frame->top_width;
503 else
504 th = test_window->client.height + extra_height;
505 #else
506 tw = test_window->frame->core->width;
507 th = test_window->frame->core->height;
508 #endif
509 tx = test_window->frame_x;
510 ty = test_window->frame_y;
512 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
513 (ty < (test_y + height)) && ((ty + th) > test_y) &&
514 (test_window->flags.mapped ||
515 (test_window->flags.shaded &&
516 test_window->frame->workspace==scr->current_workspace &&
517 !(test_window->flags.miniaturized ||
518 test_window->flags.hidden)))) {
520 loc_ok = False;
522 test_window = test_window->prev;
524 if (loc_ok == True) {
525 *x_ret = test_x;
526 *y_ret = test_y;
527 break;
529 test_x += PLACETEST_HSTEP;
531 test_y += PLACETEST_VSTEP;
534 return loc_ok;
538 static void
539 cascadeWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
540 unsigned int width, unsigned int height, int h,
541 WArea usableArea)
543 unsigned int extra_height;
546 if (wwin->frame)
547 extra_height = wwin->frame->top_width + wwin->frame->bottom_width;
548 else
549 extra_height = 24; /* random value */
551 *x_ret = h * scr->cascade_index + X_ORIGIN(scr);
552 *y_ret = h * scr->cascade_index + Y_ORIGIN(scr);
553 height += extra_height;
555 if (width + *x_ret > usableArea.x2 || height + *y_ret > usableArea.y2) {
556 scr->cascade_index = 0;
557 *x_ret = X_ORIGIN(scr);
558 *y_ret = Y_ORIGIN(scr);
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, extra_height;
570 if (wwin->frame)
571 extra_height = wwin->frame->top_width + wwin->frame->bottom_width + 2;
572 else
573 extra_height = 24; /* random value */
575 w = ((usableArea.x2-X_ORIGIN(scr)) - width);
576 h = ((usableArea.y2-Y_ORIGIN(scr)) - height - extra_height);
577 if (w<1) w = 1;
578 if (h<1) h = 1;
579 *x_ret = X_ORIGIN(scr) + rand()%w;
580 *y_ret = Y_ORIGIN(scr) + rand()%h;
585 void
586 PlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
587 unsigned width, unsigned height)
589 WScreen *scr = wwin->screen_ptr;
590 int h = WMFontHeight(scr->title_font) + (wPreferences.window_title_clearance + TITLEBAR_EXTEND_SPACE) * 2;
591 WArea usableArea = wGetUsableAreaForHead(scr,
592 wGetHeadForPointerLocation(scr),
593 NULL);
595 switch (wPreferences.window_placement) {
596 case WPM_MANUAL:
597 InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
598 break;
600 case WPM_SMART:
601 smartPlaceWindow(wwin, x_ret, y_ret, width, height, usableArea);
602 break;
604 case WPM_AUTO:
605 if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 0,
606 usableArea)) {
607 break;
608 } else if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 1,
609 usableArea)) {
610 break;
612 /* there isn't a break here, because if we fail, it should fall
613 through to cascade placement, as people who want tiling want
614 automagicness aren't going to want to place their window */
616 case WPM_CASCADE:
617 if (wPreferences.window_placement == WPM_AUTO)
618 scr->cascade_index++;
620 cascadeWindow(scr, wwin, x_ret, y_ret, width, height, h, usableArea);
622 if (wPreferences.window_placement == WPM_CASCADE)
623 scr->cascade_index++;
624 break;
626 case WPM_RANDOM:
627 randomPlaceWindow(scr, wwin, x_ret, y_ret, width, height, usableArea);
628 break;
630 #ifdef DEBUG
631 default:
632 puts("Invalid window placement!!!");
633 *x_ret = 0;
634 *y_ret = 0;
635 #endif
639 * clip to usableArea instead of full screen
640 * this will also take dock/clip etc.. into account
641 * aswell as being xinerama friendly
643 if (*x_ret + width > usableArea.x2)
644 *x_ret = usableArea.x2 - width;
645 if (*x_ret < usableArea.x1)
646 *x_ret = usableArea.x1;
648 if (*y_ret + height > usableArea.y2)
649 *y_ret = usableArea.y2 - height;
650 if (*y_ret < usableArea.y1)
651 *y_ret = usableArea.y1;