fixed layout
[wmaker-crm.git] / src / placement.c
blob83da4e2147ec5274bba2e908df666ed2c26d3123
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(scr) WMAX(usableArea.x1,\
49 wPreferences.window_place_origin.x)
51 #define Y_ORIGIN(scr) 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 //WScreen *scr = wwin->screen_ptr;
352 int test_x = 0, test_y = Y_ORIGIN(scr);
353 int from_x, to_x, from_y, to_y;
354 int sx;
355 int min_isect, min_isect_x, min_isect_y;
356 int sum_isect;
358 if (wwin->frame) {
359 height += wwin->frame->top_width + wwin->frame->bottom_width;
360 } else {
361 if (HAS_TITLEBAR(wwin)) height += 18;
362 if (HAS_RESIZEBAR(wwin)) height += 8;
364 if (HAS_BORDER(wwin)) {
365 height += 2;
366 width += 2;
368 sx = X_ORIGIN(scr);
370 min_isect = INT_MAX;
371 min_isect_x = sx;
372 min_isect_y = test_y;
374 while (((test_y + height) < usableArea.y2)) {
375 test_x = sx;
376 while ((test_x + width) < usableArea.x2) {
377 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
378 width, height);
380 if (sum_isect < min_isect) {
381 min_isect = sum_isect;
382 min_isect_x = test_x;
383 min_isect_y = test_y;
386 test_x += PLACETEST_HSTEP;
388 test_y += PLACETEST_VSTEP;
391 from_x = min_isect_x - PLACETEST_HSTEP + 1;
392 from_x = WMAX(from_x, X_ORIGIN(scr));
393 to_x = min_isect_x + PLACETEST_HSTEP;
394 if (to_x + width > usableArea.x2)
395 to_x = usableArea.x2 - width;
397 from_y = min_isect_y - PLACETEST_VSTEP + 1;
398 from_y = WMAX(from_y, Y_ORIGIN(scr));
399 to_y = min_isect_y + PLACETEST_VSTEP;
400 if (to_y + height > usableArea.y2)
401 to_y = usableArea.y2 - height;
403 for (test_x = from_x; test_x < to_x; test_x++) {
404 for (test_y = from_y; test_y < to_y; test_y++) {
405 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
406 width, height);
408 if (sum_isect < min_isect) {
409 min_isect = sum_isect;
410 min_isect_x = test_x;
411 min_isect_y = test_y;
416 *x_ret = min_isect_x;
417 *y_ret = min_isect_y;
421 static Bool
422 autoPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
423 unsigned int width, unsigned int height, int tryCount,
424 WArea usableArea)
426 WScreen *scr = wwin->screen_ptr;
427 int test_x = 0, test_y = Y_ORIGIN(scr);
428 int loc_ok = False, tw,tx,ty,th;
429 int swidth, sx;
430 WWindow *test_window;
432 if (wwin->frame) {
433 height += wwin->frame->top_width + wwin->frame->bottom_width;
434 } else {
435 if (HAS_TITLEBAR(wwin)) height += 18;
436 if (HAS_RESIZEBAR(wwin)) height += 8;
438 if (HAS_BORDER(wwin)) {
439 height += 2;
440 width += 2;
443 swidth = usableArea.x2-usableArea.x1;
444 sx = X_ORIGIN(scr);
446 /* this was based on fvwm2's smart placement */
448 while (((test_y + height) < (usableArea.y2 - usableArea.y1)) && !loc_ok) {
449 test_x = sx;
451 while (((test_x + width) < swidth) && (!loc_ok)) {
453 loc_ok = True;
454 test_window = scr->focused_window;
456 while ((test_window != NULL) && (loc_ok == True)) {
458 if (test_window->frame->core->stacking->window_level
459 < WMNormalLevel && tryCount > 0) {
460 test_window = test_window->next;
461 continue;
463 #if 0
464 tw = test_window->client.width;
465 if (test_window->flags.shaded)
466 th = test_window->frame->top_width;
467 else
468 th = test_window->client.height + extra_height;
469 #else
470 tw = test_window->frame->core->width;
471 th = test_window->frame->core->height;
472 #endif
473 tx = test_window->frame_x;
474 ty = test_window->frame_y;
476 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
477 (ty < (test_y + height)) && ((ty + th) > test_y) &&
478 (test_window->flags.mapped ||
479 (test_window->flags.shaded &&
480 test_window->frame->workspace==scr->current_workspace &&
481 !(test_window->flags.miniaturized ||
482 test_window->flags.hidden)))) {
484 loc_ok = False;
486 test_window = test_window->next;
489 test_window = scr->focused_window;
491 while ((test_window != NULL) && (loc_ok == True)) {
493 if (test_window->frame->core->stacking->window_level
494 < WMNormalLevel && tryCount > 0) {
495 test_window = test_window->prev;
496 continue;
498 #if 0
499 tw = test_window->client.width;
500 if (test_window->flags.shaded)
501 th = test_window->frame->top_width;
502 else
503 th = test_window->client.height + extra_height;
504 #else
505 tw = test_window->frame->core->width;
506 th = test_window->frame->core->height;
507 #endif
508 tx = test_window->frame_x;
509 ty = test_window->frame_y;
511 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
512 (ty < (test_y + height)) && ((ty + th) > test_y) &&
513 (test_window->flags.mapped ||
514 (test_window->flags.shaded &&
515 test_window->frame->workspace==scr->current_workspace &&
516 !(test_window->flags.miniaturized ||
517 test_window->flags.hidden)))) {
519 loc_ok = False;
521 test_window = test_window->prev;
523 if (loc_ok == True) {
524 *x_ret = test_x;
525 *y_ret = test_y;
526 break;
528 test_x += PLACETEST_HSTEP;
530 test_y += PLACETEST_VSTEP;
533 return loc_ok;
537 static void
538 cascadeWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
539 unsigned int width, unsigned int height, int h,
540 WArea usableArea)
542 if (wwin->frame) {
543 height += wwin->frame->top_width + wwin->frame->bottom_width;
544 } else {
545 if (HAS_TITLEBAR(wwin)) height += 18;
546 if (HAS_RESIZEBAR(wwin)) height += 8;
548 if (HAS_BORDER(wwin)) {
549 height += 2;
550 width += 2;
553 *x_ret = h * scr->cascade_index + X_ORIGIN(scr);
554 *y_ret = h * scr->cascade_index + Y_ORIGIN(scr);
556 if (width + *x_ret > usableArea.x2 || height + *y_ret > usableArea.y2) {
557 scr->cascade_index = 0;
558 *x_ret = X_ORIGIN(scr);
559 *y_ret = Y_ORIGIN(scr);
564 static void
565 randomPlaceWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
566 unsigned int width, unsigned int height,
567 WArea usableArea)
569 int w, h;
571 if (wwin->frame) {
572 height += wwin->frame->top_width + wwin->frame->bottom_width;
573 } else {
574 if (HAS_TITLEBAR(wwin)) height += 18;
575 if (HAS_RESIZEBAR(wwin)) height += 8;
577 if (HAS_BORDER(wwin)) {
578 height += 2;
579 width += 2;
582 w = ((usableArea.x2-X_ORIGIN(scr)) - width);
583 h = ((usableArea.y2-Y_ORIGIN(scr)) - height);
584 if (w<1) w = 1;
585 if (h<1) h = 1;
586 *x_ret = X_ORIGIN(scr) + rand()%w;
587 *y_ret = Y_ORIGIN(scr) + rand()%h;
592 void
593 PlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
594 unsigned width, unsigned height)
596 WScreen *scr = wwin->screen_ptr;
597 int h = WMFontHeight(scr->title_font) + (wPreferences.window_title_clearance + TITLEBAR_EXTEND_SPACE) * 2;
598 WArea usableArea = wGetUsableAreaForHead(scr,
599 wGetHeadForPointerLocation(scr),
600 NULL, True);
602 switch (wPreferences.window_placement) {
603 case WPM_MANUAL:
604 InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
605 break;
607 case WPM_SMART:
608 smartPlaceWindow(wwin, x_ret, y_ret, width, height, usableArea);
609 break;
611 case WPM_AUTO:
612 if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 0,
613 usableArea)) {
614 break;
615 } else if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 1,
616 usableArea)) {
617 break;
619 /* there isn't a break here, because if we fail, it should fall
620 through to cascade placement, as people who want tiling want
621 automagicness aren't going to want to place their window */
623 case WPM_CASCADE:
624 if (wPreferences.window_placement == WPM_AUTO)
625 scr->cascade_index++;
627 cascadeWindow(scr, wwin, x_ret, y_ret, width, height, h, usableArea);
629 if (wPreferences.window_placement == WPM_CASCADE)
630 scr->cascade_index++;
631 break;
633 case WPM_RANDOM:
634 randomPlaceWindow(scr, wwin, x_ret, y_ret, width, height, usableArea);
635 break;
637 #ifdef DEBUG
638 default:
639 puts("Invalid window placement!!!");
640 *x_ret = 0;
641 *y_ret = 0;
642 #endif
646 * clip to usableArea instead of full screen
647 * this will also take dock/clip etc.. into account
648 * aswell as being xinerama friendly
650 if (*x_ret + width > usableArea.x2)
651 *x_ret = usableArea.x2 - width;
652 if (*x_ret < usableArea.x1)
653 *x_ret = usableArea.x1;
655 if (*y_ret + height > usableArea.y2)
656 *y_ret = usableArea.y2 - height;
657 if (*y_ret < usableArea.y1)
658 *y_ret = usableArea.y1;