fixed many bugs, removed linked list
[wmaker-crm.git] / src / placement.c
blob7ebde181f9b1e5b1133aa767cbd47b208bbeb0c7
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 void
416 cascadeWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
417 unsigned int width, unsigned int height, int h)
419 unsigned int extra_height;
420 WArea usableArea = scr->totalUsableArea;
422 if (wwin->frame)
423 extra_height = wwin->frame->top_width + wwin->frame->bottom_width;
424 else
425 extra_height = 24; /* random value */
427 *x_ret = h * scr->cascade_index + X_ORIGIN(scr);
428 *y_ret = h * scr->cascade_index + Y_ORIGIN(scr);
429 height += extra_height;
431 if (width + *x_ret > usableArea.x2 || height + *y_ret > usableArea.y2) {
432 scr->cascade_index = 0;
433 *x_ret = X_ORIGIN(scr);
434 *y_ret = Y_ORIGIN(scr);
439 void
440 PlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
441 unsigned width, unsigned height)
443 WScreen *scr = wwin->screen_ptr;
444 int h = WMFontHeight(scr->title_font) + TITLEBAR_EXTRA_HEIGHT;
446 switch (wPreferences.window_placement) {
447 case WPM_MANUAL:
448 InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
449 break;
451 case WPM_SMART:
452 smartPlaceWindow(wwin, x_ret, y_ret, width, height);
453 break;
455 case WPM_CASCADE:
456 if (wPreferences.window_placement == WPM_SMART)
457 scr->cascade_index++;
459 cascadeWindow(scr, wwin, x_ret, y_ret, width, height, h);
461 if (wPreferences.window_placement == WPM_CASCADE)
462 scr->cascade_index++;
463 break;
465 case WPM_RANDOM:
467 int w, h, extra_height;
468 WArea usableArea = scr->totalUsableArea;
470 if (wwin->frame)
471 extra_height = wwin->frame->top_width + wwin->frame->bottom_width + 2;
472 else
473 extra_height = 24; /* random value */
475 w = ((usableArea.x2-X_ORIGIN(scr)) - width);
476 h = ((usableArea.y2-Y_ORIGIN(scr)) - height - extra_height);
477 if (w<1) w = 1;
478 if (h<1) h = 1;
479 *x_ret = X_ORIGIN(scr) + rand()%w;
480 *y_ret = Y_ORIGIN(scr) + rand()%h;
482 break;
484 #ifdef DEBUG
485 default:
486 puts("Invalid window placement!!!");
487 *x_ret = 0;
488 *y_ret = 0;
489 #endif
492 if (*x_ret + width > scr->scr_width)
493 *x_ret = scr->scr_width - width;
494 if (*x_ret < 0)
495 *x_ret = 0;
497 if (*y_ret + height > scr->scr_height)
498 *y_ret = scr->scr_height - height;
499 if (*y_ret < 0)
500 *y_ret = 0;