another test for cvs notifications
[wmaker-crm.git] / src / placement.c
blob55dd9df78b7714d6f85a23fcb8dde8f3b4fdf849
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(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
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, int head)
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;
144 WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
146 /* Find out screen boundaries. */
148 /* Allows each head to have miniwindows */
150 sx1 = area.x1;
151 sy1 = area.y1;
152 sx2 = area.x2;
153 sy2 = area.y2;
154 sw = sx2-sx1;
155 sh = sy2-sy1;
157 #if 0
158 if (scr->dock) {
159 if (scr->dock->on_right_side)
160 sx2 -= isize + DOCK_EXTRA_SPACE;
161 else
162 sx1 += isize + DOCK_EXTRA_SPACE;
164 #endif
166 sw = isize * (sw/isize);
167 sh = isize * (sh/isize);
168 fullW = (sx2-sx1)/isize;
169 fullH = (sy2-sy1)/isize;
171 /* icon yard boundaries */
172 if (wPreferences.icon_yard & IY_VERT) {
173 pf = fullH;
174 sf = fullW;
175 } else {
176 pf = fullW;
177 sf = fullH;
179 if (wPreferences.icon_yard & IY_RIGHT) {
180 xo = sx2 - isize;
181 xs = -1;
182 } else {
183 xo = sx1;
184 xs = 1;
186 if (wPreferences.icon_yard & IY_TOP) {
187 yo = sy1;
188 ys = 1;
189 } else {
190 yo = sy2 - isize;
191 ys = -1;
195 * Create a map with the occupied slots. 1 means the slot is used
196 * or at least partially used.
197 * The slot usage can be optimized by only marking fully used slots
198 * or slots that have most of it covered.
199 * Space usage is worse than the fvwm algorithm (used in the old version)
200 * but complexity is much better (faster) than it.
202 map = wmalloc((sw+2) * (sh+2));
203 memset(map, 0, (sw+2) * (sh+2));
205 #define INDEX(x,y) (((y)+1)*(sw+2) + (x) + 1)
207 WM_ETARETI_BAG(scr->stacking_list, obj, iter) {
209 while (obj) {
210 int x, y;
212 if (iconPosition(obj, sx1, sy1, sx2, sy2, scr->current_workspace,
213 &x, &y)) {
214 int xdi, ydi; /* rounded down */
215 int xui, yui; /* rounded up */
217 xdi = x/isize;
218 ydi = y/isize;
219 xui = (x+isize/2)/isize;
220 yui = (y+isize/2)/isize;
221 map[INDEX(xdi,ydi)] = 1;
222 map[INDEX(xdi,yui)] = 1;
223 map[INDEX(xui,ydi)] = 1;
224 map[INDEX(xui,yui)] = 1;
226 obj = obj->stacking->under;
230 * Default position
232 *x_ret = 0;
233 *y_ret = 0;
236 * Look for an empty slot
238 for (si=0; si<sf; si++) {
239 for (pi=0; pi<pf; pi++) {
240 if (wPreferences.icon_yard & IY_VERT) {
241 x = xo + xs*(si*isize);
242 y = yo + ys*(pi*isize);
243 } else {
244 x = xo + xs*(pi*isize);
245 y = yo + ys*(si*isize);
247 if (!map[INDEX(x/isize, y/isize)]) {
248 *x_ret = x;
249 *y_ret = y;
250 done = 1;
251 break;
254 if (done)
255 break;
258 wfree(map);
263 * This function calculates the length of the intersection of two
264 * line sections. (Hey, is that english?)
266 static int
267 calcIntersectionLength(int p1, int l1, int p2, int l2)
269 int isect;
270 int tmp;
272 if (p1 > p2) {
273 tmp = p1;
274 p1 = p2;
275 p2 = tmp;
276 tmp = l1;
277 l1 = l2;
278 l2 = tmp;
281 if (p1 + l1 < p2)
282 isect = 0;
283 else if (p2 + l2 < p1 + l1)
284 isect = l2;
285 else
286 isect = p1 + l1 - p2;
288 return isect;
293 * This function calculates the area of the intersection of two rectangles.
297 calcIntersectionArea(int x1, int y1, int w1, int h1,
298 int x2, int y2, int w2, int h2)
300 return calcIntersectionLength(x1, w1, x2, w2)
301 * calcIntersectionLength(y1, h1, y2, h2);
305 static int
306 calcSumOfCoveredAreas(WWindow *wwin, int x, int y, int w, int h)
308 int sum_isect = 0;
309 WWindow *test_window;
310 int tw,tx,ty,th;
312 test_window = wwin->screen_ptr->focused_window;
313 for(;test_window != NULL && test_window->prev != NULL;)
314 test_window = test_window->prev;
316 for(; test_window != NULL; test_window = test_window->next) {
317 if (test_window->frame->core->stacking->window_level
318 < WMNormalLevel) {
319 continue;
322 #if 0
323 tw = test_window->client.width;
324 if (test_window->flags.shaded)
325 th = test_window->frame->top_width;
326 else
327 th = test_window->client.height + extra_height;
328 #else
329 tw = test_window->frame->core->width;
330 th = test_window->frame->core->height;
331 #endif
332 tx = test_window->frame_x;
333 ty = test_window->frame_y;
335 if (test_window->flags.mapped ||
336 (test_window->flags.shaded &&
337 !(test_window->flags.miniaturized ||
338 test_window->flags.hidden))) {
339 sum_isect += calcIntersectionArea(tx, ty, tw, th, x, y, w, h);
343 return sum_isect;
347 static void
348 smartPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
349 unsigned int width, unsigned int height,
350 WArea usableArea)
352 //WScreen *scr = wwin->screen_ptr;
353 int test_x = 0, test_y = Y_ORIGIN(scr);
354 int from_x, to_x, from_y, to_y;
355 int sx;
356 int min_isect, min_isect_x, min_isect_y;
357 int sum_isect;
358 int extra_height;
360 if (wwin->frame)
361 extra_height = wwin->frame->top_width + wwin->frame->bottom_width;
362 else
363 extra_height = 24; /* random value */
365 sx = X_ORIGIN(scr);
367 min_isect = INT_MAX;
368 min_isect_x = sx;
369 min_isect_y = test_y;
371 height += extra_height;
373 while (((test_y + height) < usableArea.y2)) {
375 test_x = sx;
377 while ((test_x + width) < usableArea.x2) {
379 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
380 width, height);
382 if (sum_isect < min_isect) {
383 min_isect = sum_isect;
384 min_isect_x = test_x;
385 min_isect_y = test_y;
388 test_x += PLACETEST_HSTEP;
390 test_y += PLACETEST_VSTEP;
393 from_x = min_isect_x - PLACETEST_HSTEP + 1;
394 from_x = WMAX(from_x, X_ORIGIN(scr));
395 to_x = min_isect_x + PLACETEST_HSTEP;
396 if (to_x + width > usableArea.x2)
397 to_x = usableArea.x2 - width;
399 from_y = min_isect_y - PLACETEST_VSTEP + 1;
400 from_y = WMAX(from_y, Y_ORIGIN(scr));
401 to_y = min_isect_y + PLACETEST_VSTEP;
402 if (to_y + height > usableArea.y2)
403 to_y = usableArea.y2 - height;
405 for (test_x = from_x; test_x < to_x; test_x++) {
406 for (test_y = from_y; test_y < to_y; test_y++) {
407 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
408 width, height);
410 if (sum_isect < min_isect) {
411 min_isect = sum_isect;
412 min_isect_x = test_x;
413 min_isect_y = test_y;
418 *x_ret = min_isect_x;
419 *y_ret = min_isect_y;
423 static Bool
424 autoPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
425 unsigned int width, unsigned int height, int tryCount,
426 WArea usableArea)
428 WScreen *scr = wwin->screen_ptr;
429 int test_x = 0, test_y = Y_ORIGIN(scr);
430 int loc_ok = False, tw,tx,ty,th;
431 int swidth, sx;
432 WWindow *test_window;
433 int extra_height;
436 if (wwin->frame)
437 extra_height = wwin->frame->top_width + wwin->frame->bottom_width + 2;
438 else
439 extra_height = 24; /* random value */
441 swidth = usableArea.x2-usableArea.x1;
442 sx = X_ORIGIN(scr);
444 /* this was based on fvwm2's smart placement */
446 height += extra_height;
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 unsigned int extra_height;
545 if (wwin->frame)
546 extra_height = wwin->frame->top_width + wwin->frame->bottom_width;
547 else
548 extra_height = 24; /* random value */
550 *x_ret = h * scr->cascade_index + X_ORIGIN(scr);
551 *y_ret = h * scr->cascade_index + Y_ORIGIN(scr);
552 height += extra_height;
554 if (width + *x_ret > usableArea.x2 || height + *y_ret > usableArea.y2) {
555 scr->cascade_index = 0;
556 *x_ret = X_ORIGIN(scr);
557 *y_ret = Y_ORIGIN(scr);
562 static void
563 randomPlaceWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
564 unsigned int width, unsigned int height,
565 WArea usableArea)
567 int w, h, extra_height;
569 if (wwin->frame)
570 extra_height = wwin->frame->top_width + wwin->frame->bottom_width + 2;
571 else
572 extra_height = 24; /* random value */
574 w = ((usableArea.x2-X_ORIGIN(scr)) - width);
575 h = ((usableArea.y2-Y_ORIGIN(scr)) - height - extra_height);
576 if (w<1) w = 1;
577 if (h<1) h = 1;
578 *x_ret = X_ORIGIN(scr) + rand()%w;
579 *y_ret = Y_ORIGIN(scr) + rand()%h;
584 void
585 PlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
586 unsigned width, unsigned height)
588 WScreen *scr = wwin->screen_ptr;
589 int h = WMFontHeight(scr->title_font) + (wPreferences.window_title_clearance + TITLEBAR_EXTEND_SPACE) * 2;
590 WArea usableArea = wGetUsableAreaForHead(scr,
591 wGetHeadForPointerLocation(scr),
592 NULL, True);
594 switch (wPreferences.window_placement) {
595 case WPM_MANUAL:
596 InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
597 break;
599 case WPM_SMART:
600 smartPlaceWindow(wwin, x_ret, y_ret, width, height, usableArea);
601 break;
603 case WPM_AUTO:
604 if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 0,
605 usableArea)) {
606 break;
607 } else if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 1,
608 usableArea)) {
609 break;
611 /* there isn't a break here, because if we fail, it should fall
612 through to cascade placement, as people who want tiling want
613 automagicness aren't going to want to place their window */
615 case WPM_CASCADE:
616 if (wPreferences.window_placement == WPM_AUTO)
617 scr->cascade_index++;
619 cascadeWindow(scr, wwin, x_ret, y_ret, width, height, h, usableArea);
621 if (wPreferences.window_placement == WPM_CASCADE)
622 scr->cascade_index++;
623 break;
625 case WPM_RANDOM:
626 randomPlaceWindow(scr, wwin, x_ret, y_ret, width, height, usableArea);
627 break;
629 #ifdef DEBUG
630 default:
631 puts("Invalid window placement!!!");
632 *x_ret = 0;
633 *y_ret = 0;
634 #endif
638 * clip to usableArea instead of full screen
639 * this will also take dock/clip etc.. into account
640 * aswell as being xinerama friendly
642 if (*x_ret + width > usableArea.x2)
643 *x_ret = usableArea.x2 - width;
644 if (*x_ret < usableArea.x1)
645 *x_ret = usableArea.x1;
647 if (*y_ret + height > usableArea.y2)
648 *y_ret = usableArea.y2 - height;
649 if (*y_ret < usableArea.y1)
650 *y_ret = usableArea.y1;