updated code to use new bags
[wmaker-crm.git] / src / placement.c
blobcf3d980d27edc068f9b13dcaaa02a021fb50e58d
1 /* placement.c - window and icon placement on screen
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1997, 1998 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"
44 extern WPreferences wPreferences;
47 #define X_ORIGIN(scr) WMAX((scr)->totalUsableArea.x1,\
48 wPreferences.window_place_origin.x)
50 #define Y_ORIGIN(scr) WMAX((scr)->totalUsableArea.y1,\
51 wPreferences.window_place_origin.y)
55 * interactive window placement is in moveres.c
58 extern void
59 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)
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;
145 * Find out screen boundaries.
147 sx1 = 0;
148 sy1 = 0;
149 sx2 = scr->scr_width;
150 sy2 = scr->scr_height;
151 if (scr->dock) {
152 if (scr->dock->on_right_side)
153 sx2 -= isize + DOCK_EXTRA_SPACE;
154 else
155 sx1 += isize + DOCK_EXTRA_SPACE;
158 sw = isize * (scr->scr_width/isize);
159 sh = isize * (scr->scr_height/isize);
160 fullW = (sx2-sx1)/isize;
161 fullH = (sy2-sy1)/isize;
163 /* icon yard boundaries */
164 if (wPreferences.icon_yard & IY_VERT) {
165 pf = fullH;
166 sf = fullW;
167 } else {
168 pf = fullW;
169 sf = fullH;
171 if (wPreferences.icon_yard & IY_RIGHT) {
172 xo = sx2 - isize;
173 xs = -1;
174 } else {
175 xo = sx1;
176 xs = 1;
178 if (wPreferences.icon_yard & IY_TOP) {
179 yo = sy1;
180 ys = 1;
181 } else {
182 yo = sy2 - isize;
183 ys = -1;
187 * Create a map with the occupied slots. 1 means the slot is used
188 * or at least partially used.
189 * The slot usage can be optimized by only marking fully used slots
190 * or slots that have most of it covered.
191 * Space usage is worse than the fvwm algorithm (used in the old version)
192 * but complexity is much better (faster) than it.
194 map = wmalloc((sw+2) * (sh+2));
195 memset(map, 0, (sw+2) * (sh+2));
197 #define INDEX(x,y) (((y)+1)*(sw+2) + (x) + 1)
199 for (obj = WMBagLast(scr->stacking_list, &iter);
200 obj != NULL;
201 obj = WMBagNext(scr->stacking_list, &iter)) {
203 while (obj) {
204 int x, y;
206 if (iconPosition(obj, sx1, sy1, sx2, sy2, scr->current_workspace,
207 &x, &y)) {
208 int xdi, ydi; /* rounded down */
209 int xui, yui; /* rounded up */
211 xdi = x/isize;
212 ydi = y/isize;
213 xui = (x+isize/2)/isize;
214 yui = (y+isize/2)/isize;
215 map[INDEX(xdi,ydi)] = 1;
216 map[INDEX(xdi,yui)] = 1;
217 map[INDEX(xui,ydi)] = 1;
218 map[INDEX(xui,yui)] = 1;
220 obj = obj->stacking->under;
224 * Default position
226 *x_ret = 0;
227 *y_ret = 0;
230 * Look for an empty slot
232 for (si=0; si<sf; si++) {
233 for (pi=0; pi<pf; pi++) {
234 if (wPreferences.icon_yard & IY_VERT) {
235 x = xo + xs*(si*isize);
236 y = yo + ys*(pi*isize);
237 } else {
238 x = xo + xs*(pi*isize);
239 y = yo + ys*(si*isize);
241 if (!map[INDEX(x/isize, y/isize)]) {
242 *x_ret = x;
243 *y_ret = y;
244 done = 1;
245 break;
248 if (done)
249 break;
252 free(map);
257 * This function calculates the length of the intersection of two
258 * line sections. (Hey, is that english?)
260 static int
261 calcIntersectionLength(int p1, int l1, int p2, int l2)
263 int isect;
264 int tmp;
266 if (p1 > p2) {
267 tmp = p1;
268 p1 = p2;
269 p2 = tmp;
270 tmp = l1;
271 l1 = l2;
272 l2 = tmp;
275 if (p1 + l1 < p2)
276 isect = 0;
277 else if (p2 + l2 < p1 + l1)
278 isect = l2;
279 else
280 isect = p1 + l1 - p2;
282 return isect;
287 * This function calculates the area of the intersection of two rectangles.
289 static int
290 calcIntersectionArea(int x1, int y1, int w1, int h1,
291 int x2, int y2, int w2, int h2)
293 return calcIntersectionLength(x1, w1, x2, w2)
294 * calcIntersectionLength(y1, h1, y2, h2);
298 static int
299 calcSumOfCoveredAreas(WWindow *wwin, int x, int y, int w, int h)
301 int sum_isect = 0;
302 WWindow *test_window;
303 int tw,tx,ty,th;
305 test_window = wwin->screen_ptr->focused_window;
306 for(;test_window != NULL && test_window->prev != NULL;)
307 test_window = test_window->prev;
309 for(; test_window != NULL; test_window = test_window->next) {
310 if (test_window->frame->core->stacking->window_level
311 < WMNormalLevel) {
312 continue;
315 #if 0
316 tw = test_window->client.width;
317 if (test_window->flags.shaded)
318 th = test_window->frame->top_width;
319 else
320 th = test_window->client.height + extra_height;
321 #else
322 tw = test_window->frame->core->width;
323 th = test_window->frame->core->height;
324 #endif
325 tx = test_window->frame_x;
326 ty = test_window->frame_y;
328 if (test_window->flags.mapped ||
329 (test_window->flags.shaded &&
330 !(test_window->flags.miniaturized ||
331 test_window->flags.hidden))) {
332 sum_isect += calcIntersectionArea(tx, ty, tw, th, x, y, w, h);
336 return sum_isect;
340 static void
341 smartPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
342 unsigned int width, unsigned int height)
344 WScreen *scr = wwin->screen_ptr;
345 int test_x = 0, test_y = Y_ORIGIN(scr);
346 int from_x, to_x, from_y, to_y;
347 int sx;
348 int min_isect, min_isect_x, min_isect_y;
349 int sum_isect;
350 int extra_height;
351 WArea usableArea = scr->totalUsableArea;
353 if (wwin->frame)
354 extra_height = wwin->frame->top_width + wwin->frame->bottom_width;
355 else
356 extra_height = 24; /* random value */
358 sx = X_ORIGIN(scr);
360 min_isect = INT_MAX;
361 min_isect_x = sx;
362 min_isect_y = test_y;
364 height += extra_height;
366 while (((test_y + height) < usableArea.y2)) {
368 test_x = sx;
370 while ((test_x + width) < usableArea.x2) {
372 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
373 width, height);
375 if ( sum_isect < min_isect ) {
376 min_isect = sum_isect;
377 min_isect_x = test_x;
378 min_isect_y = test_y;
381 test_x += PLACETEST_HSTEP;
383 test_y += PLACETEST_VSTEP;
386 from_x = min_isect_x - PLACETEST_HSTEP + 1;
387 from_x = WMAX(from_x, X_ORIGIN(scr));
388 to_x = min_isect_x + PLACETEST_HSTEP;
389 if (to_x + width > usableArea.x2)
390 to_x = usableArea.x2 - width;
392 from_y = min_isect_y - PLACETEST_VSTEP + 1;
393 from_y = WMAX(from_y, Y_ORIGIN(scr));
394 to_y = min_isect_y + PLACETEST_VSTEP;
395 if (to_y + height > usableArea.y2)
396 to_y = usableArea.y2 - height;
398 for (test_x = from_x; test_x < to_x; test_x++) {
399 for (test_y = from_y; test_y < to_y; test_y++) {
400 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
401 width, height);
403 if ( sum_isect < min_isect ) {
404 min_isect = sum_isect;
405 min_isect_x = test_x;
406 min_isect_y = test_y;
411 *x_ret = min_isect_x;
412 *y_ret = min_isect_y;
416 static Bool
417 autoPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
418 unsigned int width, unsigned int height, int tryCount)
420 WScreen *scr = wwin->screen_ptr;
421 int test_x = 0, test_y = Y_ORIGIN(scr);
422 int loc_ok = False, tw,tx,ty,th;
423 int swidth, sx;
424 WWindow *test_window;
425 int extra_height;
426 WArea usableArea = scr->totalUsableArea;
428 if (wwin->frame)
429 extra_height = wwin->frame->top_width + wwin->frame->bottom_width + 2;
430 else
431 extra_height = 24; /* random value */
433 swidth = usableArea.x2-usableArea.x1;
434 sx = X_ORIGIN(scr);
436 /* this was based on fvwm2's smart placement */
438 height += extra_height;
440 while (((test_y + height) < (scr->scr_height)) && (!loc_ok)) {
442 test_x = sx;
444 while (((test_x + width) < swidth) && (!loc_ok)) {
446 loc_ok = True;
447 test_window = scr->focused_window;
449 while ((test_window != NULL) && (loc_ok == True)) {
451 if (test_window->frame->core->stacking->window_level
452 < WMNormalLevel && tryCount > 0) {
453 test_window = test_window->next;
454 continue;
456 #if 0
457 tw = test_window->client.width;
458 if (test_window->flags.shaded)
459 th = test_window->frame->top_width;
460 else
461 th = test_window->client.height + extra_height;
462 #else
463 tw = test_window->frame->core->width;
464 th = test_window->frame->core->height;
465 #endif
466 tx = test_window->frame_x;
467 ty = test_window->frame_y;
469 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
470 (ty < (test_y + height)) && ((ty + th) > test_y) &&
471 (test_window->flags.mapped ||
472 (test_window->flags.shaded &&
473 !(test_window->flags.miniaturized ||
474 test_window->flags.hidden)))) {
476 loc_ok = False;
478 test_window = test_window->next;
481 test_window = scr->focused_window;
483 while ((test_window != NULL) && (loc_ok == True)) {
485 if (test_window->frame->core->stacking->window_level
486 < WMNormalLevel && tryCount > 0) {
487 test_window = test_window->prev;
488 continue;
490 #if 0
491 tw = test_window->client.width;
492 if (test_window->flags.shaded)
493 th = test_window->frame->top_width;
494 else
495 th = test_window->client.height + extra_height;
496 #else
497 tw = test_window->frame->core->width;
498 th = test_window->frame->core->height;
499 #endif
500 tx = test_window->frame_x;
501 ty = test_window->frame_y;
503 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
504 (ty < (test_y + height)) && ((ty + th) > test_y) &&
505 (test_window->flags.mapped ||
506 (test_window->flags.shaded &&
507 !(test_window->flags.miniaturized ||
508 test_window->flags.hidden)))) {
510 loc_ok = False;
512 test_window = test_window->prev;
514 if (loc_ok == True) {
515 *x_ret = test_x;
516 *y_ret = test_y;
517 break;
519 test_x += PLACETEST_HSTEP;
521 test_y += PLACETEST_VSTEP;
524 return loc_ok;
528 static void
529 cascadeWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
530 unsigned int width, unsigned int height, int h)
532 unsigned int extra_height;
533 WArea usableArea = scr->totalUsableArea;
535 if (wwin->frame)
536 extra_height = wwin->frame->top_width + wwin->frame->bottom_width;
537 else
538 extra_height = 24; /* random value */
540 *x_ret = h * scr->cascade_index + X_ORIGIN(scr);
541 *y_ret = h * scr->cascade_index + Y_ORIGIN(scr);
542 height += extra_height;
544 if (width + *x_ret > usableArea.x2 || height + *y_ret > usableArea.y2) {
545 scr->cascade_index = 0;
546 *x_ret = X_ORIGIN(scr);
547 *y_ret = Y_ORIGIN(scr);
552 void
553 PlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
554 unsigned width, unsigned height)
556 WScreen *scr = wwin->screen_ptr;
557 int h = WMFontHeight(scr->title_font) + (wPreferences.window_title_clearance + TITLEBAR_EXTEND_SPACE) * 2;
559 switch (wPreferences.window_placement) {
560 case WPM_MANUAL:
561 InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
562 break;
564 case WPM_SMART:
565 smartPlaceWindow(wwin, x_ret, y_ret, width, height);
566 break;
568 case WPM_AUTO:
569 if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 0)) {
570 break;
571 } else if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 1)) {
572 break;
574 /* there isn't a break here, because if we fail, it should fall
575 through to cascade placement, as people who want tiling want
576 automagicness aren't going to want to place their window */
578 case WPM_CASCADE:
579 if (wPreferences.window_placement == WPM_AUTO)
580 scr->cascade_index++;
582 cascadeWindow(scr, wwin, x_ret, y_ret, width, height, h);
584 if (wPreferences.window_placement == WPM_CASCADE)
585 scr->cascade_index++;
586 break;
588 case WPM_RANDOM:
590 int w, h, extra_height;
591 WArea usableArea = scr->totalUsableArea;
593 if (wwin->frame)
594 extra_height = wwin->frame->top_width + wwin->frame->bottom_width + 2;
595 else
596 extra_height = 24; /* random value */
598 w = ((usableArea.x2-X_ORIGIN(scr)) - width);
599 h = ((usableArea.y2-Y_ORIGIN(scr)) - height - extra_height);
600 if (w<1) w = 1;
601 if (h<1) h = 1;
602 *x_ret = X_ORIGIN(scr) + rand()%w;
603 *y_ret = Y_ORIGIN(scr) + rand()%h;
605 break;
607 #ifdef DEBUG
608 default:
609 puts("Invalid window placement!!!");
610 *x_ret = 0;
611 *y_ret = 0;
612 #endif
615 if (*x_ret + width > scr->scr_width)
616 *x_ret = scr->scr_width - width;
617 if (*x_ret < 0)
618 *x_ret = 0;
620 if (*y_ret + height > scr->scr_height)
621 *y_ret = scr->scr_height - height;
622 if (*y_ret < 0)
623 *y_ret = 0;