1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, see: <http://www.gnu.org/licenses/>
14  */
15 
16 #include "config.h"
17 
18 #include "libs/fvwmlib.h"
19 #include "libs/FShape.h"
20 #include "libs/Module.h"
21 #include "libs/FEvent.h"
22 #include "FvwmIconMan.h"
23 #include "x.h"
24 #include "xmanager.h"
25 #include "libs/PictureGraphics.h"
26 #include "libs/PictureUtils.h"
27 #include "libs/Rectangles.h"
28 #include "libs/Grab.h"
29 #include "libs/Graphics.h"
30 #include "libs/Strings.h"
31 #include "libs/wild.h"
32 
33 extern FlocaleWinString *FwinString;
34 
35 /* button dirty bits: */
36 #define ICON_STATE_CHANGED  1
37 #define STATE_CHANGED       2
38 #define PICTURE_CHANGED     4
39 #define WINDOW_CHANGED      8
40 #define STRING_CHANGED      16
41 #define REDRAW_BUTTON       32
42 #define GEOMETRY_CHANGED    64
43 
44 /* manager dirty bits: */
45 /*      GEOMETRY_CHANGED    64 same as with button */
46 #define MAPPING_CHANGED     2
47 #define SHAPE_CHANGED       4
48 #define REDRAW_MANAGER      8
49 #define REDRAW_BG           16
50 
51 /* ButtonArray dirty bits: */
52 #define NUM_BUTTONS_CHANGED 1
53 #define NUM_WINDOWS_CHANGED 2
54 
55 #define ALL_CHANGED         0x7f /* high bit is special */
56 
57 typedef struct {
58   int button_x, button_y, button_h, button_w; /* dim's of the whole button */
59   int icon_x, icon_y, icon_h, icon_w;         /* what denotes icon state */
60   int text_x, text_y, text_h, text_w;         /* text field */
61   int text_base;                              /* text baseline */
62 } ButtonGeometry;
63 
64 static void print_button_info(Button *b);
65 static void insert_windows_button(WinData *win);
66 
67 /*
68  * Utility leaf functions
69  */
70 
selected_button_in_man(WinManager * man)71 static int selected_button_in_man(WinManager *man)
72 {
73   assert(man);
74   ConsoleDebug(X11, "selected_button_in_man: %p\n",
75 		globals.select_win);
76   if (globals.select_win && globals.select_win->button &&
77     globals.select_win->manager == man) {
78     return globals.select_win->button->index;
79   }
80   return -1;
81 }
82 
83 #if 0
84 static void ClipRectangle(WinManager *man, int context,
85 			   int x, int y, int w, int h)
86 {
87   XRectangle r;
88 
89   r.x = x;
90   r.y = y;
91   r.width = w;
92   r.height = h;
93 
94   XSetClipRectangles(theDisplay, man->hiContext[context], 0, 0, &r, 1,
95 		     YXBanded);
96 }
97 #endif
98 
GetRegion(int x,int y,int w,int h)99 static Region GetRegion(int x, int y, int w, int h)
100 {
101   Region region;
102   XRectangle rectangle;
103 
104   rectangle.x = x;
105   rectangle.y = y;
106   rectangle.width = w;
107   rectangle.height = h;
108 
109   region = XCreateRegion();
110   XUnionRectWithRegion(&rectangle, region, region);
111   return region;
112 }
113 
num_visible_rows(int n,int cols)114 static int num_visible_rows(int n, int cols)
115 {
116   return (n - 1) / cols + 1;
117 }
118 
first_row_len(int n,int cols)119 static int first_row_len(int n, int cols)
120 {
121 	int ret = n % cols;
122 	if (ret == 0)
123 		ret = cols;
124 
125 	return ret;
126 }
127 
index_to_box(WinManager * man,int index)128 static int index_to_box(WinManager *man, int index)
129 {
130   int first_len, n, cols;
131 
132   if (man->geometry.dir & GROW_DOWN) {
133     return index;
134   }
135   else {
136     n = man->buttons.num_windows;
137     cols = man->geometry.cols;
138     first_len = first_row_len(n, cols);
139     if (index >= first_len)
140       index += cols - first_len;
141     index += (man->geometry.rows - num_visible_rows(n, cols)) * cols;
142     return index;
143   }
144 }
145 
box_to_index(WinManager * man,int box)146 static int box_to_index(WinManager *man, int box)
147 {
148   int first_len, n, cols;
149   int index = box;
150 
151   if (man->geometry.dir & GROW_DOWN)
152   {
153 	  return index;
154   }
155   else
156   {
157 	  n = man->buttons.num_windows;
158 	  cols = man->geometry.cols;
159 	  first_len = first_row_len(n, cols);
160 
161 	  index = box - (man->geometry.rows - num_visible_rows(n, cols)) * cols;
162 	  if (!((index >= 0 && index < first_len) || index >= cols))
163 	  {
164 		  index = -1;
165 	  }
166 	  if (index >= first_len)
167 	  {
168 		  index -= cols - first_len;
169 	  }
170 
171   }
172 
173   return index;
174 }
175 
index_to_row(WinManager * man,int index)176 static int index_to_row(WinManager *man, int index)
177 {
178   int row;
179 
180   row = index_to_box(man, index) / man->geometry.cols;
181 
182   return row;
183 }
184 
index_to_col(WinManager * man,int index)185 static int index_to_col(WinManager *man, int index)
186 {
187   int col;
188 
189   col = index_to_box(man, index) % man->geometry.cols;
190 
191   return col;
192 }
193 
rects_equal(XRectangle * x,XRectangle * y)194 static int rects_equal(XRectangle *x, XRectangle *y)
195 {
196   return (x->x == y->x) && (x->y == y->y) && (x->width == y->width) &&
197     (x->height == y->height);
198 }
199 
top_y_coord(WinManager * man)200 static int top_y_coord(WinManager *man)
201 {
202   if (man->buttons.num_windows > 0 && (man->geometry.dir & GROW_UP)) {
203     return index_to_row(man, 0) * man->geometry.boxheight;
204   }
205   else {
206     return 0;
207   }
208 }
209 
figure_geometry(WinManager * man)210 static ManGeometry *figure_geometry(WinManager *man)
211 {
212 	/* Given the number of wins in icon_list and width x height, compute
213 	   new geometry. */
214 	/* if GROW_FIXED is set, don't change window geometry */
215 
216 	static ManGeometry ret;
217 	ManGeometry *g = &man->geometry;
218 	int n = man->buttons.num_windows;
219 	Bool noWindow = False;
220 
221 	ret = *g;
222 
223 	ConsoleDebug(
224 		X11, "figure_geometry: %s: %d, %d %d %d %d\n",
225 		man->titlename, n,
226 		ret.width, ret.height, ret.cols, ret.rows);
227 
228 	if (n == 0)
229 	{
230 		n = 1;
231 		noWindow = True;
232 	}
233 
234 	if (man->geometry.dir & GROW_FIXED)
235 	{
236 		ret.cols = num_visible_rows(n, g->rows);
237 		ret.boxwidth = ret.width / ret.cols;
238 		if (man->max_button_width_columns > 0)
239 		{
240 			man->max_button_width =
241 				ret.width / man->max_button_width_columns;
242 		}
243 		if (!noWindow && man->max_button_width > 0 &&
244 		    ret.boxwidth > man->max_button_width)
245 		{
246 			int i = 1;
247 			while(i <= g->rows)
248 			{
249 				if (n*man->max_button_width <= ret.width)
250 				{
251 					ret.cols = n;
252 					break;
253 				}
254 				else if ((n/i)*man->max_button_width <=
255 					 ret.width)
256 				{
257 					ret.cols =
258 						ret.width /
259 						man->max_button_width;
260 					break;
261 				}
262 				i++;
263 			}
264 			ret.boxwidth = man->max_button_width;
265 		}
266 		if (ret.boxwidth < 1)
267 		{
268 			ret.boxwidth = 1;
269 		}
270 	}
271 	else
272 	{
273 		if (man->geometry.dir & GROW_VERT)
274 		{
275 			if (g->cols)
276 			{
277 				ret.rows = num_visible_rows(n, g->cols);
278 			}
279 			else
280 			{
281 				ConsoleMessage(
282 					"Internal error in figure_geometry\n");
283 				ret.rows = 1;
284 			}
285 			ret.height = ret.rows * g->boxheight;
286 			ret.width  = ret.cols * g->boxwidth;
287 			if (ret.boxwidth < 1)
288 			{
289 				ret.boxwidth = 1;
290 			}
291 		}
292 		else
293 		{
294 			/* need to set resize inc  */
295 			if (g->rows)
296 			{
297 				ret.cols = num_visible_rows(n, g->rows);
298 			}
299 			else
300 			{
301 				ConsoleMessage(
302 					"Internal error in figure_geometry\n");
303 				ret.cols = 1;
304 			}
305 			ret.height = ret.rows * g->boxheight;
306 			ret.width = ret.cols * g->boxwidth;
307 			ret.boxwidth = ret.width / ret.cols;
308 			if (ret.boxwidth < 1)
309 			{
310 				ret.boxwidth = 1;
311 			}
312 		}
313 	}
314 
315 	ConsoleDebug(
316 		X11, "figure_geometry: %d %d %d %d %d\n",
317 		n, ret.width, ret.height, ret.cols, ret.rows);
318 
319 	return &ret;
320 }
321 
query_geometry(WinManager * man)322 static ManGeometry *query_geometry(WinManager *man)
323 {
324   XWindowAttributes frame_attr, win_attr;
325   int off_x, off_y;
326   static ManGeometry g;
327 
328   assert(man->window_mapped);
329 
330   off_x = 0;
331   off_y = 0;
332   man->theFrame = find_frame_window(man->theWindow, &off_x, &off_y);
333   if (XGetWindowAttributes(theDisplay, man->theFrame, &frame_attr))
334   {
335     g.x = frame_attr.x + off_x + frame_attr.border_width;
336     g.y = frame_attr.y + off_y + frame_attr.border_width;
337   }
338   else
339   {
340     g.x = off_x;
341     g.y = off_y;
342     fvwm_debug(__func__,
343                "%s: query_geometry: failed to get frame attributes.\n",
344                MyName);
345   }
346   if (XGetWindowAttributes(theDisplay, man->theWindow, &win_attr))
347   {
348     g.width = win_attr.width;
349     g.height = win_attr.height;
350   }
351   else
352   {
353     g.width = 1;
354     g.height = 1;
355     fvwm_debug(__func__,
356                "%s: query_geometry: failed to get window attributes.\n",
357                MyName);
358   }
359 
360   return &g;
361 }
362 
fix_manager_size(WinManager * man,int w,int h)363 static void fix_manager_size(WinManager *man, int w, int h)
364 {
365   XSizeHints size;
366   long mask;
367 
368   if (man->geometry.dir & GROW_FIXED)
369     return;
370 
371   FGetWMNormalHints(theDisplay, man->theWindow, &size, &mask);
372   size.min_width = w;
373   size.max_width = w;
374   size.min_height = h;
375   size.max_height = h;
376   XSetWMNormalHints(theDisplay, man->theWindow, &size);
377 }
378 
release_manager_size(WinManager * man)379 static void release_manager_size(WinManager *man)
380 {
381   XSizeHints size;
382   long mask;
383 
384   if (man->geometry.dir & GROW_FIXED)
385     return;
386 
387   FGetWMNormalHints(theDisplay, man->theWindow, &size, &mask);
388   size.min_width = 1;
389   size.max_width = 65535;
390   size.min_height = 1;
391   size.max_height = 65535;
392   XSetWMNormalHints(theDisplay, man->theWindow, &size);
393 }
394 
395 /* Like XMoveResizeWindow(), but can move in arbitary directions */
resize_window(WinManager * man)396 static void resize_window(WinManager *man)
397 {
398   ManGeometry *g;
399   int x_changed, y_changed, dir;
400 
401   dir = man->geometry.dir;
402   /* Remove any size limits so that the window can be resized without
403    * warnings. */
404   release_manager_size(man);
405 
406   if ((dir & GROW_DOWN) && (dir & GROW_RIGHT)) {
407     XResizeWindow(theDisplay, man->theWindow, man->geometry.width,
408 		   man->geometry.height);
409   }
410   else {
411     MyXGrabServer(theDisplay);
412     g = query_geometry(man);
413     x_changed = y_changed = 0;
414     if (dir & GROW_LEFT) {
415       man->geometry.x = g->x + g->width - man->geometry.width;
416       x_changed = 1;
417     }
418     else {
419       man->geometry.x = g->x;
420     }
421     if (dir & GROW_UP) {
422       man->geometry.y = g->y + g->height - man->geometry.height;
423       y_changed = 1;
424     }
425     else {
426       man->geometry.y = g->y;
427     }
428 
429     ConsoleDebug(X11, "queried: y: %d, h: %d, queried: %d\n", g->y,
430 		  man->geometry.height, g->height);
431 
432     if (x_changed || y_changed) {
433       XMoveResizeWindow(theDisplay, man->theWindow,
434 			 man->geometry.x,
435 			 man->geometry.y,
436 			 man->geometry.width, man->geometry.height);
437     }
438     else {
439       XResizeWindow(theDisplay, man->theWindow, man->geometry.width,
440 		     man->geometry.height);
441     }
442     MyXUngrabServer(theDisplay);
443   }
444   fix_manager_size(man, man->geometry.width, man->geometry.height);
445 }
446 
make_display_string(WinData * win,char * format,int len)447 static char *make_display_string(WinData *win, char *format, int len)
448 {
449 #define MAX_DISPLAY_SIZE 1024
450 #define COPY(field)                                       \
451   temp_p = win->field;                                    \
452   if (temp_p)                                             \
453     while (*temp_p && out_p - buf < len - 1) \
454       *out_p++ = *temp_p++;                               \
455   in_p++;
456 
457   static char buf[MAX_DISPLAY_SIZE];
458   char *string, *in_p, *out_p, *temp_p;
459 
460   in_p = format;
461   out_p = buf;
462 
463   if (len > MAX_DISPLAY_SIZE || len <= 0)
464     len = MAX_DISPLAY_SIZE;
465 
466   while (*in_p && out_p - buf < len - 1) {
467     if (*in_p == '%') {
468       switch (*(++in_p)) {
469       case 'i':
470 	COPY(visible_icon_name);
471 	break;
472 
473       case 't':
474 	COPY(visible_name);
475 	break;
476 
477       case 'r':
478 	COPY(resname);
479 	break;
480 
481       case 'c':
482 	COPY(classname);
483 	break;
484 
485       default:
486 	*out_p++ = *in_p++;
487 	break;
488       }
489     }
490     else {
491       *out_p++ = *in_p++;
492     }
493   }
494 
495   *out_p++ = '\0';
496 
497   string = buf;
498   return string;
499 
500 #undef COPY
501 #undef MAX_DISPLAY_SIZE
502 }
503 
button_above(WinManager * man,Button * b)504 Button *button_above(WinManager *man, Button *b)
505 {
506   int n = man->buttons.num_windows, cols = man->geometry.cols;
507   int i = -1;
508 
509   if (b) {
510     i = box_to_index(man, index_to_box(man, b->index) - cols);
511   }
512   if (i < 0 || i >= n)
513     return b;
514   else
515     return man->buttons.buttons[i];
516 }
517 
button_below(WinManager * man,Button * b)518 Button *button_below(WinManager *man, Button *b)
519 {
520   int n = man->buttons.num_windows;
521   int i = -1;
522 
523   if (b) {
524     i = box_to_index(man, index_to_box(man, b->index) + man->geometry.cols);
525   }
526   if (i < 0 || i >= n)
527     return b;
528   else
529     return man->buttons.buttons[i];
530 }
531 
button_right(WinManager * man,Button * b)532 Button *button_right(WinManager *man, Button *b)
533 {
534   int i = -1;
535 
536   if (index_to_col(man, b->index) < man->geometry.cols - 1) {
537     i = box_to_index(man, index_to_box(man, b->index) + 1);
538   }
539 
540   if (i < 0 || i >= man->buttons.num_windows)
541     return b;
542   else
543     return man->buttons.buttons[i];
544 }
545 
button_left(WinManager * man,Button * b)546 Button *button_left(WinManager *man, Button *b)
547 {
548   int i = -1;
549   if (index_to_col(man, b->index) > 0) {
550     i = box_to_index(man, index_to_box(man, b->index) - 1);
551   }
552 
553   if (i < 0 || i >= man->buttons.num_windows)
554     return b;
555   else
556     return man->buttons.buttons[i];
557 }
558 
button_next(WinManager * man,Button * b)559 Button *button_next(WinManager *man, Button *b)
560 {
561   int i = b->index + 1;
562 
563   if (i >= 0 && i < man->buttons.num_windows)
564     return man->buttons.buttons[i];
565   else
566     return b;
567 }
568 
button_prev(WinManager * man,Button * b)569 Button *button_prev(WinManager *man, Button *b)
570 {
571   int i = b->index - 1;
572 
573   if (i >= 0 && i < man->buttons.num_windows)
574     return man->buttons.buttons[i];
575   else
576     return b;
577 }
578 
xy_to_button(WinManager * man,int x,int y)579 Button *xy_to_button(WinManager *man, int x, int y)
580 {
581 	ManGeometry *g;
582 	int row, col, box, index;
583 
584 	g = figure_geometry(man);
585 	row = y / g->boxheight;
586 	col = x / g->boxwidth;
587 
588 	if (x >= 0 && x <= g->width &&
589 	    y >= 0 && y <= g->height && col < g->cols)
590 	{
591 		box = row * g->cols + col;
592 		index = box_to_index(man, box);
593 		if (index >= 0 && index < man->buttons.num_windows)
594 		{
595 			return man->buttons.buttons[index];
596 		}
597 	}
598 
599 	return NULL;
600 }
601 
602 /*
603  * Routines which change dirtyable state
604  */
605 
set_button_geometry(WinManager * man,Button * box)606 static void set_button_geometry(WinManager *man, Button *box)
607 {
608 	ManGeometry *g;
609 
610 	g = figure_geometry(man);
611 	box->x = index_to_col(man, box->index) * g->boxwidth;
612 	box->y = index_to_row(man, box->index) * g->boxheight;
613 	box->w = g->boxwidth;
614 	box->h = g->boxheight;
615 	box->drawn_state.dirty_flags |= GEOMETRY_CHANGED;
616 }
617 
clear_button(Button * b)618 static void clear_button(Button *b)
619 {
620   assert(b);
621   b->drawn_state.win = NULL;
622   b->drawn_state.dirty_flags = REDRAW_BUTTON;
623   b->drawn_state.display_string = NULL;
624   b->drawn_state.ew = 0;
625 }
626 
set_window_button(WinData * win,int index)627 static void set_window_button(WinData *win, int index)
628 {
629   Button *b;
630 
631   assert(win->manager && index < win->manager->buttons.num_buttons);
632 
633   b = win->manager->buttons.buttons[index];
634 
635   /* Can optimize here */
636   if (FMiniIconsSupported)
637   {
638     b->drawn_state.pic = win->pic;
639   }
640   b->drawn_state.win = win;
641   copy_string(&b->drawn_state.display_string, win->display_string);
642   b->drawn_state.iconified = win->iconified;
643   b->drawn_state.state = win->state;
644   b->drawn_state.dirty_flags = ALL_CHANGED;
645 
646   win->button = b;
647 }
648 
set_num_buttons(ButtonArray * buttons,int n)649 static void set_num_buttons(ButtonArray *buttons, int n)
650 {
651   int i;
652 
653   ConsoleDebug(X11, "set_num_buttons: %d %d\n", buttons->num_buttons, n);
654 
655   if (n > buttons->num_buttons) {
656     buttons->dirty_flags |= NUM_BUTTONS_CHANGED;
657     buttons->buttons = fxrealloc((void *)buttons->buttons, n, sizeof(Button *));
658 
659     for (i = buttons->num_buttons; i < n; i++) {
660       buttons->buttons[i] = fxcalloc(1, sizeof(Button));
661       buttons->buttons[i]->drawn_state.display_string = NULL;
662       buttons->buttons[i]->index = i;
663     }
664 
665     buttons->dirty_flags |= NUM_BUTTONS_CHANGED;
666     buttons->num_buttons = n;
667   }
668 }
669 
increase_num_windows(ButtonArray * buttons,int off)670 static void increase_num_windows(ButtonArray *buttons, int off)
671 {
672   int n;
673 
674   if (off != 0) {
675     buttons->num_windows += off;
676     buttons->dirty_flags |= NUM_WINDOWS_CHANGED;
677 
678     if (buttons->num_windows > buttons->num_buttons) {
679       n = buttons->num_windows + 10;
680       set_num_buttons(buttons, n);
681     }
682   }
683 }
684 
set_man_gravity_origin(WinManager * man)685 static void set_man_gravity_origin(WinManager *man)
686 {
687   if (man->gravity == NorthWestGravity || man->gravity == NorthEastGravity)
688     man->geometry.gravity_y = 0;
689   else
690     man->geometry.gravity_y = man->geometry.height;
691   if (man->gravity == NorthWestGravity || man->gravity == SouthWestGravity)
692     man->geometry.gravity_x = 0;
693   else
694     man->geometry.gravity_x = man->geometry.width;
695 }
696 
set_man_geometry(WinManager * man,ManGeometry * new)697 static void set_man_geometry(WinManager *man, ManGeometry *new)
698 {
699   int n;
700 
701   if (man->geometry.width != new->width ||
702       man->geometry.height != new->height ||
703       man->geometry.rows != new->rows ||
704       man->geometry.cols != new->cols ||
705       man->geometry.boxheight != new->boxheight ||
706       man->geometry.boxwidth != new->boxwidth) {
707     man->dirty_flags |= GEOMETRY_CHANGED;
708   }
709 
710   man->geometry = *new;
711   set_man_gravity_origin(man);
712   n = man->geometry.rows * man->geometry.cols;
713   if (man->buttons.num_buttons < n)
714     set_num_buttons(&man->buttons, n + 10);
715 }
716 
set_manager_width(WinManager * man,int width)717 void set_manager_width(WinManager *man, int width)
718 {
719   if (width != man->geometry.width) {
720     ConsoleDebug(X11, "set_manager_width: %d -> %d, %d -> %d\n",
721 		  man->geometry.width, width, man->geometry.boxwidth,
722 		  width / man->geometry.cols);
723     man->geometry.width = width;
724     man->geometry.boxwidth = width / man->geometry.cols;
725     if (man->geometry.boxwidth < 1)
726       man->geometry.boxwidth = 1;
727     man->dirty_flags |= GEOMETRY_CHANGED;
728   }
729 }
730 
force_manager_redraw(WinManager * man)731 void force_manager_redraw(WinManager *man)
732 {
733   man->dirty_flags |= REDRAW_MANAGER;
734   draw_manager(man);
735 }
736 
set_win_picture(WinData * win,Pixmap picture,Pixmap mask,Pixmap alpha,unsigned int depth,unsigned int width,unsigned int height)737 void set_win_picture(WinData *win, Pixmap picture, Pixmap mask, Pixmap alpha,
738 		      unsigned int depth, unsigned int width,
739 		      unsigned int height)
740 {
741   if (!FMiniIconsSupported)
742   {
743     return;
744   }
745   if (win->button)
746     win->button->drawn_state.dirty_flags |= PICTURE_CHANGED;
747   win->old_pic = win->pic;
748   win->pic.picture = picture;
749   win->pic.mask = mask;
750   win->pic.alpha = alpha;
751   win->pic.width = width;
752   win->pic.height = height;
753   win->pic.depth = depth;
754 }
755 
set_win_iconified(WinData * win,int iconified)756 void set_win_iconified(WinData *win, int iconified)
757 {
758 	/* This change has become necessary because with colorsets we don't
759 	 * know the background colour of the button (gradient background).
760 	 * Thus the button has to be redrawn completely, we can not just draw
761 	 * the square in the background colour. */
762 	char string[256];
763 	WinData *man_data = NULL;
764 	Bool do_animate = True;
765 
766 	if (win->button != NULL && (win->iconified != iconified) )
767 	{
768 		if (win->manager->flags.is_shaded)
769 		{
770 			man_data = id_to_win(win->manager->theWindow);
771 			if (!man_data->geometry_set)
772 			{
773 				do_animate = False;
774 			}
775 		}
776 		else if (win->manager->swallowed)
777 		{
778 			if (win->manager->swallower_win &&
779 			    (man_data =
780 			     id_to_win(win->manager->swallower_win)) &&
781 			    man_data->geometry_set)
782 			{
783 				/* ok */
784 				if (!IS_SHADED(man_data))
785 				{
786 					/* animate as usual */
787 					man_data = NULL;
788 				}
789 			}
790 			else
791 			{
792 				do_animate = False;
793 			}
794 		}
795 		/* we check the win->button width and height because they
796 		 * will only be == zero on init, and if we didn't check we
797 		 * would get animations of all iconified windows to the far
798 		 * left of whereever thae manager would eventually be. */
799 		if (do_animate && win->manager->AnimCommand &&
800 		    (win->manager->AnimCommand[0] != 0)
801 		    && IS_ICON_SUPPRESSED(win) && (win->button->w != 0)
802 		    && (win->button->h !=0))
803 		{
804 			int abs_x, abs_y, w, h;
805 			Window junkw;
806 
807 			XTranslateCoordinates(
808 				theDisplay, win->manager->theWindow,
809 				theRoot, win->button->x, win->button->y,
810 				&abs_x, &abs_y, &junkw);
811 			w = win->button->w;
812 			h = win->button->h;
813 			if (man_data)
814 			{
815 				if (w > man_data->real_g.width)
816 				{
817 					w = 1;
818 				}
819 				if (h > man_data->real_g.height)
820 				{
821 					h = 1;
822 				}
823 				if (abs_x < man_data->real_g.x ||
824 				    abs_x > man_data->real_g.x +
825 				    man_data->real_g.width)
826 				{
827 					abs_x = man_data->real_g.x;
828 				}
829 				if (abs_y < man_data->real_g.y ||
830 				    abs_y > man_data->real_g.y +
831 				    man_data->real_g.height)
832 				{
833 					abs_y = man_data->real_g.y;
834 				}
835 			}
836 			if (iconified)
837 			{
838 				sprintf(string, "%s %d %d %d %d %d %d %d %d",
839 					win->manager->AnimCommand,
840 					(int)win->x, (int)win->y,
841 					(int)win->width, (int)win->height,
842 					abs_x, abs_y, w, h);
843 			}
844 			else
845 			{
846 				sprintf(string, "%s %d %d %d %d %d %d %d %d",
847 					win->manager->AnimCommand,
848 					abs_x, abs_y, w, h,
849 					(int)win->x, (int)win->y,
850 					(int)win->width, (int)win->height);
851 			}
852 			SendText(fvwm_fd, string, 0);
853 		}
854 		win->button->drawn_state.dirty_flags |= ICON_STATE_CHANGED;
855 	}
856 	win->iconified = iconified;
857 	if (iconified)
858 	{
859 		win->state = ICON_CONTEXT;
860 		if (globals.select_win == win)
861 		{
862 			add_win_state(win, SELECT_CONTEXT);
863 		}
864 	}
865 	else
866 	{
867 		win->state = PLAIN_CONTEXT;
868 		if (globals.select_win == win)
869 		{
870 			add_win_state(win, SELECT_CONTEXT);
871 		}
872 		if (globals.focus_win == win)
873 		{
874 			add_win_state(win, FOCUS_CONTEXT);
875 		}
876 	}
877 }
878 
set_win_state(WinData * win,int state)879 void set_win_state(WinData *win, int state)
880 {
881 	if (win->button && win->state != state)
882 		win->button->drawn_state.dirty_flags |= STATE_CHANGED;
883 	win->state = state;
884 }
885 
886 /* this is "broken" */
add_win_state(WinData * win,int flag)887 void add_win_state(WinData *win, int flag)
888 {
889 	int old_state = win->state;
890 
891 	if (flag == FOCUS_CONTEXT)
892 	{
893 		if (win->state == SELECT_CONTEXT ||
894 		    win->state == FOCUS_SELECT_CONTEXT)
895 		{
896 			win->state = FOCUS_SELECT_CONTEXT;
897 		}
898 		else
899 		{
900 			win->state = FOCUS_CONTEXT;
901 		}
902 	}
903 	else if (flag == SELECT_CONTEXT)
904 	{
905 		if (win->state == FOCUS_CONTEXT ||
906 		    win->state == FOCUS_SELECT_CONTEXT)
907 		{
908 			win->state = FOCUS_SELECT_CONTEXT;
909 		}
910 		else if (win->state == ICON_CONTEXT ||
911 			 win->state == ICON_SELECT_CONTEXT)
912 		{
913 			win->state = ICON_SELECT_CONTEXT;
914 		}
915 		else
916 		{
917 			win->state = SELECT_CONTEXT;
918 		}
919 	}
920 	else
921 	{
922 		/* add_win_state should not be called  */
923 		win->state = flag;
924 	}
925 	if (win->button && (old_state != win->state))
926 	{
927 		win->button->drawn_state.dirty_flags |= STATE_CHANGED;
928 	}
929 }
930 
931 /* this is "broken" */
del_win_state(WinData * win,int flag)932 void del_win_state(WinData *win, int flag)
933 {
934 	if (flag == FOCUS_CONTEXT)
935 	{
936 		if (win->state == FOCUS_SELECT_CONTEXT)
937 		{
938 			if (win->iconified)
939 			{
940 				win->state = ICON_SELECT_CONTEXT;
941 			}
942 			else
943 			{
944 				win->state = SELECT_CONTEXT;
945 			}
946 		}
947 		else if (win->state == FOCUS_CONTEXT)
948 		{
949 			if (win->iconified)
950 			{
951 				win->state = ICON_CONTEXT;
952 			}
953 			else
954 			{
955 				win->state = PLAIN_CONTEXT;
956 			}
957 		}
958 		else
959 		{
960 			/* nothing */
961 		}
962 	}
963 	else if (flag == SELECT_CONTEXT)
964 	{
965 		if (win->state == FOCUS_SELECT_CONTEXT)
966 		{
967 			win->state = FOCUS_CONTEXT;
968 		}
969 		else if (win->state == ICON_SELECT_CONTEXT)
970 		{
971 			win->state = ICON_CONTEXT;
972 		}
973 		else if (win->state == SELECT_CONTEXT)
974 		{
975 			win->state = PLAIN_CONTEXT;
976 		}
977 		else
978 		{
979 			/* */
980 		}
981 	}
982 	else
983 	{
984 		/* del_win_state should not be called  */
985 		/*win->state = PLAIN_CONTEXT;*/
986 	}
987 	if (win->button)
988 	{
989 		win->button->drawn_state.dirty_flags |= STATE_CHANGED;
990 	}
991 }
992 
set_win_displaystring(WinData * win)993 void set_win_displaystring(WinData *win)
994 {
995   WinManager *man = win->manager;
996   int maxlen;
997   char *tmp;
998 
999   if (man && win->button && win->button == man->tipped_button)
1000   {
1001 	  tips_update_label(man);
1002   }
1003 
1004   if (!man || ((man->format_depend & CLASS_NAME) && !win->classname)
1005       || ((man->format_depend & ICON_NAME) && !win->visible_icon_name)
1006       || ((man->format_depend & TITLE_NAME) && !win->visible_name)
1007       || ((man->format_depend & RESOURCE_NAME) && !win->resname)) {
1008     return;
1009   }
1010 
1011   if (man->window_up) {
1012     assert(man->geometry.width && man->fontwidth);
1013     maxlen = man->geometry.width / man->fontwidth + 2 /* fudge factor */;
1014   }
1015   else {
1016     maxlen = 0;
1017   }
1018 
1019   tmp = make_display_string(win, man->formatstring, maxlen);
1020   if ((tmp == NULL && win->display_string == NULL) ||
1021       (tmp != NULL && win->display_string != NULL &&
1022        strcmp(tmp, win->display_string) == 0))
1023   {
1024 	  return;
1025   }
1026   copy_string(&win->display_string, tmp);
1027   if (win->button)
1028     win->button->drawn_state.dirty_flags |= STRING_CHANGED;
1029 }
1030 
1031 /* This function is here and not with the other static utility functions
1032    because it basically is the inverse of set_shape() */
1033 
clear_empty_region(WinManager * man)1034 static void clear_empty_region(WinManager *man)
1035 {
1036   XRectangle rects[3];
1037   int num_rects = 0, n = man->buttons.num_windows, cols = man->geometry.cols;
1038   int rows = man->geometry.rows;
1039   int boxwidth = man->geometry.boxwidth;
1040   int boxheight = man->geometry.boxheight;
1041 
1042   if (man->shaped)
1043     return;
1044 
1045   rects[1].x = rects[1].y = rects[1].width = rects[1].height = 0;
1046 
1047   if (n == 0 || rows * cols == 0 /* just be to safe */)
1048   {
1049     rects[0].x = 0;
1050     rects[0].y = 0;
1051     rects[0].width = man->geometry.width;
1052     rects[0].height = man->geometry.height;
1053     num_rects = 1;
1054   }
1055   else if (man->geometry.dir & GROW_DOWN)
1056   {
1057     assert(cols);
1058     if (n % cols == 0)
1059     {
1060       rects[0].x = 0;
1061       rects[0].y = num_visible_rows(n, cols) * man->geometry.boxheight;
1062       rects[0].width = man->geometry.width;
1063       rects[0].height = man->geometry.height - rects[0].y;
1064       num_rects = 1;
1065     }
1066     else
1067     {
1068       rects[0].x = (n % cols) * man->geometry.boxwidth;
1069       rects[0].y = (num_visible_rows(n, cols) - 1) * man->geometry.boxheight;
1070       rects[0].width = man->geometry.width - rects[0].x;
1071       rects[0].height = boxheight;
1072       rects[1].x = 0;
1073       rects[1].y = rects[0].y + rects[0].height;
1074       rects[1].width = man->geometry.width;
1075       rects[1].height = man->geometry.height - rects[0].y;
1076       num_rects = 2;
1077     }
1078   }
1079   else
1080   {
1081     assert(cols);
1082     /* for shaped windows, we won't see this part of the window */
1083     if (n % cols == 0)
1084     {
1085       rects[0].x = 0;
1086       rects[0].y = 0;
1087       rects[0].width = man->geometry.width;
1088       rects[0].height = top_y_coord(man);
1089       num_rects = 1;
1090     }
1091     else
1092     {
1093       rects[0].x = 0;
1094       rects[0].y = 0;
1095       rects[0].width = man->geometry.width;
1096       rects[0].height = top_y_coord(man);
1097       rects[1].x = (n % cols) * man->geometry.boxwidth;
1098       rects[1].y = rects[0].height;
1099       rects[1].width = man->geometry.width - rects[1].x;
1100       rects[1].height = boxheight;
1101       num_rects = 2;
1102     }
1103   }
1104 
1105   if (n != 0 && rows * cols != 0)
1106   {
1107     rects[num_rects].x = cols * boxwidth;
1108     rects[num_rects].y = 0;
1109     rects[num_rects].width = man->geometry.width - cols * boxwidth;
1110     rects[num_rects].height = man->geometry.height;
1111     num_rects++;
1112   }
1113 
1114   ConsoleDebug(X11, "Clearing: %d: (%d, %d, %d, %d) + (%d, %d, %d, %d)\n",
1115 		num_rects,
1116 		rects[0].x, rects[0].y, rects[0].width, rects[0].height,
1117 		rects[1].x, rects[1].y, rects[1].width, rects[1].height);
1118 
1119   int cset = man->colorsets[DEFAULT];
1120 
1121   if (CSET_IS_TRANSPARENT_PR_PURE(cset))
1122   {
1123     for(n=0; n < num_rects; n++)
1124     {
1125 	    if (rects[n].width > 0 && rects[n].height > 0)
1126 	    {
1127 		    XClearArea(
1128 			    theDisplay, man->theWindow, rects[n].x, rects[n].y,
1129 			    rects[n].width, rects[n].height, False);
1130 	    }
1131     }
1132   }
1133   else if (cset >= 0)
1134   {
1135     for(n=0; n < num_rects; n++)
1136     {
1137 	    if (rects[n].width > 0 && rects[n].height > 0)
1138 	    {
1139 		    SetRectangleBackground(theDisplay, man->theWindow,
1140 			    rects[n].x, rects[n].y, rects[n].width,
1141 			    rects[n].height, &Colorset[cset], Pdepth,
1142 			    man->backContext[DEFAULT]);
1143 	    }
1144     }
1145   }
1146   else
1147   {
1148     XFillRectangles(theDisplay, man->theWindow,
1149 		     man->backContext[DEFAULT], rects, num_rects);
1150   }
1151 }
1152 
set_shape(WinManager * man)1153 void set_shape(WinManager *man)
1154 {
1155   if (FShapesSupported)
1156   {
1157     int n;
1158     XRectangle rects[2];
1159     int cols = man->geometry.cols;
1160 
1161     if (man->shaped == 0)
1162       return;
1163 
1164     ConsoleDebug(X11, "in set_shape: %s\n", man->titlename);
1165 
1166     n = man->buttons.num_windows;
1167     if (n == 0)
1168     {
1169       man->shape.num_rects = 1;
1170       rects[0].x = -1;
1171       rects[0].y = -1;
1172       rects[0].width = 1;
1173       rects[0].height = 1;
1174       if (man->shape.num_rects != 0) {
1175 	man->dirty_flags |= SHAPE_CHANGED;
1176       }
1177       man->shape.rects[0] = rects[0];
1178       return;
1179     }
1180 
1181     if (cols == 0 || n % cols == 0) {
1182       rects[0].x = 0;
1183       rects[0].y = top_y_coord(man);
1184       rects[0].width = cols * man->geometry.boxwidth;
1185       rects[0].height = num_visible_rows(n, cols) * man->geometry.boxheight;
1186       if (man->shape.num_rects != 1 || !rects_equal(rects, man->shape.rects)) {
1187 	man->dirty_flags |= SHAPE_CHANGED;
1188       }
1189       man->shape.num_rects = 1;
1190       man->shape.rects[0] = rects[0];
1191     }
1192     else {
1193       if (man->geometry.dir & GROW_DOWN) {
1194 	rects[0].x = 0;
1195 	rects[0].y = 0;
1196 	rects[0].width = cols * man->geometry.boxwidth;
1197 	rects[0].height =
1198 	  (num_visible_rows(n, cols) - 1) * man->geometry.boxheight;
1199 	rects[1].x = 0;
1200 	rects[1].y = rects[0].height;
1201 	rects[1].width = (n % cols) * man->geometry.boxwidth;
1202 	rects[1].height = man->geometry.boxheight;
1203       }
1204       else {
1205 	rects[0].x = 0;
1206 	rects[0].y = top_y_coord(man);
1207 	rects[0].width = (n % cols) * man->geometry.boxwidth;
1208 	rects[0].height = man->geometry.boxheight;
1209 	rects[1].x = 0;
1210 	rects[1].y = rects[0].y + rects[0].height;
1211 	rects[1].width = man->geometry.width;
1212 	rects[1].height = (num_visible_rows(n, cols) - 1) *
1213 	  man->geometry.boxheight;
1214       }
1215       if (man->shape.num_rects != 2 ||
1216 	  !rects_equal(rects, man->shape.rects) ||
1217 	  !rects_equal(rects + 1, man->shape.rects + 1)) {
1218 	man->dirty_flags |= SHAPE_CHANGED;
1219       }
1220       man->shape.num_rects = 2;
1221       man->shape.rects[0] = rects[0];
1222       man->shape.rects[1] = rects[1];
1223     }
1224   }
1225 }
1226 
set_manager_window_mapping(WinManager * man,int flag)1227 void set_manager_window_mapping(WinManager *man, int flag)
1228 {
1229   if (flag != man->window_mapped) {
1230     man->window_mapped = flag;
1231     man->dirty_flags |= MAPPING_CHANGED;
1232   }
1233 }
1234 
1235 /*
1236  * Major exported functions
1237  */
1238 
init_button_array(ButtonArray * array)1239 void init_button_array(ButtonArray *array)
1240 {
1241   memset(array, 0, sizeof(ButtonArray));
1242 }
1243 
1244 /* Pretty much like resize_manager, but used only to figure the
1245    correct size when creating the window */
1246 
size_manager(WinManager * man)1247 void size_manager(WinManager *man)
1248 {
1249   ManGeometry *new;
1250   int oldwidth, oldheight, w, h;
1251 
1252   new = figure_geometry(man);
1253 
1254   assert(new->width && new->height);
1255 
1256   w = new->width;
1257   h = new->height;
1258 
1259   oldwidth = man->geometry.width;
1260   oldheight = man->geometry.height;
1261 
1262   set_man_geometry(man, new);
1263 
1264   if (oldheight != h || oldwidth != w) {
1265     if (man->geometry.dir & GROW_UP)
1266       man->geometry.y -= h - oldheight;
1267     if (man->geometry.dir & GROW_LEFT)
1268       man->geometry.x -= w - oldwidth;
1269   }
1270 
1271   ConsoleDebug(X11, "size_manager %s: %d %d %d %d\n", man->titlename,
1272 		man->geometry.x, man->geometry.y, man->geometry.width,
1273 		man->geometry.height);
1274 }
1275 
resize_manager(WinManager * man,int force)1276 static void resize_manager(WinManager *man, int force)
1277 {
1278   ManGeometry *new;
1279   int oldwidth, oldheight, oldrows, oldcols, old_boxwidth, old_boxheight;
1280   int dir;
1281   int i;
1282 
1283   if (man->flags.is_shaded)
1284   {
1285     man->flags.needs_resize_after_unshade = 1;
1286     return;
1287   }
1288   if (man->can_draw == 0)
1289     return;
1290 
1291   oldwidth = man->geometry.width;
1292   oldheight = man->geometry.height;
1293   oldrows = man->geometry.rows;
1294   oldcols = man->geometry.cols;
1295   dir = man->geometry.dir;
1296   old_boxwidth = man->geometry.boxheight;
1297   old_boxheight = man->geometry.boxwidth;
1298 
1299   if (dir & GROW_FIXED) {
1300     new = figure_geometry(man);
1301     set_man_geometry(man, new);
1302     set_shape(man);
1303     if (force || oldrows != new->rows || oldcols != new->cols ||
1304 	oldwidth != new->width || oldheight != new->height) {
1305       man->dirty_flags |= GEOMETRY_CHANGED;
1306     }
1307   }
1308   else {
1309     new = figure_geometry(man);
1310     set_man_geometry(man, new);
1311     set_shape(man);
1312     if (force || oldrows != new->rows || oldcols != new->cols ||
1313 	oldwidth != new->width || oldheight != new->height) {
1314       resize_window(man);
1315     }
1316   }
1317 
1318 #if 1 /* not sure that this is needed */
1319   if (oldwidth != new->width || oldheight != new->height)
1320   {
1321     for (i = 0; i < NUM_CONTEXTS; i++)
1322     {
1323 	    if (man->colorsets[i] >=0 &&
1324 		(i == DEFAULT || CSET_IS_TRANSPARENT(man->colorsets[i])) &&
1325 		Colorset[man->colorsets[i]].pixmap)
1326 	    {
1327 		    recreate_background(man, i);
1328 	    }
1329     }
1330   }
1331 
1332   if (old_boxwidth != new->boxwidth || old_boxheight != new->boxheight)
1333   {
1334     for (i = 0; i < NUM_CONTEXTS; i++)
1335     {
1336 	    if (man->colorsets[i] >= 0 && i != DEFAULT &&
1337 		!CSET_IS_TRANSPARENT(man->colorsets[i]) &&
1338 		Colorset[man->colorsets[i]].pixmap)
1339 	    {
1340 		    recreate_background(man, i);
1341 	    }
1342     }
1343   }
1344 #endif
1345 }
1346 
center_padding(int h1,int h2)1347 static int center_padding(int h1, int h2)
1348 {
1349   return (h2 - h1) / 2;
1350 }
1351 
get_title_geometry(WinManager * man,ButtonGeometry * g)1352 static void get_title_geometry(WinManager *man, ButtonGeometry *g)
1353 {
1354   int text_pad;
1355   assert(man);
1356   g->button_x = 0;
1357   g->button_y = 0;
1358   g->button_w = man->geometry.boxwidth;
1359   g->button_h = man->geometry.boxheight;
1360   g->text_x = g->button_x + g->button_h / 2;
1361   g->text_w = g->button_w - 4 - (g->text_x - g->button_x);
1362   if (g->text_w <= 0)
1363     g->text_w = 1;
1364   g->text_h = man->fontheight;
1365   text_pad = center_padding(man->fontheight, g->button_h);
1366 
1367   g->text_y = g->button_y + text_pad;
1368   g->text_base = g->text_y + man->FButtonFont->ascent;
1369 }
1370 
get_button_geometry(WinManager * man,Button * button,ButtonGeometry * g)1371 static void get_button_geometry(WinManager *man, Button *button,
1372 				 ButtonGeometry *g)
1373 {
1374   int icon_pad, text_pad;
1375   WinData *win;
1376 
1377   assert(man);
1378 
1379   win = button->drawn_state.win;
1380 
1381   g->button_x = button->x;
1382   g->button_y = button->y;
1383 
1384   g->button_w = button->w;
1385   g->button_h = button->h;
1386 
1387 /* [BV 16-Apr-97] Mini Icons work on black-and-white too */
1388   if (FMiniIconsSupported && man->draw_icons && win && win->pic.picture) {
1389     /* If no window, then icon_* aren't used, so doesn't matter what
1390        they are */
1391     g->icon_w = min(win->pic.width, g->button_h);
1392     g->icon_h = min(g->button_h - 4, win->pic.height);
1393     icon_pad  = center_padding(g->icon_h, g->button_h);
1394     g->icon_x = g->button_x + 4;
1395     g->icon_y = g->button_y + icon_pad;
1396   }
1397   else {
1398     g->icon_h = man->geometry.boxheight - 8;
1399     g->icon_w = g->icon_h;
1400 
1401     icon_pad = center_padding(g->icon_h, g->button_h);
1402     g->icon_x = g->button_x + icon_pad;
1403     g->icon_y = g->button_y + icon_pad;
1404   }
1405 
1406   g->text_x = g->icon_x + g->icon_w + 2;
1407   g->text_w = g->button_w - 4 - (g->text_x - g->button_x);
1408   if (g->text_w <= 0)
1409     g->text_w = 1;
1410   g->text_h = man->fontheight;
1411 
1412   text_pad = center_padding(man->fontheight, g->button_h);
1413 
1414   g->text_y = g->button_y + text_pad;
1415   g->text_base = g->text_y + man->FButtonFont->ascent;
1416 }
1417 
draw_button_background(WinManager * man,XRectangle bounding,Contexts button_state)1418 static void draw_button_background(
1419 	WinManager *man, XRectangle bounding, Contexts button_state)
1420 {
1421 	int cset = man->colorsets[button_state];
1422 
1423 	if (cset >= 0)
1424 	{
1425 		SetRectangleBackground(
1426 			theDisplay, man->theWindow,
1427 			bounding.x, bounding.y,
1428 			bounding.width, bounding.height,
1429 			&Colorset[cset], Pdepth,
1430 			man->backContext[button_state]);
1431 	}
1432 	else if (man->backContext[button_state] == None)
1433 	{
1434 		XClearArea(
1435 			theDisplay, man->theWindow,
1436 			bounding.x, bounding.y,
1437 			bounding.width, bounding.height,
1438 			False);
1439 	}
1440 	else
1441 	{
1442 		XFillRectangle(
1443 			theDisplay, man->theWindow,
1444 			man->backContext[button_state],
1445 			bounding.x, bounding.y,
1446 			bounding.width, bounding.height);
1447 	}
1448 }
1449 
draw_3d_icon(WinManager * man,int box,ButtonGeometry * g,int iconified,Contexts contextId)1450 static void draw_3d_icon(WinManager *man, int box, ButtonGeometry *g,
1451 			  int iconified, Contexts contextId)
1452 {
1453   if ((iconified == 0) || (man->relief_thickness == 0))
1454     return;
1455   else
1456     RelieveRectangle(theDisplay, man->theWindow, g->icon_x, g->icon_y,
1457 		      g->icon_w - 1, g->icon_h - 1,
1458 		      man->reliefContext[contextId],
1459 		      man->shadowContext[contextId], man->relief_thickness);
1460 }
1461 
1462 
1463   /* this routine should only be called from draw_button() */
iconify_box(WinManager * man,WinData * win,int box,ButtonGeometry * g,int iconified,Contexts contextId,int button_already_cleared,XRectangle bounding)1464 static void iconify_box(WinManager *man, WinData *win, int box,
1465 			 ButtonGeometry *g, int iconified, Contexts contextId,
1466 			 int button_already_cleared, XRectangle bounding)
1467 {
1468 	XRectangle inter;
1469 	int cset = man->colorsets[contextId];
1470 	Bool erase = False;
1471 
1472 	if (!man->window_up)
1473 	{
1474 		return;
1475 	}
1476 	frect_get_intersection(
1477 		bounding.x, bounding.y, bounding.width, bounding.height,
1478 		g->icon_x, g->icon_y, g->icon_w, g->icon_h,
1479 		&inter);
1480 
1481 	if (inter.width <= 0 || inter.height <= 0)
1482 	{
1483 		return;
1484 	}
1485 
1486         /* [BV 16-Apr-97] Mini Icons work on black-and-white too */
1487 	if (FMiniIconsSupported && man->draw_icons && win->pic.picture)
1488 	{
1489 		if (iconified == 0 && man->draw_icons != 2)
1490 		{
1491 			erase = True;
1492 		}
1493 		else
1494 		{
1495 			FvwmRenderAttributes fra;
1496 			int p_x = 0, p_y = 0;
1497 
1498 			fra.mask = FRAM_DEST_IS_A_WINDOW;
1499 			if (cset >= 0)
1500 			{
1501 				fra.mask |= FRAM_HAVE_ICON_CSET;
1502 				fra.colorset = &Colorset[cset];
1503 			}
1504 			if (inter.x > g->icon_x)
1505 			{
1506 				p_x = inter.x - g->icon_x;
1507 			}
1508 			if (inter.y > g->icon_y)
1509 			{
1510 				p_y = inter.y - g->icon_y;
1511 			}
1512 			PGraphicsRenderPicture(
1513 				theDisplay, man->theWindow, &win->pic, &fra,
1514 				man->theWindow, man->hiContext[contextId],
1515 				None, None,
1516 				p_x, p_y, inter.width, inter.height,
1517 				inter.x, inter.y, inter.width, inter.height,
1518 				False);
1519 		}
1520 	}
1521 	else
1522 	{
1523 		if (!PictureUseBWOnly())
1524 		{
1525 			draw_3d_icon(man, box, g, iconified, contextId);
1526 		}
1527 		else
1528 		{
1529 			if (iconified == 0)
1530 			{
1531 				erase = True;
1532 			}
1533 			else
1534 			{
1535 				XFillArc(
1536 					theDisplay, man->theWindow,
1537 					man->hiContext[contextId],
1538 					g->icon_x, g->icon_y, g->icon_w,
1539 					g->icon_h, 0, 360 * 64);
1540 			}
1541 		}
1542 	}
1543 	if (erase)
1544 	{
1545 		draw_button_background(man, inter, contextId);
1546 	}
1547 }
1548 
change_windows_manager(WinData * win)1549 int change_windows_manager(WinData *win)
1550 {
1551   WinManager *oldman;
1552   WinManager *newman;
1553 
1554   ConsoleDebug(X11, "change_windows_manager: %s\n", win->titlename);
1555 
1556   oldman = win->manager;
1557   newman = figure_win_manager(win, ALL_NAME);
1558   if (oldman && newman != oldman && win->button) {
1559     delete_windows_button(win);
1560   }
1561   win->manager = newman;
1562   set_win_displaystring(win);
1563   check_win_complete(win);
1564   check_in_window(win);
1565   ConsoleDebug(X11, "change_windows_manager: returning %d\n",
1566 		newman != oldman);
1567   return (newman != oldman);
1568 }
1569 
check_in_window(WinData * win)1570 void check_in_window(WinData *win)
1571 {
1572   int in_viewport;
1573   int is_state_selected;
1574 
1575   if (win->complete) {
1576     WinManager *oldman;
1577     WinManager *newman;
1578 
1579     ConsoleDebug(X11, "change_windows_manager: %s\n", win->titlename);
1580 
1581     oldman = win->manager;
1582     newman = figure_win_manager(win, ALL_NAME);
1583     if (oldman && newman != oldman && win->button) {
1584       oldman->we_are_drawing = 1;
1585       delete_windows_button(win);
1586     }
1587     win->manager = newman;
1588     set_win_displaystring(win);
1589   }
1590 
1591   if (win->manager && win->complete) {
1592     is_state_selected =
1593 	    ((!win->manager->showonlyiconic || win->iconified) &&
1594 	     (!win->manager->shownoiconic || !win->iconified) &&
1595 	     (win->manager->showtransient || !IS_TRANSIENT(win)));
1596     in_viewport = win_in_viewport(win);
1597     if (win->manager->usewinlist && DO_SKIP_WINDOW_LIST(win))
1598       in_viewport = 0;
1599     if ((win->manager->showonlyfocused && win->state != FOCUS_CONTEXT) &&
1600 	(win->manager->showonlyfocused && win->state != FOCUS_SELECT_CONTEXT))
1601 	in_viewport = 0;
1602     if (win->button == NULL && in_viewport && is_state_selected) {
1603       insert_windows_button(win);
1604       if (win->manager->window_up == 0 && globals.got_window_list)
1605 	create_manager_window(win->manager->index);
1606     }
1607     if (win->button && (!in_viewport || !is_state_selected)) {
1608       if (win->button->drawn_state.display_string)
1609 	Free(win->button->drawn_state.display_string);
1610       delete_windows_button(win);
1611     }
1612   }
1613 }
1614 
get_gcs(WinManager * man,int state,int iconified,GC * context1,GC * context2)1615 static void get_gcs(WinManager *man, int state, int iconified,
1616 		     GC *context1, GC *context2)
1617 {
1618   GC gc1, gc2;
1619 
1620   switch (man->buttonState[state]) {
1621   case BUTTON_FLAT:
1622     gc1 = man->flatContext[state];
1623     gc2 = man->flatContext[state];
1624     break;
1625 
1626   case BUTTON_UP:
1627   case BUTTON_EDGEUP:
1628     gc1 = man->reliefContext[state];
1629     gc2 = man->shadowContext[state];
1630     break;
1631 
1632   case BUTTON_DOWN:
1633   case BUTTON_EDGEDOWN:
1634     gc1 = man->shadowContext[state];
1635     gc2 = man->reliefContext[state];
1636     break;
1637 
1638   default:
1639     ConsoleMessage("Internal error in get_gcs\n");
1640     return;
1641   }
1642 
1643   if (((man->rev == REVERSE_ICON) && iconified)
1644       || ((man->rev == REVERSE_NORMAL) && !iconified)) {
1645     *context1 = gc2;
1646     *context2 = gc1;
1647   } else {
1648     *context1 = gc1;
1649     *context2 = gc2;
1650   }
1651   return;
1652 }
1653 
draw_relief(WinManager * man,int button_state,ButtonGeometry * g,GC context1,GC context2)1654 static void draw_relief(WinManager *man, int button_state, ButtonGeometry *g,
1655 			 GC context1, GC context2)
1656 {
1657   int state;
1658   int relief;
1659 
1660   state = man->buttonState[button_state];
1661   relief = man->relief_thickness;
1662 
1663   /* Make sure that the relief isn't too large for the button... */
1664   if ((abs(relief) > (man->geometry.boxheight / 2)) ||
1665       (abs(relief) > (man->geometry.boxwidth / 2))) {
1666       relief = min(man->geometry.boxheight/2, man->geometry.boxwidth/2);
1667       if (man->relief_thickness < 0)
1668         relief *= -1;
1669   }
1670 
1671   if (state == BUTTON_FLAT || relief == 0)
1672     return;
1673   if (state == BUTTON_EDGEUP || state == BUTTON_EDGEDOWN) {
1674     RelieveRectangle(theDisplay, man->theWindow, g->button_x, g->button_y,
1675 		      g->button_w - 1, g->button_h - 1, context1, context2,
1676 		      relief);
1677     RelieveRectangle(theDisplay, man->theWindow, g->button_x+2, g->button_y+2,
1678 		      g->button_w - 5, g->button_h - 5, context2, context1,
1679 		      relief);
1680   }
1681   else {
1682     RelieveRectangle(theDisplay, man->theWindow, g->button_x, g->button_y,
1683 		      g->button_w - 1, g->button_h - 1, context1, context2,
1684 		      relief);
1685   }
1686 }
1687 
draw_button(WinManager * man,int button,int force)1688 static void draw_button(WinManager *man, int button, int force)
1689 {
1690 	Button *b;
1691 	WinData *win;
1692 	ButtonGeometry g, old_g;
1693 	GC context1 = 0, context2 = 0;
1694 	Contexts button_state;
1695 	int cleared_button = 0, dirty;
1696 	int draw_background = 0, draw_icon = 0, draw_string = 0;
1697 	int clear_old_pic = 0;
1698 
1699 	assert(man);
1700 
1701 	if (!man->window_up)
1702 	{
1703 		ConsoleMessage("draw_button: manager not up yet\n");
1704 		return;
1705 	}
1706 
1707 	b = man->buttons.buttons[button];
1708 	win = b->drawn_state.win;
1709 	dirty = b->drawn_state.dirty_flags;
1710 
1711 	if (win && win->button != b)
1712 	{
1713 		ConsoleMessage("Internal error in draw_button.\n");
1714 		return;
1715 	}
1716 
1717 	if (!win)
1718 	{
1719 		return;
1720 	}
1721 
1722 	if (force || (dirty & REDRAW_BUTTON))
1723 	{
1724 		ConsoleDebug(X11, "draw_button: %d forced\n", b->index);
1725 		draw_background = 1;
1726 		draw_icon = 1;
1727 		draw_string = 1;
1728 	}
1729 
1730 	/* figure out what we have to draw */
1731 	if (dirty)
1732 	{
1733 		ConsoleDebug(X11, "draw_button: %d dirty\n", b->index);
1734 		/* humm ... this should be optimised one day ! */
1735 		if (dirty & GEOMETRY_CHANGED)
1736 		{
1737 			ConsoleDebug(X11, "\tGeometry changed\n");
1738 			/* Determine if geometry has changed relative
1739 			 * to the window gravity */
1740 			if (b->w != b->drawn_state.w ||
1741 			    b->h != b->drawn_state.h ||
1742 			    b->x - man->geometry.gravity_x !=
1743 			    b->drawn_state.x -
1744 			    man->drawn_geometry.gravity_x ||
1745 			    b->y - man->geometry.gravity_y !=
1746 			    b->drawn_state.y -
1747 			    man->drawn_geometry.gravity_y)
1748 			{
1749 				draw_background = 1;
1750 				draw_icon = 1;
1751 				draw_string = 1;
1752 			}
1753 		}
1754 		if (dirty & STATE_CHANGED)
1755 		{
1756 			ConsoleDebug(X11, "\tState changed\n");
1757 			b->drawn_state.state = win->state;
1758 			draw_background = 1;
1759 			draw_icon = 1;
1760 			draw_string = 1;
1761 		}
1762 		if (FMiniIconsSupported && (dirty & PICTURE_CHANGED))
1763 		{
1764 			FvwmPicture tpic;
1765 
1766 			ConsoleDebug(X11, "\tPicture changed\n");
1767 			tpic = win->pic;
1768 			win->pic = win->old_pic;
1769 			get_button_geometry(man, b, &old_g);
1770 			win->pic = tpic;
1771 			b->drawn_state.pic = win->pic;
1772 			draw_icon = 1;
1773 			draw_string = 1;
1774 			draw_background = 1;
1775 		}
1776 		if ((dirty & ICON_STATE_CHANGED) &&
1777 		    b->drawn_state.iconified != win->iconified)
1778 		{
1779 			ConsoleDebug(X11, "\tIcon changed\n");
1780 			b->drawn_state.iconified = win->iconified;
1781 			draw_icon = 1;
1782 			draw_background = 1;
1783 			draw_string = 1;
1784 		}
1785 		if (dirty & STRING_CHANGED)
1786 		{
1787 			ConsoleDebug(
1788 				X11, "\tString changed: %s\n",
1789 				win->display_string);
1790 			copy_string(
1791 				&b->drawn_state.display_string,
1792 				win->display_string);
1793 			assert(b->drawn_state.display_string);
1794 			draw_icon = 1;
1795 			draw_background = 1;
1796 			draw_string = 1;
1797 		}
1798 	}
1799 
1800 	if (draw_background || draw_icon || draw_string)
1801 	{
1802 		XRectangle bounding;
1803 		Region b_region;
1804 
1805 		get_button_geometry(man, b, &g);
1806 		if (b->drawn_state.ew > 0 && b->drawn_state.eh > 0)
1807 		{
1808 			/* we redraw from an expose event */
1809 			bounding.x =  b->drawn_state.ex;
1810 			bounding.y =  b->drawn_state.ey;
1811 			bounding.width =  b->drawn_state.ew;
1812 			bounding.height =  b->drawn_state.eh;
1813 		}
1814 		else
1815 		{
1816 			bounding.x = g.button_x;
1817 			bounding.y = g.button_y;
1818 			bounding.width = g.button_w;
1819 			bounding.height = g.button_h;
1820 		}
1821 		b_region = GetRegion(
1822 			bounding.x, bounding.y, bounding.width, bounding.height);
1823 		ConsoleDebug(
1824 			X11, "\tgeometry: %d %d %d %d\n", g.button_x, g.button_y,
1825 			g.button_w, g.button_h);
1826 		button_state = b->drawn_state.state;
1827 		if (b->drawn_state.iconified && button_state == PLAIN_CONTEXT)
1828 		{
1829 			button_state = ICON_CONTEXT;
1830 		}
1831 		if (draw_background)
1832 		{
1833 			ConsoleDebug(X11, "\tDrawing background\n");
1834 			draw_button_background(man, bounding, button_state);
1835 			cleared_button = 1;
1836 		}
1837 		if (clear_old_pic)
1838 		{
1839 			ConsoleDebug(X11, "\tClearing old picture\n");
1840 			if (!cleared_button)
1841 			{
1842 				XRectangle r;
1843 
1844 				frect_get_intersection(
1845 					bounding.x, bounding.y,
1846 					bounding.width, bounding.height,
1847 					g.icon_x, g.icon_y, g.icon_w, g.icon_h,
1848 					&r);
1849 				if (r.width > 0)
1850 				{
1851 					draw_button_background(
1852 						man, r, button_state);
1853 				}
1854 			}
1855 		}
1856 		if (draw_icon)
1857 		{
1858 			ConsoleDebug(X11, "\tDrawing icon\n");
1859 			iconify_box(
1860 				man, win, button, &g, win->iconified,
1861 				button_state, cleared_button, bounding);
1862 		}
1863 		/* Draw reliefs after icon since the icon might overlay
1864 		   part of it. */
1865 		if (draw_background)
1866 		{
1867 			ConsoleDebug(X11, "\tDrawing reliefs\n");
1868 			if (!PictureUseBWOnly())
1869 			{
1870 				get_gcs(
1871 					man, button_state, win->iconified,
1872 					&context1, &context2);
1873 				draw_relief(
1874 					man, button_state, &g, context1,
1875 					context2);
1876 			}
1877 			else if (button_state == SELECT_CONTEXT ||
1878 				 button_state == FOCUS_SELECT_CONTEXT ||
1879 				 button_state == ICON_SELECT_CONTEXT)
1880 			{
1881 				XDrawRectangle(
1882 					theDisplay, man->theWindow,
1883 					man->hiContext[button_state],
1884 					g.button_x + 2, g.button_y + 1,
1885 					g.button_w - 4, g.button_h - 2);
1886 			}
1887 		}
1888 		if (draw_string)
1889 		{
1890 			Region tr;
1891 
1892 			ConsoleDebug(
1893 				X11, "\tDrawing text: %s\n",
1894 				b->drawn_state.display_string);
1895 
1896 			tr = GetRegion(
1897 				g.text_x, g.text_y, g.text_w, g.text_h);
1898 			XIntersectRegion(tr, b_region, tr);
1899 			XSetRegion(
1900 				theDisplay, man->hiContext[button_state], tr);
1901 			if (!cleared_button)
1902 			{
1903 				XRectangle r;
1904 
1905 				frect_get_intersection(
1906 					bounding.x, bounding.y,
1907 					bounding.width, bounding.height,
1908 					g.text_x, g.text_y, g.text_w, g.text_h,
1909 					&r);
1910 				draw_button_background(man, r, button_state);
1911 			}
1912 			FwinString->str =  b->drawn_state.display_string;
1913 			FwinString->win = man->theWindow;
1914 			FwinString->gc = man->hiContext[button_state];
1915 			if (man->colorsets[button_state] >= 0)
1916 			{
1917 				FwinString->colorset =
1918 					&Colorset[man->colorsets[button_state]];
1919 				FwinString->flags.has_colorset = True;
1920 			}
1921 			else
1922 			{
1923 				FwinString->flags.has_colorset = False;
1924 			}
1925 			FwinString->flags.has_clip_region = True;
1926 			FwinString->clip_region = tr;
1927 			FwinString->x = g.text_x;
1928 			FwinString->y = g.text_base;
1929 			FlocaleDrawString(
1930 				theDisplay, man->FButtonFont, FwinString, 0);
1931 			XDestroyRegion(tr);
1932 			XSetClipMask(
1933 				theDisplay, man->hiContext[button_state], None);
1934 		}
1935 		XDestroyRegion(b_region);
1936 	}
1937 
1938 	b->drawn_state.dirty_flags = 0;
1939 	b->drawn_state.x = b->x;
1940 	b->drawn_state.y = b->y;
1941 	b->drawn_state.w = b->w;
1942 	b->drawn_state.h = b->h;
1943 	b->drawn_state.ew = 0;
1944 	b->drawn_state.eh = 0;
1945 	XFlush(theDisplay);
1946 }
1947 
draw_managers(void)1948 void draw_managers(void)
1949 {
1950   int i;
1951   for (i = 0; i < globals.num_managers; i++)
1952     draw_manager(&globals.managers[i]);
1953 }
1954 
draw_empty_manager(WinManager * man)1955 static void draw_empty_manager(WinManager *man)
1956 {
1957 	GC context1 = 0, context2 = 0;
1958 	int state = TITLE_CONTEXT;
1959 	ButtonGeometry g;
1960 	int len = strlen(man->titlename);
1961 	Region region;
1962 
1963 	ConsoleDebug(X11, "draw_empty_manager\n");
1964 	clear_empty_region(man);
1965 	get_title_geometry(man, &g);
1966 
1967 	/* FIXME: should bound when exposed */
1968 	if (len > 0)
1969 	{
1970 		XRectangle r;
1971 
1972 		r.x = g.button_x;
1973 		r.y = g.button_y;
1974 		r.width = g.button_w;
1975 		r.height = g.button_h;
1976 		draw_button_background(man, r, state);
1977 	}
1978 	if (!PictureUseBWOnly())
1979 	{
1980 		get_gcs(man, state, 0, &context1, &context2);
1981 		draw_relief(man, state, &g, context1, context2);
1982 	}
1983 	region = GetRegion(g.text_x, g.text_y, g.text_w, g.text_h);
1984 	XSetRegion(theDisplay, man->hiContext[state], region);
1985 	FwinString->str = man->titlename;
1986 	FwinString->win = man->theWindow;
1987 	FwinString->gc = man->hiContext[state];
1988 	if (man->colorsets[state] >= 0)
1989 	{
1990 		FwinString->colorset = &Colorset[man->colorsets[state]];
1991 		FwinString->flags.has_colorset = True;
1992 	}
1993 	else
1994 	{
1995 		FwinString->flags.has_colorset = False;
1996 	}
1997 	FwinString->flags.has_clip_region = True;
1998 	FwinString->clip_region = region;
1999 	FwinString->x = g.text_x;
2000 	FwinString->y = g.text_base;
2001 	FlocaleDrawString(theDisplay, man->FButtonFont, FwinString, 0);
2002 	XSetClipMask(theDisplay, man->hiContext[state], None);
2003 }
2004 
draw_manager(WinManager * man)2005 void draw_manager(WinManager *man)
2006 {
2007   int i, force_draw = 0, update_geometry = 0, redraw_all = 0;
2008   int shape_changed = 0;
2009 
2010   assert(man->buttons.num_buttons >= 0 && man->buttons.num_windows >= 0);
2011 
2012   if (!man->window_up)
2013     return;
2014   ConsoleDebug(X11, "Drawing Manager: %s\n", man->titlename);
2015   redraw_all = (man->dirty_flags & REDRAW_MANAGER);
2016 
2017   if (!man->flags.is_shaded && man->flags.needs_resize_after_unshade) {
2018     ConsoleDebug(X11, "\tresizing manager\n");
2019     resize_manager(man, 1);
2020     update_geometry = 1;
2021     force_draw = 1;
2022   } else if (redraw_all || (man->buttons.dirty_flags & NUM_WINDOWS_CHANGED)) {
2023     ConsoleDebug(X11, "\tresizing manager\n");
2024     resize_manager(man, redraw_all);
2025     update_geometry = 1;
2026     force_draw = 1;
2027   }
2028 
2029   if (redraw_all || (man->dirty_flags & MAPPING_CHANGED)) {
2030     force_draw = 1;
2031     ConsoleDebug(X11, "manager %s: mapping changed\n", man->titlename);
2032   }
2033 
2034   if (FShapesSupported && man->shaped)
2035   {
2036     if (redraw_all || (man->dirty_flags & SHAPE_CHANGED)) {
2037       FShapeCombineRectangles(
2038 	theDisplay, man->theWindow, FShapeBounding, 0, 0, man->shape.rects,
2039 	man->shape.num_rects, FShapeSet, Unsorted);
2040       FShapeCombineRectangles(
2041 	theDisplay, man->theWindow, FShapeClip, 0, 0, man->shape.rects,
2042 	man->shape.num_rects, FShapeSet, Unsorted);
2043       shape_changed = 1;
2044       update_geometry = 1;
2045     }
2046   }
2047 
2048   if (redraw_all || (man->dirty_flags & GEOMETRY_CHANGED)) {
2049     ConsoleDebug(X11, "\tredrawing all buttons\n");
2050     update_geometry = 1;
2051   }
2052 
2053   if (update_geometry) {
2054     for (i = 0; i < man->buttons.num_windows; i++)
2055       set_button_geometry(man, man->buttons.buttons[i]);
2056   }
2057 
2058   if (force_draw || update_geometry || (man->dirty_flags & REDRAW_BG)) {
2059     ConsoleDebug(X11, "\tredrawing background\n");
2060     clear_empty_region(man);
2061   }
2062   if (force_draw || update_geometry)
2063   {
2064     /* FIXME: maybe not useful but safe */
2065     if (CSET_IS_TRANSPARENT_PR_PURE(man->colorsets[DEFAULT]))
2066       force_draw = True;
2067   }
2068 
2069   man->dirty_flags = 0;
2070   man->buttons.dirty_flags = 0;
2071   man->buttons.drawn_num_buttons = man->buttons.num_buttons;
2072   man->buttons.drawn_num_windows = man->buttons.num_windows;
2073 
2074   if (man->buttons.num_windows == 0) {
2075     if (force_draw)
2076       draw_empty_manager(man);
2077   }
2078   else {
2079     /* I was having the problem where when the shape changed the manager
2080        wouldn't get redrawn. It appears we weren't getting the expose.
2081        How can I tell when I am going to reliably get an expose event? */
2082 
2083     if (1 || shape_changed) {
2084       /* if shape changed, we'll catch it on the expose */
2085       for (i = 0; i < man->buttons.num_buttons; i++) {
2086 	draw_button(man, i, force_draw);
2087       }
2088     }
2089   }
2090   man->drawn_geometry = man->geometry;
2091   XFlush(theDisplay);
2092 }
2093 
draw_transparent_buttons(WinManager * man,Bool only_moved,Bool clear_only)2094 Bool draw_transparent_buttons(
2095 	WinManager *man, Bool only_moved, Bool clear_only)
2096 {
2097 	Button **bp;
2098 	Contexts button_state;
2099 	int i,cset;
2100 	Bool r = False;
2101 	Bool man_bg_transparent = False;
2102 
2103 	if (CSET_IS_TRANSPARENT(man->colorsets[DEFAULT]))
2104 	{
2105 		clear_empty_region(man);
2106 		man_bg_transparent = True;
2107 	}
2108 
2109 	if (!man->buttons.num_windows)
2110 	{
2111 		if (CSET_IS_TRANSPARENT(man->colorsets[TITLE_CONTEXT]) ||
2112 		    CSET_IS_TRANSPARENT_PR_TINT(man->colorsets[DEFAULT]))
2113 		{
2114 			draw_empty_manager(man);
2115 		}
2116 		return 0;
2117 	}
2118 
2119 	bp = man->buttons.buttons;
2120 	for (i = 0; i < man->buttons.num_windows; i++)
2121 	{
2122 		button_state = bp[i]->drawn_state.state;
2123 		if (bp[i]->drawn_state.iconified &&
2124 		    button_state == PLAIN_CONTEXT)
2125 		{
2126 			button_state = ICON_CONTEXT;
2127 		}
2128 		cset = man->colorsets[button_state];
2129 
2130 		if ((!CSET_IS_TRANSPARENT(cset) &&
2131 		     !CSET_IS_TRANSPARENT_PR_TINT(man->colorsets[DEFAULT]))
2132 		    ||
2133 		    (only_moved && !man_bg_transparent &&
2134 		     !CSET_IS_TRANSPARENT_ROOT(cset)))
2135 		{
2136 			continue;
2137 		}
2138 		if (clear_only)
2139 		{
2140 			XClearArea(
2141 				theDisplay, man->theWindow,
2142 				bp[i]->x, bp[i]->y, bp[i]->w, bp[i]->h,
2143 				True);
2144 			r = True;
2145 		}
2146 		else
2147 		{
2148 			bp[i]->drawn_state.dirty_flags |= REDRAW_BUTTON;
2149 			draw_button(man, i, 0);
2150 		}
2151 	}
2152 	return r;
2153 }
2154 
compute_weight(WinData * win)2155 static int compute_weight(WinData *win)
2156 {
2157   WinManager *man;
2158   WeightedSort *sort;
2159   int i;
2160 
2161   man = win->manager;
2162   for (i = 0; i < man->weighted_sorts_len; i++) {
2163     sort = &man->weighted_sorts[i];
2164     if (sort->resname && !matchWildcards(sort->resname, win->resname)) {
2165       continue;
2166     }
2167     if (sort->classname && !matchWildcards(sort->classname, win->classname)) {
2168       continue;
2169     }
2170     if (sort->titlename && !matchWildcards(sort->titlename, win->titlename)) {
2171       continue;
2172     }
2173     if (sort->iconname && !matchWildcards(sort->iconname, win->iconname)) {
2174       continue;
2175     }
2176     return sort->weight;
2177   }
2178   return 0;
2179 }
2180 
compare_windows(SortType type,WinData * a,WinData * b)2181 static int compare_windows(SortType type, WinData *a, WinData *b)
2182 {
2183 	int wa, wb;
2184 
2185 	if (type == SortId)
2186 	{
2187 		return a->app_id - b->app_id;
2188 	}
2189 	else if (type == SortName)
2190 	{
2191 		return strcasecmp(
2192 			(a->display_string)? a->display_string:"",
2193 			(b->display_string)? b->display_string:"");
2194 	}
2195 	else if (type == SortNameCase)
2196 	{
2197 		return strcmp((a->display_string)? a->display_string:"",
2198 			       (b->display_string)? b->display_string:"");
2199 	}
2200 	else if (type == SortWeighted)
2201 	{
2202 		wa = compute_weight(a);
2203 		wb = compute_weight(b);
2204 		if (wa != wb)
2205 		{
2206 			return wa - wb;
2207 		}
2208 		return strcmp((a->display_string)? a->display_string:"",
2209 			      (b->display_string)? b->display_string:"");
2210 	}
2211 	else
2212 	{
2213 		ConsoleMessage("Internal error in compare_windows\n");
2214 		return 0;
2215 	}
2216 }
2217 
2218 /* find_windows_spot: returns index of button to stick the window in.
2219  *                    checks win->button to see if it's already in manager.
2220  *                    if it isn't, then gives spot at which it should be,
2221  *                    if it were.
2222  */
2223 
find_windows_spot(WinData * win)2224 static int find_windows_spot(WinData *win)
2225 {
2226   WinManager *man = win->manager;
2227   int num_windows = man->buttons.num_windows;
2228 
2229   if (man->sort != SortNone) {
2230     int i, cur, start, finish, cmp_dir, correction;
2231     Button **bp;
2232 
2233     bp = man->buttons.buttons;
2234     if (win->button) {
2235       /* start search from our current location */
2236       cur = win->button->index;
2237 
2238       if (cur - 1 >= 0 &&
2239 	  compare_windows(man->sort,
2240 			   win, bp[cur - 1]->drawn_state.win) < 0) {
2241 	start = cur - 1;
2242 	finish = -1;
2243 	cmp_dir = -1;
2244 	correction = 1;
2245       }
2246       else if (cur < num_windows - 1 &&
2247 	       compare_windows(man->sort,
2248 				win, bp[cur + 1]->drawn_state.win) > 0) {
2249 	start = cur + 1;
2250 	finish = num_windows;
2251 	cmp_dir = 1;
2252 	correction = -1;
2253       }
2254       else {
2255 	return cur;
2256       }
2257     }
2258     else {
2259       start = 0;
2260       finish = num_windows;
2261       cmp_dir = 1;
2262       correction = 0;
2263     }
2264     for (i = start; i != finish && bp[i]->drawn_state.win && cmp_dir *
2265 	     compare_windows(man->sort, win, bp[i]->drawn_state.win) > 0;
2266 	 i = i + cmp_dir)
2267       ;
2268     i += correction;
2269     ConsoleDebug(X11, "find_windows_spot: %s %d\n", win->display_string, i);
2270     return i;
2271   }
2272   else {
2273     if (win->button) {
2274       /* already have a perfectly fine spot */
2275       return win->button->index;
2276     }
2277     else {
2278       return num_windows;
2279     }
2280   }
2281 
2282   /* shouldn't get here */
2283   return -1;
2284 }
2285 
move_window_buttons(WinManager * man,int start,int finish,int offset)2286 static void move_window_buttons(WinManager *man, int start, int finish,
2287 				 int offset)
2288 {
2289   int n = man->buttons.num_buttons, i;
2290   Button **bp;
2291 
2292   ConsoleDebug(X11, "move_window_buttons: %s(%d): (%d, %d) + %d\n",
2293 		man->titlename, n, start, finish, offset);
2294 
2295   if (finish >= n || finish + offset >= n || start < 0 || start + offset < 0) {
2296     ConsoleMessage("Internal error in move_window_buttons\n");
2297     ConsoleMessage("\tn = %d, start = %d, finish = %d, offset = %d\n",
2298 		    n, start, finish, offset);
2299     return;
2300   }
2301   bp = man->buttons.buttons;
2302   if (offset > 0)
2303   {
2304     for (i = finish; i >= start; i--)
2305     {
2306       bp[i + offset]->drawn_state = bp[i]->drawn_state;
2307       bp[i + offset]->drawn_state.dirty_flags = ALL_CHANGED;
2308       if (bp[i + offset]->drawn_state.win)
2309 	bp[i + offset]->drawn_state.win->button = bp[i + offset];
2310     }
2311     for (i = 0; i < offset; i++)
2312       clear_button(bp[start + i]);
2313   }
2314   else if (offset < 0)
2315   {
2316     for (i = start; i <= finish; i++)
2317     {
2318       bp[i + offset]->drawn_state = bp[i]->drawn_state;
2319       bp[i + offset]->drawn_state.dirty_flags = ALL_CHANGED;
2320       if (bp[i + offset]->drawn_state.win)
2321 	bp[i + offset]->drawn_state.win->button = bp[i + offset];
2322     }
2323     for (i = 0; i > offset; i--)
2324       clear_button(bp[finish + i]);
2325   }
2326 }
2327 
insert_windows_button(WinData * win)2328 static void insert_windows_button(WinData *win)
2329 {
2330   int spot;
2331   int selected_index = -1;
2332   WinManager *man = win->manager;
2333   ButtonArray *buttons;
2334 
2335   ConsoleDebug(X11, "insert_windows_button: %s\n", win->titlename);
2336 
2337   assert(man);
2338   selected_index = selected_button_in_man(man);
2339 
2340   if (win->button) {
2341     ConsoleDebug(X11, "insert_windows_button: POSSIBLE BUG: "
2342 		  "already have a button\n");
2343     return;
2344   }
2345 
2346   if (!win || !win->complete || !man) {
2347     ConsoleMessage("Internal error in insert_windows_button\n");
2348     ShutMeDown(1);
2349   }
2350 
2351   buttons = &man->buttons;
2352 
2353   spot = find_windows_spot(win);
2354 
2355   increase_num_windows(buttons, 1);
2356   move_window_buttons(man, spot, buttons->num_windows - 2, 1);
2357 
2358   set_window_button(win, spot);
2359   if (selected_index >= 0) {
2360     ConsoleDebug(X11, "insert_windows_button: selected_index = %d, moving\n",
2361 		  selected_index);
2362     move_highlight(man, man->buttons.buttons[selected_index]);
2363   }
2364 }
2365 
delete_windows_button(WinData * win)2366 void delete_windows_button(WinData *win)
2367 {
2368   int spot;
2369   int selected_index = -1;
2370   WinManager *man = (win->manager);
2371   ButtonArray *buttons;
2372 
2373   ConsoleDebug(X11, "delete_windows_button: %s\n", win->titlename);
2374 
2375   assert(man);
2376 
2377   buttons = &win->manager->buttons;
2378 
2379   assert(win->button);
2380   assert(buttons->buttons);
2381 
2382   selected_index = selected_button_in_man(man);
2383   ConsoleDebug(X11, "delete_windows_button: selected_index = %d\n",
2384 		selected_index);
2385 
2386   spot = win->button->index;
2387 
2388   tips_cancel(man);
2389   move_window_buttons(win->manager, spot + 1, buttons->num_windows - 1, -1);
2390   increase_num_windows(buttons, -1);
2391   win->button = NULL;
2392   if (globals.focus_win == win) {
2393     globals.focus_win = NULL;
2394   }
2395   if (selected_index >= 0) {
2396     ConsoleDebug(X11, "delete_windows_button: selected_index = %d, moving\n",
2397 		  selected_index);
2398     move_highlight(man, man->buttons.buttons[selected_index]);
2399   }
2400   if (win->iconified)
2401   {
2402     win->state = ICON_CONTEXT;
2403   }
2404   else
2405   {
2406     win->state = PLAIN_CONTEXT;
2407   }
2408 }
2409 
resort_windows_button(WinData * win)2410 void resort_windows_button(WinData *win)
2411 {
2412   int new_spot, cur_spot;
2413   int selected_index = -1;
2414   WinManager *man = win->manager;
2415 
2416   assert(win->button && man);
2417 
2418   ConsoleDebug(X11, "In resort_windows_button: %s\n", win->resname);
2419 
2420   selected_index = selected_button_in_man(man);
2421 
2422   new_spot = find_windows_spot(win);
2423   cur_spot = win->button->index;
2424 
2425   print_button_info(win->button);
2426 
2427   if (new_spot != cur_spot) {
2428     ConsoleDebug(X11, "resort_windows_button: win moves from %d to %d\n",
2429 		    cur_spot, new_spot);
2430     if (new_spot < cur_spot) {
2431       move_window_buttons(man, new_spot, cur_spot - 1, +1);
2432     }
2433     else {
2434       move_window_buttons(man, cur_spot + 1, new_spot, -1);
2435     }
2436     set_window_button(win, new_spot);
2437 
2438     if (selected_index >= 0) {
2439       move_highlight(man, man->buttons.buttons[selected_index]);
2440     }
2441   }
2442 }
2443 
move_highlight(WinManager * man,Button * b)2444 void move_highlight(WinManager *man, Button *b)
2445 {
2446   WinData *old;
2447 
2448   assert(man);
2449 
2450   ConsoleDebug(X11, "move_highlight\n");
2451 
2452   old = globals.select_win;
2453 
2454   if (old && old->button) {
2455     del_win_state(old, SELECT_CONTEXT);
2456     old->manager->select_button = NULL;
2457     draw_button(old->manager, old->button->index, 0);
2458   }
2459   if (b && b->drawn_state.win) {
2460     add_win_state(b->drawn_state.win, SELECT_CONTEXT);
2461     draw_button(man, b->index, 0);
2462     globals.select_win = b->drawn_state.win;
2463   }
2464   else {
2465     globals.select_win = NULL;
2466   }
2467 
2468   man->select_button = b;
2469 }
2470 
man_exposed(WinManager * man,XEvent * theEvent)2471 void man_exposed(WinManager *man, XEvent *theEvent)
2472 {
2473 	rectangle r1, r2;
2474 	int i;
2475 	Button **bp;
2476 
2477 	ConsoleDebug(X11, "manager: %s, got expose\n", man->titlename);
2478 
2479 	r1.x = theEvent->xexpose.x;
2480 	r1.y = theEvent->xexpose.y;
2481 	r1.width = theEvent->xexpose.width;
2482 	r1.height = theEvent->xexpose.height;
2483 
2484 	r2.width = man->geometry.boxwidth;
2485 	r2.height = man->geometry.boxheight;
2486 
2487 	bp = man->buttons.buttons;
2488 
2489 	/* Background must be redrawn. */
2490 	/* olicha: I do not think so */
2491 	/*man->dirty_flags |= REDRAW_BG;*/
2492 
2493 	if (0 && FHaveShapeExtension && man->shaped)
2494 	{
2495 		/* There's some weird problem where if we change window shapes,
2496 		 * we can't draw into buttons in the area NewShape intersect
2497 		 * (not OldShape) until we get our Expose event. So, for now,
2498 		 * just redraw everything when we get Expose events. This has
2499 		 * the disadvantage of drawing buttons twice, but avoids having
2500 		 * to match which expose event results from which shape change.*/
2501 		/* seems fixed ? olicha */
2502 		if (man->buttons.num_windows)
2503 		{
2504 			for (i = 0; i < man->buttons.num_windows; i++)
2505 			{
2506 				bp[i]->drawn_state.dirty_flags |= REDRAW_BUTTON;
2507 			}
2508 		}
2509 		else
2510 		{
2511 			draw_empty_manager(man);
2512 		}
2513 
2514 		return;
2515 	}
2516 
2517 	if (CSET_IS_TRANSPARENT_PR(man->colorsets[DEFAULT]))
2518 	{
2519 		clear_empty_region(man);
2520 	}
2521 	if (man->buttons.num_windows)
2522 	{
2523 		for (i = 0; i < man->buttons.num_windows; i++)
2524 		{
2525 			r2.x = index_to_col(man, i) * r2.width;
2526 			r2.y = index_to_row(man, i) * r2.height;
2527 			if (fvwmrect_do_rectangles_intersect(
2528 				&r1, &r2))
2529 			{
2530 				bp[i]->drawn_state.ex = max(r1.x,r2.x);
2531 				bp[i]->drawn_state.ey = max(r1.y,r2.y);
2532 				bp[i]->drawn_state.ew =
2533 					min(r1.x+r1.width,r2.x+r2.width) -
2534 						max(r1.x,r2.x);
2535 				bp[i]->drawn_state.eh =
2536 					min(r1.y+r1.height, r2.y+r2.height) -
2537 						max(r1.y,r2.y);
2538 				bp[i]->drawn_state.dirty_flags |= REDRAW_BUTTON;
2539 			}
2540 		}
2541 	}
2542 	else
2543 	{
2544 		draw_empty_manager(man);
2545 	}
2546 }
2547 
2548 /*
2549  * tips routine
2550  */
2551 
get_tips(WinManager * man,Button * b)2552 static char *get_tips(WinManager *man, Button *b)
2553 {
2554 	ButtonGeometry g;
2555 	char *s;
2556 	static char *free_str = NULL;
2557 
2558 	if (man->tips == TIPS_NEVER || b == NULL)
2559 	{
2560 		return NULL;
2561 	}
2562 
2563 	if (free_str != NULL)
2564 	{
2565 		free(free_str);
2566 		free_str = NULL;
2567 	}
2568 
2569 	if (man->tips_formatstring && b->drawn_state.win)
2570 	{
2571 		CopyString(
2572 			&s, make_display_string(
2573 				b->drawn_state.win, man->tips_formatstring,
2574 				0));
2575 		free_str = s;
2576 	}
2577 	else
2578 	{
2579 		s = b->drawn_state.display_string;
2580 	}
2581 
2582 	if (s == NULL)
2583 	{
2584 		return NULL;
2585 	}
2586 
2587 	if (man->tips == TIPS_ALWAYS)
2588 	{
2589 		return s;
2590 	}
2591 
2592 	/* TIPS_NEEDED */
2593 	if (free_str != NULL &&
2594 	    (b->drawn_state.display_string == NULL ||
2595 	     strcmp(s, b->drawn_state.display_string)))
2596 	{
2597 		return s;
2598 	}
2599 
2600 	get_button_geometry(man, b, &g);
2601 
2602 	if (g.text_x + FlocaleTextWidth(man->FButtonFont, s, strlen(s))
2603 	    > g.button_x + g.button_w - 4)
2604 	{
2605 		return s;
2606 	}
2607 
2608 	return NULL;
2609 }
2610 
tips_cancel(WinManager * man)2611 void tips_cancel(WinManager *man)
2612 {
2613 	int j;
2614 
2615 	if (man)
2616 	{
2617 		man->tipped_button = NULL;
2618 	}
2619 	else
2620 	{
2621 		for (j = 0; j < globals.num_managers; j++)
2622 		{
2623 			man = &globals.managers[j];
2624 			man->tipped_button = NULL;
2625 		}
2626 	}
2627 	FTipsCancel(theDisplay);
2628 }
2629 
tips_on(WinManager * man,Button * b)2630 void tips_on(WinManager *man, Button *b)
2631 {
2632 	char *tips_str;
2633 
2634 	if ((tips_str = get_tips(man, b)) != NULL)
2635 	{
2636 		 FTipsOn(
2637 			 theDisplay, man->theWindow, man->tips_conf, (void *)b,
2638 			 tips_str, b->x, b->y, b->w, b->h);
2639 		 man->tipped_button = b;
2640 	}
2641 	else
2642 	{
2643 		tips_cancel(man);
2644 	}
2645 }
2646 
tips_update_label(WinManager * man)2647 void tips_update_label(WinManager *man)
2648 {
2649 	char *tips_str;
2650 
2651 	if ((tips_str = get_tips(man, man->tipped_button)) != NULL)
2652 	{
2653 		 FTipsUpdateLabel(theDisplay, tips_str);
2654 	}
2655 	else
2656 	{
2657 		tips_cancel(man);
2658 	}
2659 }
2660 
2661 
2662 /*
2663  * Debugging routines
2664  */
2665 
check_managers_consistency(void)2666 void check_managers_consistency(void)
2667 {
2668 #ifdef FVWM_DEBUG_MSGS
2669   int i, j;
2670   Button **b;
2671 
2672   for (i = 0; i < globals.num_managers; i++) {
2673     for (j = 0, b = globals.managers[i].buttons.buttons;
2674 	 j < globals.managers[i].buttons.num_buttons; j++, b++) {
2675       if ((*b)->drawn_state.win && (*b)->drawn_state.win->button != *b) {
2676 	ConsoleMessage("manager %d, button %d is confused\n", i, j);
2677 	abort();
2678       }
2679       else if ((*b)->drawn_state.win &&
2680 	       j >= globals.managers[i].buttons.num_windows) {
2681 	ConsoleMessage("manager %d: button %d has window and shouldn't\n",
2682 			i, j);
2683 	abort();
2684       }
2685     }
2686   }
2687 #endif
2688 }
2689 
print_button_info(Button * b)2690 static void print_button_info(Button *b)
2691 {
2692 #ifdef FVWM_DEBUG_MSGS
2693   ConsoleMessage("button: %d\n", b->index);
2694   ConsoleMessage("win: 0x%x\n", (unsigned int) b->drawn_state.win);
2695   ConsoleMessage("dirty: 0x%x\n", b->drawn_state.dirty_flags);
2696   if (b->drawn_state.win) {
2697     ConsoleMessage("name: %s\n", b->drawn_state.display_string);
2698     ConsoleMessage("iconified: %d state %d\n", b->drawn_state.iconified,
2699 		    b->drawn_state.state);
2700     ConsoleMessage("win->button: 0x%x\n",
2701 		    (unsigned int) b->drawn_state.win->button);
2702   }
2703 #endif
2704 }
2705