- Fixed compilation warnings.
[wmaker-crm.git] / src / placement.c
blob425826fbdc67a4a4bee5c99ee85cccdd4505aa28
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 int level;
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 (level = MAX_WINDOW_LEVELS-1; level >= WMDesktopLevel; level--) {
200 obj = scr->stacking_list[level];
202 while (obj) {
203 int x, y;
205 if (iconPosition(obj, sx1, sy1, sx2, sy2, scr->current_workspace,
206 &x, &y)) {
207 int xdi, ydi; /* rounded down */
208 int xui, yui; /* rounded up */
210 xdi = x/isize;
211 ydi = y/isize;
212 xui = (x+isize/2)/isize;
213 yui = (y+isize/2)/isize;
214 map[INDEX(xdi,ydi)] = 1;
215 map[INDEX(xdi,yui)] = 1;
216 map[INDEX(xui,ydi)] = 1;
217 map[INDEX(xui,yui)] = 1;
219 obj = obj->stacking->under;
223 * Default position
225 *x_ret = 0;
226 *y_ret = 0;
229 * Look for an empty slot
231 for (si=0; si<sf; si++) {
232 for (pi=0; pi<pf; pi++) {
233 if (wPreferences.icon_yard & IY_VERT) {
234 x = xo + xs*(si*isize);
235 y = yo + ys*(pi*isize);
236 } else {
237 x = xo + xs*(pi*isize);
238 y = yo + ys*(si*isize);
240 if (!map[INDEX(x/isize, y/isize)]) {
241 *x_ret = x;
242 *y_ret = y;
243 done = 1;
244 break;
247 if (done)
248 break;
251 free(map);
256 * This function calculates the length of the intersection of two
257 * line sections. (Hey, is that english?)
259 static int
260 calcIntersectionLength(int p1, int l1, int p2, int l2)
262 int isect;
263 int tmp;
265 if (p1 > p2) {
266 tmp = p1;
267 p1 = p2;
268 p2 = tmp;
269 tmp = l1;
270 l1 = l2;
271 l2 = tmp;
274 if (p1 + l1 < p2)
275 isect = 0;
276 else if (p2 + l2 < p1 + l1)
277 isect = l2;
278 else
279 isect = p1 + l1 - p2;
281 return isect;
286 * This function calculates the area of the intersection of two rectangles.
288 static int
289 calcIntersectionArea(int x1, int y1, int w1, int h1,
290 int x2, int y2, int w2, int h2)
292 return calcIntersectionLength(x1, w1, x2, w2)
293 * calcIntersectionLength(y1, h1, y2, h2);
297 static int
298 calcSumOfCoveredAreas(WWindow *wwin, int x, int y, int w, int h)
300 int sum_isect = 0;
301 WWindow *test_window;
302 int tw,tx,ty,th;
304 test_window = wwin->screen_ptr->focused_window;
305 for(;test_window != NULL && test_window->prev != NULL;)
306 test_window = test_window->prev;
308 for(; test_window != NULL; test_window = test_window->next) {
309 if (test_window->frame->core->stacking->window_level
310 < WMNormalLevel) {
311 continue;
314 #if 0
315 tw = test_window->client.width;
316 if (test_window->flags.shaded)
317 th = test_window->frame->top_width;
318 else
319 th = test_window->client.height + extra_height;
320 #else
321 tw = test_window->frame->core->width;
322 th = test_window->frame->core->height;
323 #endif
324 tx = test_window->frame_x;
325 ty = test_window->frame_y;
327 if (test_window->flags.mapped ||
328 (test_window->flags.shaded &&
329 !(test_window->flags.miniaturized ||
330 test_window->flags.hidden))) {
331 sum_isect += calcIntersectionArea(tx, ty, tw, th, x, y, w, h);
335 return sum_isect;
339 static void
340 smartPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
341 unsigned int width, unsigned int height)
343 WScreen *scr = wwin->screen_ptr;
344 int test_x = 0, test_y = Y_ORIGIN(scr);
345 int from_x, to_x, from_y, to_y;
346 int sx;
347 int min_isect, min_isect_x, min_isect_y;
348 int sum_isect;
349 int extra_height;
350 WArea usableArea = scr->totalUsableArea;
352 if (wwin->frame)
353 extra_height = wwin->frame->top_width + wwin->frame->bottom_width;
354 else
355 extra_height = 24; /* random value */
357 sx = X_ORIGIN(scr);
359 min_isect = INT_MAX;
360 min_isect_x = sx;
361 min_isect_y = test_y;
363 height += extra_height;
365 while (((test_y + height) < usableArea.y2)) {
367 test_x = sx;
369 while ((test_x + width) < usableArea.x2) {
371 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
372 width, height);
374 if ( sum_isect < min_isect ) {
375 min_isect = sum_isect;
376 min_isect_x = test_x;
377 min_isect_y = test_y;
380 test_x += PLACETEST_HSTEP;
382 test_y += PLACETEST_VSTEP;
385 from_x = min_isect_x - PLACETEST_HSTEP + 1;
386 from_x = WMAX(from_x, X_ORIGIN(scr));
387 to_x = min_isect_x + PLACETEST_HSTEP;
388 if (to_x + width > usableArea.x2)
389 to_x = usableArea.x2 - width;
391 from_y = min_isect_y - PLACETEST_VSTEP + 1;
392 from_y = WMAX(from_y, Y_ORIGIN(scr));
393 to_y = min_isect_y + PLACETEST_VSTEP;
394 if (to_y + height > usableArea.y2)
395 to_y = usableArea.y2 - height;
397 for (test_x = from_x; test_x < to_x; test_x++) {
398 for (test_y = from_y; test_y < to_y; test_y++) {
399 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
400 width, height);
402 if ( sum_isect < min_isect ) {
403 min_isect = sum_isect;
404 min_isect_x = test_x;
405 min_isect_y = test_y;
410 *x_ret = min_isect_x;
411 *y_ret = min_isect_y;
415 static Bool
416 autoPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
417 unsigned int width, unsigned int height, int tryCount)
419 WScreen *scr = wwin->screen_ptr;
420 int test_x = 0, test_y = Y_ORIGIN(scr);
421 int loc_ok = False, tw,tx,ty,th;
422 int swidth, sx;
423 WWindow *test_window;
424 int extra_height;
425 WArea usableArea = scr->totalUsableArea;
427 if (wwin->frame)
428 extra_height = wwin->frame->top_width + wwin->frame->bottom_width + 2;
429 else
430 extra_height = 24; /* random value */
432 swidth = usableArea.x2-usableArea.x1;
433 sx = X_ORIGIN(scr);
435 /* this was based on fvwm2's smart placement */
437 height += extra_height;
439 while (((test_y + height) < (scr->scr_height)) && (!loc_ok)) {
441 test_x = sx;
443 while (((test_x + width) < swidth) && (!loc_ok)) {
445 loc_ok = True;
446 test_window = scr->focused_window;
448 while ((test_window != NULL) && (loc_ok == True)) {
450 if (test_window->frame->core->stacking->window_level
451 < WMNormalLevel && tryCount > 0) {
452 test_window = test_window->next;
453 continue;
455 #if 0
456 tw = test_window->client.width;
457 if (test_window->flags.shaded)
458 th = test_window->frame->top_width;
459 else
460 th = test_window->client.height + extra_height;
461 #else
462 tw = test_window->frame->core->width;
463 th = test_window->frame->core->height;
464 #endif
465 tx = test_window->frame_x;
466 ty = test_window->frame_y;
468 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
469 (ty < (test_y + height)) && ((ty + th) > test_y) &&
470 (test_window->flags.mapped ||
471 (test_window->flags.shaded &&
472 !(test_window->flags.miniaturized ||
473 test_window->flags.hidden)))) {
475 loc_ok = False;
477 test_window = test_window->next;
480 test_window = scr->focused_window;
482 while ((test_window != NULL) && (loc_ok == True)) {
484 if (test_window->frame->core->stacking->window_level
485 < WMNormalLevel && tryCount > 0) {
486 test_window = test_window->prev;
487 continue;
489 #if 0
490 tw = test_window->client.width;
491 if (test_window->flags.shaded)
492 th = test_window->frame->top_width;
493 else
494 th = test_window->client.height + extra_height;
495 #else
496 tw = test_window->frame->core->width;
497 th = test_window->frame->core->height;
498 #endif
499 tx = test_window->frame_x;
500 ty = test_window->frame_y;
502 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
503 (ty < (test_y + height)) && ((ty + th) > test_y) &&
504 (test_window->flags.mapped ||
505 (test_window->flags.shaded &&
506 !(test_window->flags.miniaturized ||
507 test_window->flags.hidden)))) {
509 loc_ok = False;
511 test_window = test_window->prev;
513 if (loc_ok == True) {
514 *x_ret = test_x;
515 *y_ret = test_y;
516 break;
518 test_x += PLACETEST_HSTEP;
520 test_y += PLACETEST_VSTEP;
523 return loc_ok;
527 static void
528 cascadeWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
529 unsigned int width, unsigned int height, int h)
531 unsigned int extra_height;
532 WArea usableArea = scr->totalUsableArea;
534 if (wwin->frame)
535 extra_height = wwin->frame->top_width + wwin->frame->bottom_width;
536 else
537 extra_height = 24; /* random value */
539 *x_ret = h * scr->cascade_index + X_ORIGIN(scr);
540 *y_ret = h * scr->cascade_index + Y_ORIGIN(scr);
541 height += extra_height;
543 if (width + *x_ret > usableArea.x2 || height + *y_ret > usableArea.y2) {
544 scr->cascade_index = 0;
545 *x_ret = X_ORIGIN(scr);
546 *y_ret = Y_ORIGIN(scr);
551 void
552 PlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
553 unsigned width, unsigned height)
555 WScreen *scr = wwin->screen_ptr;
556 int h = WMFontHeight(scr->title_font) + TITLEBAR_EXTRA_HEIGHT;
558 switch (wPreferences.window_placement) {
559 case WPM_MANUAL:
560 InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
561 break;
563 case WPM_SMART:
564 smartPlaceWindow(wwin, x_ret, y_ret, width, height);
565 break;
567 case WPM_AUTO:
568 if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 0)) {
569 break;
570 } else if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 1)) {
571 break;
573 /* there isn't a break here, because if we fail, it should fall
574 through to cascade placement, as people who want tiling want
575 automagicness aren't going to want to place their window */
577 case WPM_CASCADE:
578 if (wPreferences.window_placement == WPM_AUTO)
579 scr->cascade_index++;
581 cascadeWindow(scr, wwin, x_ret, y_ret, width, height, h);
583 if (wPreferences.window_placement == WPM_CASCADE)
584 scr->cascade_index++;
585 break;
587 case WPM_RANDOM:
589 int w, h, extra_height;
590 WArea usableArea = scr->totalUsableArea;
592 if (wwin->frame)
593 extra_height = wwin->frame->top_width + wwin->frame->bottom_width + 2;
594 else
595 extra_height = 24; /* random value */
597 w = ((usableArea.x2-X_ORIGIN(scr)) - width);
598 h = ((usableArea.y2-Y_ORIGIN(scr)) - height - extra_height);
599 if (w<1) w = 1;
600 if (h<1) h = 1;
601 *x_ret = X_ORIGIN(scr) + rand()%w;
602 *y_ret = Y_ORIGIN(scr) + rand()%h;
604 break;
606 #ifdef DEBUG
607 default:
608 puts("Invalid window placement!!!");
609 *x_ret = 0;
610 *y_ret = 0;
611 #endif
614 if (*x_ret + width > scr->scr_width)
615 *x_ret = scr->scr_width - width;
616 if (*x_ret < 0)
617 *x_ret = 0;
619 if (*y_ret + height > scr->scr_height)
620 *y_ret = scr->scr_height - height;
621 if (*y_ret < 0)
622 *y_ret = 0;