- Fixed text in info panel for multibyte (Seiichi SATO <ssato@sh.rim.or.jp>)
[wmaker-crm.git] / src / placement.c
blob77fa228d60dbcaae8259f8334e44b7bdd6a87400
1 /* placement.c - window and icon placement on screen
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1997-2002 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 WM_ETARETI_BAG(scr->stacking_list, obj, iter) {
201 while (obj) {
202 int x, y;
204 if (iconPosition(obj, sx1, sy1, sx2, sy2, scr->current_workspace,
205 &x, &y)) {
206 int xdi, ydi; /* rounded down */
207 int xui, yui; /* rounded up */
209 xdi = x/isize;
210 ydi = y/isize;
211 xui = (x+isize/2)/isize;
212 yui = (y+isize/2)/isize;
213 map[INDEX(xdi,ydi)] = 1;
214 map[INDEX(xdi,yui)] = 1;
215 map[INDEX(xui,ydi)] = 1;
216 map[INDEX(xui,yui)] = 1;
218 obj = obj->stacking->under;
222 * Default position
224 *x_ret = 0;
225 *y_ret = 0;
228 * Look for an empty slot
230 for (si=0; si<sf; si++) {
231 for (pi=0; pi<pf; pi++) {
232 if (wPreferences.icon_yard & IY_VERT) {
233 x = xo + xs*(si*isize);
234 y = yo + ys*(pi*isize);
235 } else {
236 x = xo + xs*(pi*isize);
237 y = yo + ys*(si*isize);
239 if (!map[INDEX(x/isize, y/isize)]) {
240 *x_ret = x;
241 *y_ret = y;
242 done = 1;
243 break;
246 if (done)
247 break;
250 wfree(map);
255 * This function calculates the length of the intersection of two
256 * line sections. (Hey, is that english?)
258 static int
259 calcIntersectionLength(int p1, int l1, int p2, int l2)
261 int isect;
262 int tmp;
264 if (p1 > p2) {
265 tmp = p1;
266 p1 = p2;
267 p2 = tmp;
268 tmp = l1;
269 l1 = l2;
270 l2 = tmp;
273 if (p1 + l1 < p2)
274 isect = 0;
275 else if (p2 + l2 < p1 + l1)
276 isect = l2;
277 else
278 isect = p1 + l1 - p2;
280 return isect;
285 * This function calculates the area of the intersection of two rectangles.
287 static int
288 calcIntersectionArea(int x1, int y1, int w1, int h1,
289 int x2, int y2, int w2, int h2)
291 return calcIntersectionLength(x1, w1, x2, w2)
292 * calcIntersectionLength(y1, h1, y2, h2);
296 static int
297 calcSumOfCoveredAreas(WWindow *wwin, int x, int y, int w, int h)
299 int sum_isect = 0;
300 WWindow *test_window;
301 int tw,tx,ty,th;
303 test_window = wwin->screen_ptr->focused_window;
304 for(;test_window != NULL && test_window->prev != NULL;)
305 test_window = test_window->prev;
307 for(; test_window != NULL; test_window = test_window->next) {
308 if (test_window->frame->core->stacking->window_level
309 < WMNormalLevel) {
310 continue;
313 #if 0
314 tw = test_window->client.width;
315 if (test_window->flags.shaded)
316 th = test_window->frame->top_width;
317 else
318 th = test_window->client.height + extra_height;
319 #else
320 tw = test_window->frame->core->width;
321 th = test_window->frame->core->height;
322 #endif
323 tx = test_window->frame_x;
324 ty = test_window->frame_y;
326 if (test_window->flags.mapped ||
327 (test_window->flags.shaded &&
328 !(test_window->flags.miniaturized ||
329 test_window->flags.hidden))) {
330 sum_isect += calcIntersectionArea(tx, ty, tw, th, x, y, w, h);
334 return sum_isect;
338 static void
339 smartPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
340 unsigned int width, unsigned int height)
342 WScreen *scr = wwin->screen_ptr;
343 int test_x = 0, test_y = Y_ORIGIN(scr);
344 int from_x, to_x, from_y, to_y;
345 int sx;
346 int min_isect, min_isect_x, min_isect_y;
347 int sum_isect;
348 int extra_height;
349 WArea usableArea = scr->totalUsableArea;
351 if (wwin->frame)
352 extra_height = wwin->frame->top_width + wwin->frame->bottom_width;
353 else
354 extra_height = 24; /* random value */
356 sx = X_ORIGIN(scr);
358 min_isect = INT_MAX;
359 min_isect_x = sx;
360 min_isect_y = test_y;
362 height += extra_height;
364 while (((test_y + height) < usableArea.y2)) {
366 test_x = sx;
368 while ((test_x + width) < usableArea.x2) {
370 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
371 width, height);
373 if ( sum_isect < min_isect ) {
374 min_isect = sum_isect;
375 min_isect_x = test_x;
376 min_isect_y = test_y;
379 test_x += PLACETEST_HSTEP;
381 test_y += PLACETEST_VSTEP;
384 from_x = min_isect_x - PLACETEST_HSTEP + 1;
385 from_x = WMAX(from_x, X_ORIGIN(scr));
386 to_x = min_isect_x + PLACETEST_HSTEP;
387 if (to_x + width > usableArea.x2)
388 to_x = usableArea.x2 - width;
390 from_y = min_isect_y - PLACETEST_VSTEP + 1;
391 from_y = WMAX(from_y, Y_ORIGIN(scr));
392 to_y = min_isect_y + PLACETEST_VSTEP;
393 if (to_y + height > usableArea.y2)
394 to_y = usableArea.y2 - height;
396 for (test_x = from_x; test_x < to_x; test_x++) {
397 for (test_y = from_y; test_y < to_y; test_y++) {
398 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
399 width, height);
401 if ( sum_isect < min_isect ) {
402 min_isect = sum_isect;
403 min_isect_x = test_x;
404 min_isect_y = test_y;
409 *x_ret = min_isect_x;
410 *y_ret = min_isect_y;
414 static Bool
415 autoPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
416 unsigned int width, unsigned int height, int tryCount)
418 WScreen *scr = wwin->screen_ptr;
419 int test_x = 0, test_y = Y_ORIGIN(scr);
420 int loc_ok = False, tw,tx,ty,th;
421 int swidth, sx;
422 WWindow *test_window;
423 int extra_height;
424 WArea usableArea = scr->totalUsableArea;
426 if (wwin->frame)
427 extra_height = wwin->frame->top_width + wwin->frame->bottom_width + 2;
428 else
429 extra_height = 24; /* random value */
431 swidth = usableArea.x2-usableArea.x1;
432 sx = X_ORIGIN(scr);
434 /* this was based on fvwm2's smart placement */
436 height += extra_height;
438 while (((test_y + height) < (scr->scr_height)) && (!loc_ok)) {
440 test_x = sx;
442 while (((test_x + width) < swidth) && (!loc_ok)) {
444 loc_ok = True;
445 test_window = scr->focused_window;
447 while ((test_window != NULL) && (loc_ok == True)) {
449 if (test_window->frame->core->stacking->window_level
450 < WMNormalLevel && tryCount > 0) {
451 test_window = test_window->next;
452 continue;
454 #if 0
455 tw = test_window->client.width;
456 if (test_window->flags.shaded)
457 th = test_window->frame->top_width;
458 else
459 th = test_window->client.height + extra_height;
460 #else
461 tw = test_window->frame->core->width;
462 th = test_window->frame->core->height;
463 #endif
464 tx = test_window->frame_x;
465 ty = test_window->frame_y;
467 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
468 (ty < (test_y + height)) && ((ty + th) > test_y) &&
469 (test_window->flags.mapped ||
470 (test_window->flags.shaded &&
471 test_window->frame->workspace==scr->current_workspace &&
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->frame->workspace==scr->current_workspace &&
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;