]> icculus.org git repositories - mikachu/openbox.git/blob - src/Window.cc
support the blackbox hints right and the blackbox attributes for decoration state
[mikachu/openbox.git] / src / Window.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Window.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh at debian.org>
4 // Copyright (c) 1997 - 2000, 2002 Brad Hughes <bhughes at trolltech.com>
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <X11/Xatom.h>
30 #include <X11/keysym.h>
31
32 #ifdef HAVE_STRING_H
33 #  include <string.h>
34 #endif // HAVE_STRING_H
35
36 #ifdef    DEBUG
37 #  ifdef    HAVE_STDIO_H
38 #    include <stdio.h>
39 #  endif // HAVE_STDIO_H
40 #endif // DEBUG
41
42 #ifdef HAVE_STDLIB_H
43    #include <stdlib.h>
44 #endif // HAVE_STDLIB_H
45 }
46
47 #include "i18n.hh"
48 #include "blackbox.hh"
49 #include "Clientmenu.hh"
50 #include "Font.hh"
51 #include "GCCache.hh"
52 #include "Iconmenu.hh"
53 #include "Image.hh"
54 #include "Screen.hh"
55 #include "Toolbar.hh"
56 #include "Util.hh"
57 #include "Window.hh"
58 #include "Windowmenu.hh"
59 #include "Workspace.hh"
60 #include "Slit.hh"
61
62 using std::string;
63 using std::abs;
64
65 // change this to change what modifier keys openbox uses for mouse bindings
66 // for example: Mod1Mask | ControlMask
67 //          or: ControlMask| ShiftMask
68 const unsigned int ModMask = Mod1Mask;
69
70 /*
71  * Initializes the class with default values/the window's set initial values.
72  */
73 BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
74   // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
75   // sizeof(BlackboxWindow));
76
77 #ifdef    DEBUG
78   fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
79 #endif // DEBUG
80
81   /*
82     set timer to zero... it is initialized properly later, so we check
83     if timer is zero in the destructor, and assume that the window is not
84     fully constructed if timer is zero...
85   */
86   timer = 0;
87   blackbox = b;
88   client.window = w;
89   screen = s;
90   xatom = blackbox->getXAtom();
91
92   if (! validateClient()) {
93     delete this;
94     return;
95   }
96
97   // fetch client size and placement
98   XWindowAttributes wattrib;
99   if (! XGetWindowAttributes(blackbox->getXDisplay(),
100                              client.window, &wattrib) ||
101       ! wattrib.screen || wattrib.override_redirect) {
102 #ifdef    DEBUG
103     fprintf(stderr,
104             "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
105 #endif // DEBUG
106
107     delete this;
108     return;
109   }
110
111   // set the eventmask early in the game so that we make sure we get
112   // all the events we are interested in
113   XSetWindowAttributes attrib_set;
114   attrib_set.event_mask = PropertyChangeMask | FocusChangeMask |
115                           StructureNotifyMask;
116   attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
117                                      ButtonMotionMask;
118   XChangeWindowAttributes(blackbox->getXDisplay(), client.window,
119                           CWEventMask|CWDontPropagate, &attrib_set);
120
121   flags.moving = flags.resizing = flags.shaded = flags.visible =
122     flags.iconic = flags.focused = flags.stuck = flags.modal =
123     flags.send_focus_message = flags.shaped = flags.skip_taskbar =
124     flags.skip_pager = flags.fullscreen = False;
125   flags.maximized = 0;
126
127   blackbox_attrib.workspace = window_number = BSENTINEL;
128
129   blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack = 0l;
130   blackbox_attrib.decoration = DecorNormal;
131   blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
132   blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
133
134   frame.border_w = 1;
135   frame.window = frame.plate = frame.title = frame.handle = None;
136   frame.close_button = frame.iconify_button = frame.maximize_button = None;
137   frame.right_grip = frame.left_grip = None;
138
139   frame.ulabel_pixel = frame.flabel_pixel = frame.utitle_pixel =
140   frame.ftitle_pixel = frame.uhandle_pixel = frame.fhandle_pixel =
141     frame.ubutton_pixel = frame.fbutton_pixel = frame.pbutton_pixel =
142     frame.uborder_pixel = frame.fborder_pixel = frame.ugrip_pixel =
143     frame.fgrip_pixel = 0;
144   frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
145   frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
146   frame.pbutton = frame.ugrip = frame.fgrip = None;
147
148   functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
149   mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
150                     Decor_Iconify | Decor_Maximize | Decor_Close;
151
152   client.normal_hint_flags = 0;
153   client.window_group = None;
154   client.transient_for = 0;
155
156   current_state = NormalState;
157
158   windowmenu = 0;
159
160   /*
161     get the initial size and location of client window (relative to the
162     _root window_). This position is the reference point used with the
163     window's gravity to find the window's initial position.
164   */
165   client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
166   client.old_bw = wattrib.border_width;
167
168   lastButtonPressTime = 0;
169
170   timer = new BTimer(blackbox, this);
171   timer->setTimeout(blackbox->getAutoRaiseDelay());
172
173   // get size, aspect, minimum/maximum size and other hints set by the
174   // client
175
176   if (! getBlackboxHints())
177     getNetWMHints();
178
179   getWMProtocols();
180   getWMHints();
181   getWMNormalHints();
182
183   frame.window = createToplevelWindow();
184
185   blackbox->saveWindowSearch(frame.window, this);
186   
187   frame.plate = createChildWindow(frame.window);
188   blackbox->saveWindowSearch(frame.plate, this);
189
190   // determine if this is a transient window
191   getTransientInfo();
192
193   // determine the window's type, so we can decide its decorations and
194   // functionality, or if we should not manage it at all
195   if (getWindowType()) {
196     // adjust the window decorations/behavior based on the window type
197     switch (window_type) {
198     case Type_Desktop:
199     case Type_Dock:
200     case Type_Menu:
201       blackbox_attrib.workspace = 0;  // we do need to belong to a workspace
202       flags.stuck = True;             // we show up on all workspaces
203     case Type_Splash:
204       // none of these windows are manipulated by the window manager
205       functions = 0;
206       break;
207
208     case Type_Toolbar:
209     case Type_Utility:
210       // these windows get less functionality
211       functions &= ~(Func_Maximize | Func_Resize | Func_Iconify);
212       break;
213
214     case Type_Dialog:
215       // dialogs cannot be maximized
216       functions &= ~Func_Maximize;
217       break;
218
219     case Type_Normal:
220       // normal windows retain all of the possible decorations and functionality
221       break;
222     }
223   } else {
224     getMWMHints();
225   }
226   
227   // further adjeust the window's decorations/behavior based on window sizes
228   if ((client.normal_hint_flags & PMinSize) &&
229       (client.normal_hint_flags & PMaxSize) &&
230       client.max_width <= client.min_width &&
231       client.max_height <= client.min_height) {
232     functions &= ~(Func_Resize | Func_Maximize);
233   }
234   
235   setAllowedActions();
236
237   setupDecor();
238   
239   if (decorations & Decor_Titlebar)
240     createTitlebar();
241
242   if (decorations & Decor_Handle)
243     createHandle();
244
245   // apply the size and gravity hint to the frame
246
247   upsize();
248
249   bool place_window = True;
250   if (blackbox->isStartup() || isTransient() ||
251       client.normal_hint_flags & (PPosition|USPosition)) {
252     applyGravity(frame.rect);
253
254     if (blackbox->isStartup() || client.rect.intersects(screen->getRect()))
255       place_window = False;
256   }
257
258   // add the window's strut. note this is done *after* placing the window.
259   screen->addStrut(&client.strut);
260   updateStrut();
261   
262   /*
263     the server needs to be grabbed here to prevent client's from sending
264     events while we are in the process of configuring their window.
265     We hold the grab until after we are done moving the window around.
266   */
267
268   XGrabServer(blackbox->getXDisplay());
269
270   associateClientWindow();
271
272   blackbox->saveWindowSearch(client.window, this);
273
274   if (blackbox_attrib.workspace >= screen->getWorkspaceCount())
275     screen->getCurrentWorkspace()->addWindow(this, place_window);
276   else
277     screen->getWorkspace(blackbox_attrib.workspace)->
278       addWindow(this, place_window);
279
280   if (! place_window) {
281     // don't need to call configure if we are letting the workspace
282     // place the window
283     configure(frame.rect.x(), frame.rect.y(),
284               frame.rect.width(), frame.rect.height());
285
286   }
287
288   positionWindows();
289
290   XUngrabServer(blackbox->getXDisplay());
291
292 #ifdef    SHAPE
293   if (blackbox->hasShapeExtensions() && flags.shaped)
294     configureShape();
295 #endif // SHAPE
296
297   // now that we know where to put the window and what it should look like
298   // we apply the decorations
299   decorate();
300
301   grabButtons();
302
303   XMapSubwindows(blackbox->getXDisplay(), frame.window);
304
305   // this ensures the title, buttons, and other decor are properly displayed
306   redrawWindowFrame();
307
308   // preserve the window's initial state on first map, and its current state
309   // across a restart
310   unsigned long initial_state = current_state;
311   if (! getState())
312     current_state = initial_state;
313
314   // get sticky state from our parent window if we've got one
315   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
316       client.transient_for->isStuck() != flags.stuck)
317     flags.stuck = True;
318
319   if (flags.shaded) {
320     flags.shaded = False;
321     initial_state = current_state;
322     shade();
323
324     /*
325       At this point in the life of a window, current_state should only be set
326       to IconicState if the window was an *icon*, not if it was shaded.
327     */
328     if (initial_state != IconicState)
329       current_state = NormalState;
330   }
331
332   if (flags.stuck) {
333     flags.stuck = False;
334     stick();
335   }
336
337   if (flags.maximized && (functions & Func_Maximize))
338     remaximize();
339
340   // create this last so it only needs to be configured once
341   windowmenu = new Windowmenu(this);
342 }
343
344
345 BlackboxWindow::~BlackboxWindow(void) {
346 #ifdef    DEBUG
347   fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
348           client.window);
349 #endif // DEBUG
350
351   if (! timer) // window not managed...
352     return;
353
354   if (flags.moving)
355     endMove();
356
357   screen->removeStrut(&client.strut);
358   screen->updateAvailableArea();
359
360   // We don't need to worry about resizing because resizing always grabs the X
361   // server. This should only ever happen if using opaque moving.
362   if (flags.moving)
363     endMove();
364
365   delete timer;
366
367   delete windowmenu;
368
369   if (client.window_group) {
370     BWindowGroup *group = blackbox->searchGroup(client.window_group);
371     if (group) group->removeWindow(this);
372   }
373
374   // remove ourselves from our transient_for
375   if (isTransient()) {
376     if (client.transient_for != (BlackboxWindow *) ~0ul)
377       client.transient_for->client.transientList.remove(this);
378     client.transient_for = (BlackboxWindow*) 0;
379   }
380
381   if (client.transientList.size() > 0) {
382     // reset transient_for for all transients
383     BlackboxWindowList::iterator it, end = client.transientList.end();
384     for (it = client.transientList.begin(); it != end; ++it)
385       (*it)->client.transient_for = (BlackboxWindow*) 0;
386   }
387
388   if (frame.title)
389     destroyTitlebar();
390
391   if (frame.handle)
392     destroyHandle();
393
394   if (frame.plate) {
395     blackbox->removeWindowSearch(frame.plate);
396     XDestroyWindow(blackbox->getXDisplay(), frame.plate);
397   }
398
399   if (frame.window) {
400     blackbox->removeWindowSearch(frame.window);
401     XDestroyWindow(blackbox->getXDisplay(), frame.window);
402   }
403
404   blackbox->removeWindowSearch(client.window);
405 }
406
407
408 void BlackboxWindow::enableDecor(bool enable) {
409   blackbox_attrib.flags |= AttribDecoration;
410   blackbox_attrib.decoration = enable ? DecorNormal : DecorNone;
411   setupDecor();
412     
413   // we can not be shaded if we lack a titlebar
414   if (! (decorations & Decor_Titlebar) && flags.shaded)
415     shade();
416     
417   if (flags.visible && frame.window) {
418     XMapSubwindows(blackbox->getXDisplay(), frame.window);
419     XMapWindow(blackbox->getXDisplay(), frame.window);
420   }
421
422   reconfigure();
423   setState(current_state);
424 }
425
426
427 void BlackboxWindow::setupDecor() {
428   if (blackbox_attrib.decoration != DecorNone) {
429     // start with everything on
430     decorations =
431       (mwm_decorations & Decor_Titlebar ? Decor_Titlebar : 0) |
432       (mwm_decorations & Decor_Border ? Decor_Border : 0) |
433       (mwm_decorations & Decor_Handle ? Decor_Handle : 0) |
434       (mwm_decorations & Decor_Iconify ? Decor_Iconify : 0) |
435       (mwm_decorations & Decor_Maximize ? Decor_Maximize : 0) |
436       (mwm_decorations & Decor_Close ? Decor_Close : 0);
437
438     if (! (functions & Func_Close)) decorations &= ~Decor_Close;
439     if (! (functions & Func_Maximize)) decorations &= ~Decor_Maximize;
440     if (! (functions & Func_Iconify)) decorations &= ~Decor_Iconify;
441     if (! (functions & Func_Resize)) decorations &= ~Decor_Handle;
442
443     switch (window_type) {
444     case Type_Desktop:
445     case Type_Dock:
446     case Type_Menu:
447     case Type_Splash:
448       // none of these windows are decorated by the window manager at all
449       decorations = 0;
450       break;
451
452     case Type_Toolbar:
453     case Type_Utility:
454       decorations &= ~(Decor_Border);
455       break;
456
457     case Type_Dialog:
458       decorations &= ~Decor_Handle;
459       break;
460
461     case Type_Normal:
462       break;
463     }
464   } else {
465     decorations = 0;
466   }
467 }
468
469 /*
470  * Creates a new top level window, with a given location, size, and border
471  * width.
472  * Returns: the newly created window
473  */
474 Window BlackboxWindow::createToplevelWindow(void) {
475   XSetWindowAttributes attrib_create;
476   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
477                               CWOverrideRedirect | CWEventMask;
478
479   attrib_create.background_pixmap = None;
480   attrib_create.colormap = screen->getColormap();
481   attrib_create.override_redirect = True;
482   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
483                              ButtonMotionMask |
484                              EnterWindowMask | LeaveWindowMask;
485
486   return XCreateWindow(blackbox->getXDisplay(), screen->getRootWindow(),
487                        0, 0, 1, 1, frame.border_w, screen->getDepth(),
488                        InputOutput, screen->getVisual(), create_mask,
489                        &attrib_create);
490 }
491
492
493 /*
494  * Creates a child window, and optionally associates a given cursor with
495  * the new window.
496  */
497 Window BlackboxWindow::createChildWindow(Window parent, Cursor cursor) {
498   XSetWindowAttributes attrib_create;
499   unsigned long create_mask = CWBackPixmap | CWBorderPixel |
500                               CWEventMask;
501
502   attrib_create.background_pixmap = None;
503   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
504                              ButtonMotionMask | ExposureMask;
505
506   if (cursor) {
507     create_mask |= CWCursor;
508     attrib_create.cursor = cursor;
509   }
510
511   return XCreateWindow(blackbox->getXDisplay(), parent, 0, 0, 1, 1, 0,
512                        screen->getDepth(), InputOutput, screen->getVisual(),
513                        create_mask, &attrib_create);
514 }
515
516
517 void BlackboxWindow::associateClientWindow(void) {
518   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, 0);
519   getWMName();
520   getWMIconName();
521
522   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeInsert);
523
524   XSelectInput(blackbox->getXDisplay(), frame.plate, SubstructureRedirectMask);
525
526   /*
527     note we used to grab around this call to XReparentWindow however the
528     server is now grabbed before this method is called
529   */
530   unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
531                              StructureNotifyMask;
532   XSelectInput(blackbox->getXDisplay(), client.window,
533                event_mask & ~StructureNotifyMask);
534   XReparentWindow(blackbox->getXDisplay(), client.window, frame.plate, 0, 0);
535   XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
536
537   XRaiseWindow(blackbox->getXDisplay(), frame.plate);
538   XMapSubwindows(blackbox->getXDisplay(), frame.plate);
539
540 #ifdef    SHAPE
541   if (blackbox->hasShapeExtensions()) {
542     XShapeSelectInput(blackbox->getXDisplay(), client.window,
543                       ShapeNotifyMask);
544
545     Bool shaped = False;
546     int foo;
547     unsigned int ufoo;
548
549     XShapeQueryExtents(blackbox->getXDisplay(), client.window, &shaped,
550                        &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo,
551                        &ufoo, &ufoo);
552     flags.shaped = shaped;
553   }
554 #endif // SHAPE
555 }
556
557
558 void BlackboxWindow::decorate(void) {
559   BTexture* texture;
560
561   texture = &(screen->getWindowStyle()->b_focus);
562   frame.fbutton = texture->render(frame.button_w, frame.button_w,
563                                   frame.fbutton);
564   if (! frame.fbutton)
565     frame.fbutton_pixel = texture->color().pixel();
566
567   texture = &(screen->getWindowStyle()->b_unfocus);
568   frame.ubutton = texture->render(frame.button_w, frame.button_w,
569                                   frame.ubutton);
570   if (! frame.ubutton)
571     frame.ubutton_pixel = texture->color().pixel();
572
573   texture = &(screen->getWindowStyle()->b_pressed);
574   frame.pbutton = texture->render(frame.button_w, frame.button_w,
575                                   frame.pbutton);
576   if (! frame.pbutton)
577     frame.pbutton_pixel = texture->color().pixel();
578
579   if (decorations & Decor_Titlebar) {
580     texture = &(screen->getWindowStyle()->t_focus);
581     frame.ftitle = texture->render(frame.inside_w, frame.title_h,
582                                    frame.ftitle);
583     if (! frame.ftitle)
584       frame.ftitle_pixel = texture->color().pixel();
585
586     texture = &(screen->getWindowStyle()->t_unfocus);
587     frame.utitle = texture->render(frame.inside_w, frame.title_h,
588                                    frame.utitle);
589     if (! frame.utitle)
590       frame.utitle_pixel = texture->color().pixel();
591
592     XSetWindowBorder(blackbox->getXDisplay(), frame.title,
593                      screen->getBorderColor()->pixel());
594
595     decorateLabel();
596   }
597
598   if (decorations & Decor_Border) {
599     frame.fborder_pixel = screen->getWindowStyle()->f_focus.pixel();
600     frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.pixel();
601   }
602
603   if (decorations & Decor_Handle) {
604     texture = &(screen->getWindowStyle()->h_focus);
605     frame.fhandle = texture->render(frame.inside_w, frame.handle_h,
606                                     frame.fhandle);
607     if (! frame.fhandle)
608       frame.fhandle_pixel = texture->color().pixel();
609
610     texture = &(screen->getWindowStyle()->h_unfocus);
611     frame.uhandle = texture->render(frame.inside_w, frame.handle_h,
612                                     frame.uhandle);
613     if (! frame.uhandle)
614       frame.uhandle_pixel = texture->color().pixel();
615
616     texture = &(screen->getWindowStyle()->g_focus);
617     frame.fgrip = texture->render(frame.grip_w, frame.handle_h, frame.fgrip);
618     if (! frame.fgrip)
619       frame.fgrip_pixel = texture->color().pixel();
620
621     texture = &(screen->getWindowStyle()->g_unfocus);
622     frame.ugrip = texture->render(frame.grip_w, frame.handle_h, frame.ugrip);
623     if (! frame.ugrip)
624       frame.ugrip_pixel = texture->color().pixel();
625
626     XSetWindowBorder(blackbox->getXDisplay(), frame.handle,
627                      screen->getBorderColor()->pixel());
628     XSetWindowBorder(blackbox->getXDisplay(), frame.left_grip,
629                      screen->getBorderColor()->pixel());
630     XSetWindowBorder(blackbox->getXDisplay(), frame.right_grip,
631                      screen->getBorderColor()->pixel());
632   }
633
634   XSetWindowBorder(blackbox->getXDisplay(), frame.window,
635                    screen->getBorderColor()->pixel());
636 }
637
638
639 void BlackboxWindow::decorateLabel(void) {
640   BTexture *texture;
641
642   texture = &(screen->getWindowStyle()->l_focus);
643   frame.flabel = texture->render(frame.label_w, frame.label_h, frame.flabel);
644   if (! frame.flabel)
645     frame.flabel_pixel = texture->color().pixel();
646
647   texture = &(screen->getWindowStyle()->l_unfocus);
648   frame.ulabel = texture->render(frame.label_w, frame.label_h, frame.ulabel);
649   if (! frame.ulabel)
650     frame.ulabel_pixel = texture->color().pixel();
651 }
652
653
654 void BlackboxWindow::createHandle(void) {
655   frame.handle = createChildWindow(frame.window);
656   blackbox->saveWindowSearch(frame.handle, this);
657
658   frame.left_grip =
659     createChildWindow(frame.handle, blackbox->getLowerLeftAngleCursor());
660   blackbox->saveWindowSearch(frame.left_grip, this);
661
662   frame.right_grip =
663     createChildWindow(frame.handle, blackbox->getLowerRightAngleCursor());
664   blackbox->saveWindowSearch(frame.right_grip, this);
665 }
666
667
668 void BlackboxWindow::destroyHandle(void) {
669   if (frame.fhandle)
670     screen->getImageControl()->removeImage(frame.fhandle);
671
672   if (frame.uhandle)
673     screen->getImageControl()->removeImage(frame.uhandle);
674
675   if (frame.fgrip)
676     screen->getImageControl()->removeImage(frame.fgrip);
677
678   if (frame.ugrip)
679     screen->getImageControl()->removeImage(frame.ugrip);
680
681   blackbox->removeWindowSearch(frame.left_grip);
682   blackbox->removeWindowSearch(frame.right_grip);
683
684   XDestroyWindow(blackbox->getXDisplay(), frame.left_grip);
685   XDestroyWindow(blackbox->getXDisplay(), frame.right_grip);
686   frame.left_grip = frame.right_grip = None;
687
688   blackbox->removeWindowSearch(frame.handle);
689   XDestroyWindow(blackbox->getXDisplay(), frame.handle);
690   frame.handle = None;
691 }
692
693
694 void BlackboxWindow::createTitlebar(void) {
695   frame.title = createChildWindow(frame.window);
696   frame.label = createChildWindow(frame.title);
697   blackbox->saveWindowSearch(frame.title, this);
698   blackbox->saveWindowSearch(frame.label, this);
699
700   if (decorations & Decor_Iconify) createIconifyButton();
701   if (decorations & Decor_Maximize) createMaximizeButton();
702   if (decorations & Decor_Close) createCloseButton();
703 }
704
705
706 void BlackboxWindow::destroyTitlebar(void) {
707   if (frame.close_button)
708     destroyCloseButton();
709
710   if (frame.iconify_button)
711     destroyIconifyButton();
712
713   if (frame.maximize_button)
714     destroyMaximizeButton();
715
716   if (frame.ftitle)
717     screen->getImageControl()->removeImage(frame.ftitle);
718
719   if (frame.utitle)
720     screen->getImageControl()->removeImage(frame.utitle);
721
722   if (frame.flabel)
723     screen->getImageControl()->removeImage(frame.flabel);
724
725   if( frame.ulabel)
726     screen->getImageControl()->removeImage(frame.ulabel);
727
728   if (frame.fbutton)
729     screen->getImageControl()->removeImage(frame.fbutton);
730
731   if (frame.ubutton)
732     screen->getImageControl()->removeImage(frame.ubutton);
733
734   if (frame.pbutton)
735     screen->getImageControl()->removeImage(frame.pbutton);
736
737   blackbox->removeWindowSearch(frame.title);
738   blackbox->removeWindowSearch(frame.label);
739
740   XDestroyWindow(blackbox->getXDisplay(), frame.label);
741   XDestroyWindow(blackbox->getXDisplay(), frame.title);
742   frame.title = frame.label = None;
743 }
744
745
746 void BlackboxWindow::createCloseButton(void) {
747   if (frame.title != None) {
748     frame.close_button = createChildWindow(frame.title);
749     blackbox->saveWindowSearch(frame.close_button, this);
750   }
751 }
752
753
754 void BlackboxWindow::destroyCloseButton(void) {
755   blackbox->removeWindowSearch(frame.close_button);
756   XDestroyWindow(blackbox->getXDisplay(), frame.close_button);
757   frame.close_button = None;
758 }
759
760
761 void BlackboxWindow::createIconifyButton(void) {
762   if (frame.title != None) {
763     frame.iconify_button = createChildWindow(frame.title);
764     blackbox->saveWindowSearch(frame.iconify_button, this);
765   }
766 }
767
768
769 void BlackboxWindow::destroyIconifyButton(void) {
770   blackbox->removeWindowSearch(frame.iconify_button);
771   XDestroyWindow(blackbox->getXDisplay(), frame.iconify_button);
772   frame.iconify_button = None;
773 }
774
775
776 void BlackboxWindow::createMaximizeButton(void) {
777   if (frame.title != None) {
778     frame.maximize_button = createChildWindow(frame.title);
779     blackbox->saveWindowSearch(frame.maximize_button, this);
780   }
781 }
782
783
784 void BlackboxWindow::destroyMaximizeButton(void) {
785   blackbox->removeWindowSearch(frame.maximize_button);
786   XDestroyWindow(blackbox->getXDisplay(), frame.maximize_button);
787   frame.maximize_button = None;
788 }
789
790
791 void BlackboxWindow::positionButtons(bool redecorate_label) {
792   string layout = blackbox->getTitlebarLayout();
793   string parsed;
794
795   bool hasclose, hasiconify, hasmaximize, haslabel;
796   hasclose = hasiconify = hasmaximize = haslabel = false;
797
798   string::const_iterator it, end;
799   for (it = layout.begin(), end = layout.end(); it != end; ++it) {
800     switch(*it) {
801     case 'C':
802       if (! hasclose && (decorations & Decor_Close)) {
803         hasclose = true;
804         parsed += *it;
805       }
806       break;
807     case 'I':
808       if (! hasiconify && (decorations & Decor_Iconify)) {
809         hasiconify = true;
810         parsed += *it;
811       }
812       break;
813     case 'M':
814       if (! hasmaximize && (decorations & Decor_Maximize)) {
815         hasmaximize = true;
816         parsed += *it;
817       }
818       break;
819     case 'L':
820       if (! haslabel) {
821         haslabel = true;
822         parsed += *it;
823       }
824     }
825   }
826   if (! hasclose && frame.close_button)
827     destroyCloseButton();
828   if (! hasiconify && frame.iconify_button)
829     destroyIconifyButton();
830   if (! hasmaximize && frame.maximize_button)
831     destroyMaximizeButton();
832   if (! haslabel)
833     parsed += 'L';      // require that the label be in the layout
834
835   const unsigned int bsep = frame.bevel_w + 1;  // separation between elements
836   const unsigned int by = frame.bevel_w + 1;
837   const unsigned int ty = frame.bevel_w;
838
839   frame.label_w = frame.inside_w - bsep * 2 -
840     (frame.button_w + bsep) * (parsed.size() - 1);
841
842   unsigned int x = bsep;
843   for (it = parsed.begin(), end = parsed.end(); it != end; ++it) {
844     switch(*it) {
845     case 'C':
846       if (! frame.close_button) createCloseButton();
847       XMoveResizeWindow(blackbox->getXDisplay(), frame.close_button, x, by,
848                         frame.button_w, frame.button_w);
849       x += frame.button_w + bsep;
850       break;
851     case 'I':
852       if (! frame.iconify_button) createIconifyButton();
853       XMoveResizeWindow(blackbox->getXDisplay(), frame.iconify_button, x, by,
854                         frame.button_w, frame.button_w);
855       x += frame.button_w + bsep;
856       break;
857     case 'M':
858       if (! frame.maximize_button) createMaximizeButton();
859       XMoveResizeWindow(blackbox->getXDisplay(), frame.maximize_button, x, by,
860                         frame.button_w, frame.button_w);
861       x += frame.button_w + bsep;
862       break;
863     case 'L':
864       XMoveResizeWindow(blackbox->getXDisplay(), frame.label, x, ty,
865                         frame.label_w, frame.label_h);
866       x += frame.label_w + bsep;
867       break;
868     }
869   }
870
871   if (redecorate_label) decorateLabel();
872   redrawLabel();
873   redrawAllButtons();
874 }
875
876
877 void BlackboxWindow::reconfigure(void) {
878   restoreGravity(client.rect);
879   upsize();
880   applyGravity(frame.rect);
881   positionWindows();
882   decorate();
883   redrawWindowFrame();
884
885   ungrabButtons();
886   grabButtons();
887
888   if (windowmenu) {
889     windowmenu->move(windowmenu->getX(), frame.rect.y() + frame.title_h);
890     windowmenu->reconfigure();
891   }
892 }
893
894
895 void BlackboxWindow::grabButtons(void) {
896   if (! screen->isSloppyFocus() || screen->doClickRaise())
897     // grab button 1 for changing focus/raising
898     blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
899                          GrabModeSync, GrabModeSync, frame.plate, None,
900                          screen->allowScrollLock());
901   
902   if (functions & Func_Move)
903     blackbox->grabButton(Button1, ModMask, frame.window, True,
904                          ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
905                          GrabModeAsync, frame.window, None,
906                          screen->allowScrollLock());
907   if (functions & Func_Resize)
908     blackbox->grabButton(Button3, ModMask, frame.window, True,
909                          ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
910                          GrabModeAsync, frame.window, None,
911                          screen->allowScrollLock());
912   // alt+middle lowers the window
913   blackbox->grabButton(Button2, ModMask, frame.window, True,
914                        ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
915                        frame.window, None,
916                        screen->allowScrollLock());
917 }
918
919
920 void BlackboxWindow::ungrabButtons(void) {
921   blackbox->ungrabButton(Button1, 0, frame.plate);
922   blackbox->ungrabButton(Button1, ModMask, frame.window);
923   blackbox->ungrabButton(Button2, ModMask, frame.window);
924   blackbox->ungrabButton(Button3, ModMask, frame.window);
925 }
926
927
928 void BlackboxWindow::positionWindows(void) {
929   XMoveResizeWindow(blackbox->getXDisplay(), frame.window,
930                     frame.rect.x(), frame.rect.y(), frame.inside_w,
931                     (flags.shaded) ? frame.title_h : frame.inside_h);
932   XSetWindowBorderWidth(blackbox->getXDisplay(), frame.window,
933                         frame.border_w);
934   XSetWindowBorderWidth(blackbox->getXDisplay(), frame.plate,
935                         frame.mwm_border_w);
936   XMoveResizeWindow(blackbox->getXDisplay(), frame.plate,
937                     frame.margin.left - frame.mwm_border_w - frame.border_w,
938                     frame.margin.top - frame.mwm_border_w - frame.border_w,
939                     client.rect.width(), client.rect.height());
940   XMoveResizeWindow(blackbox->getXDisplay(), client.window,
941                     0, 0, client.rect.width(), client.rect.height());
942   // ensure client.rect contains the real location
943   client.rect.setPos(frame.rect.left() + frame.margin.left,
944                      frame.rect.top() + frame.margin.top);
945
946   if (decorations & Decor_Titlebar) {
947     if (frame.title == None) createTitlebar();
948
949     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.title,
950                           frame.border_w);
951     XMoveResizeWindow(blackbox->getXDisplay(), frame.title, -frame.border_w,
952                       -frame.border_w, frame.inside_w, frame.title_h);
953
954     positionButtons();
955     XMapSubwindows(blackbox->getXDisplay(), frame.title);
956     XMapWindow(blackbox->getXDisplay(), frame.title);
957   } else if (frame.title) {
958     destroyTitlebar();
959   }
960   if (decorations & Decor_Handle) {
961     if (frame.handle == None) createHandle();
962     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.handle,
963                           frame.border_w);
964     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.left_grip,
965                           frame.border_w);
966     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.right_grip,
967                           frame.border_w);
968
969     // use client.rect here so the value is correct even if shaded
970     XMoveResizeWindow(blackbox->getXDisplay(), frame.handle,
971                       -frame.border_w,
972                       client.rect.height() + frame.margin.top +
973                       frame.mwm_border_w - frame.border_w,
974                       frame.inside_w, frame.handle_h);
975     XMoveResizeWindow(blackbox->getXDisplay(), frame.left_grip,
976                       -frame.border_w, -frame.border_w,
977                       frame.grip_w, frame.handle_h);
978     XMoveResizeWindow(blackbox->getXDisplay(), frame.right_grip,
979                       frame.inside_w - frame.grip_w - frame.border_w,
980                       -frame.border_w, frame.grip_w, frame.handle_h);
981
982     XMapSubwindows(blackbox->getXDisplay(), frame.handle);
983     XMapWindow(blackbox->getXDisplay(), frame.handle);
984   } else if (frame.handle) {
985     destroyHandle();
986   }
987   XSync(blackbox->getXDisplay(), False);
988 }
989
990
991 void BlackboxWindow::updateStrut(void) {
992   unsigned long num = 4;
993   unsigned long *data;
994   if (! xatom->getValue(client.window, XAtom::net_wm_strut, XAtom::cardinal,
995                         num, &data))
996     return;
997  
998   if (num == 4) {
999     client.strut.left = data[0];
1000     client.strut.right = data[1];
1001     client.strut.top = data[2];
1002     client.strut.bottom = data[3];
1003
1004     screen->updateAvailableArea();
1005   }
1006
1007   delete [] data;
1008 }
1009
1010
1011 bool BlackboxWindow::getWindowType(void) {
1012   unsigned long val;
1013   if (xatom->getValue(client.window, XAtom::net_wm_window_type, XAtom::atom,
1014                       val)) {
1015     if (val == xatom->getAtom(XAtom::net_wm_window_type_desktop))
1016       window_type = Type_Desktop;
1017     else if (val == xatom->getAtom(XAtom::net_wm_window_type_dock))
1018       window_type = Type_Dock;
1019     else if (val == xatom->getAtom(XAtom::net_wm_window_type_toolbar))
1020       window_type = Type_Toolbar;
1021     else if (val == xatom->getAtom(XAtom::net_wm_window_type_menu))
1022       window_type = Type_Menu;
1023     else if (val == xatom->getAtom(XAtom::net_wm_window_type_utility))
1024       window_type = Type_Utility;
1025     else if (val == xatom->getAtom(XAtom::net_wm_window_type_splash))
1026       window_type = Type_Splash;
1027     else if (val == xatom->getAtom(XAtom::net_wm_window_type_dialog))
1028       window_type = Type_Dialog;
1029     else //if (val[0] == xatom->getAtom(XAtom::net_wm_window_type_normal))
1030       window_type = Type_Normal;
1031
1032     return True;
1033   }
1034
1035   /*
1036    * the window type hint was not set, which means we either classify ourself
1037    * as a normal window or a dialog, depending on if we are a transient.
1038    */
1039   if (isTransient())
1040     window_type = Type_Dialog;
1041
1042   window_type = Type_Normal;
1043
1044   return False;
1045 }
1046
1047
1048 void BlackboxWindow::getWMName(void) {
1049   if (xatom->getValue(client.window, XAtom::net_wm_name,
1050                       XAtom::utf8, client.title) &&
1051       !client.title.empty()) {
1052     xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
1053     return;
1054   }
1055   //fall through to using WM_NAME
1056   if (xatom->getValue(client.window, XAtom::wm_name, XAtom::ansi, client.title)
1057       && !client.title.empty()) {
1058     xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
1059     return;
1060   }
1061   // fall back to an internal default
1062   client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
1063   xatom->setValue(client.window, XAtom::net_wm_visible_name, XAtom::utf8,
1064                   client.title);
1065 }
1066
1067
1068 void BlackboxWindow::getWMIconName(void) {
1069   if (xatom->getValue(client.window, XAtom::net_wm_icon_name,
1070                       XAtom::utf8, client.icon_title) && 
1071       !client.icon_title.empty()) {
1072     xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1073     return;
1074   }
1075   //fall through to using WM_ICON_NAME
1076   if (xatom->getValue(client.window, XAtom::wm_icon_name, XAtom::ansi,
1077                       client.icon_title) && 
1078       !client.icon_title.empty()) {
1079     xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1080     return;
1081   }
1082   // fall back to using the main name
1083   client.icon_title = client.title;
1084   xatom->setValue(client.window, XAtom::net_wm_visible_icon_name, XAtom::utf8,
1085                   client.icon_title);
1086 }
1087
1088
1089 /*
1090  * Retrieve which WM Protocols are supported by the client window.
1091  * If the WM_DELETE_WINDOW protocol is supported, add the close button to the
1092  * window's decorations and allow the close behavior.
1093  * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates
1094  * this.
1095  */
1096 void BlackboxWindow::getWMProtocols(void) {
1097   Atom *proto;
1098   int num_return = 0;
1099
1100   if (XGetWMProtocols(blackbox->getXDisplay(), client.window,
1101                       &proto, &num_return)) {
1102     for (int i = 0; i < num_return; ++i) {
1103       if (proto[i] == xatom->getAtom(XAtom::wm_delete_window)) {
1104         decorations |= Decor_Close;
1105         functions |= Func_Close;
1106       } else if (proto[i] == xatom->getAtom(XAtom::wm_take_focus))
1107         flags.send_focus_message = True;
1108       else if (proto[i] == xatom->getAtom(XAtom::blackbox_structure_messages))
1109         screen->addNetizen(new Netizen(screen, client.window));
1110     }
1111
1112     XFree(proto);
1113   }
1114 }
1115
1116
1117 /*
1118  * Gets the value of the WM_HINTS property.
1119  * If the property is not set, then use a set of default values.
1120  */
1121 void BlackboxWindow::getWMHints(void) {
1122   focus_mode = F_Passive;
1123
1124   // remove from current window group
1125   if (client.window_group) {
1126     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1127     if (group) group->removeWindow(this);
1128   }
1129   client.window_group = None;
1130
1131   XWMHints *wmhint = XGetWMHints(blackbox->getXDisplay(), client.window);
1132   if (! wmhint) {
1133     return;
1134   }
1135
1136   if (wmhint->flags & InputHint) {
1137     if (wmhint->input == True) {
1138       if (flags.send_focus_message)
1139         focus_mode = F_LocallyActive;
1140     } else {
1141       if (flags.send_focus_message)
1142         focus_mode = F_GloballyActive;
1143       else
1144         focus_mode = F_NoInput;
1145     }
1146   }
1147
1148   if (wmhint->flags & StateHint)
1149     current_state = wmhint->initial_state;
1150
1151   if (wmhint->flags & WindowGroupHint) {
1152     client.window_group = wmhint->window_group;
1153
1154     // add window to the appropriate group
1155     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1156     if (! group) { // no group found, create it!
1157       new BWindowGroup(blackbox, client.window_group);
1158       group = blackbox->searchGroup(client.window_group);
1159     }
1160     if (group)
1161       group->addWindow(this);
1162   }
1163
1164   XFree(wmhint);
1165 }
1166
1167
1168 /*
1169  * Gets the value of the WM_NORMAL_HINTS property.
1170  * If the property is not set, then use a set of default values.
1171  */
1172 void BlackboxWindow::getWMNormalHints(void) {
1173   long icccm_mask;
1174   XSizeHints sizehint;
1175
1176   client.min_width = client.min_height =
1177     client.width_inc = client.height_inc = 1;
1178   client.base_width = client.base_height = 0;
1179   client.win_gravity = NorthWestGravity;
1180 #if 0
1181   client.min_aspect_x = client.min_aspect_y =
1182     client.max_aspect_x = client.max_aspect_y = 1;
1183 #endif
1184
1185   /*
1186     use the full screen, not the strut modified size. otherwise when the
1187     availableArea changes max_width/height will be incorrect and lead to odd
1188     rendering bugs.
1189   */
1190   const Rect& screen_area = screen->getRect();
1191   client.max_width = screen_area.width();
1192   client.max_height = screen_area.height();
1193
1194   if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window,
1195                           &sizehint, &icccm_mask))
1196     return;
1197
1198   client.normal_hint_flags = sizehint.flags;
1199
1200   if (sizehint.flags & PMinSize) {
1201     if (sizehint.min_width >= 0)
1202       client.min_width = sizehint.min_width;
1203     if (sizehint.min_height >= 0)
1204       client.min_height = sizehint.min_height;
1205   }
1206
1207   if (sizehint.flags & PMaxSize) {
1208     if (sizehint.max_width > static_cast<signed>(client.min_width))
1209       client.max_width = sizehint.max_width;
1210     else
1211       client.max_width = client.min_width;
1212
1213     if (sizehint.max_height > static_cast<signed>(client.min_height))
1214       client.max_height = sizehint.max_height;
1215     else
1216       client.max_height = client.min_height;
1217   }
1218
1219   if (sizehint.flags & PResizeInc) {
1220     client.width_inc = sizehint.width_inc;
1221     client.height_inc = sizehint.height_inc;
1222   }
1223
1224 #if 0 // we do not support this at the moment
1225   if (sizehint.flags & PAspect) {
1226     client.min_aspect_x = sizehint.min_aspect.x;
1227     client.min_aspect_y = sizehint.min_aspect.y;
1228     client.max_aspect_x = sizehint.max_aspect.x;
1229     client.max_aspect_y = sizehint.max_aspect.y;
1230   }
1231 #endif
1232
1233   if (sizehint.flags & PBaseSize) {
1234     client.base_width = sizehint.base_width;
1235     client.base_height = sizehint.base_height;
1236   }
1237
1238   if (sizehint.flags & PWinGravity)
1239     client.win_gravity = sizehint.win_gravity;
1240 }
1241
1242
1243 /*
1244  * Gets the NETWM hints for the class' contained window.
1245  */
1246 void BlackboxWindow::getNetWMHints(void) {
1247   unsigned long workspace;
1248
1249   if (xatom->getValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1250                       workspace)) {
1251     if (workspace == 0xffffffff)
1252       flags.stuck = True;
1253     else
1254       blackbox_attrib.workspace = workspace;
1255   }
1256
1257   unsigned long *state;
1258   unsigned long num = (unsigned) -1;
1259   if (xatom->getValue(client.window, XAtom::net_wm_state, XAtom::atom,
1260                       num, &state)) {
1261     bool vert = False,
1262          horz = False;
1263     for (unsigned long i = 0; i < num; ++i) {
1264       if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal))
1265         flags.modal = True;
1266       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_shaded))
1267         flags.shaded = True;
1268       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_taskbar))
1269         flags.skip_taskbar = True;
1270       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_pager))
1271         flags.skip_pager = True;
1272       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_fullscreen))
1273         flags.fullscreen = True;
1274       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_hidden))
1275         setState(IconicState);
1276       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_vert))
1277         vert = True;
1278       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_horz))
1279         horz = True;
1280     }
1281     if (vert && horz)
1282       flags.maximized = 1;
1283     else if (vert)
1284       flags.maximized = 2;
1285     else if (horz)
1286       flags.maximized = 3;
1287
1288     delete [] state;
1289   }
1290 }
1291
1292
1293 /*
1294  * Gets the MWM hints for the class' contained window.
1295  * This is used while initializing the window to its first state, and not
1296  * thereafter.
1297  * Returns: true if the MWM hints are successfully retreived and applied;
1298  * false if they are not.
1299  */
1300 void BlackboxWindow::getMWMHints(void) {
1301   unsigned long num;
1302   MwmHints *mwm_hint;
1303
1304   num = PropMwmHintsElements;
1305   if (! xatom->getValue(client.window, XAtom::motif_wm_hints,
1306                         XAtom::motif_wm_hints, num,
1307                         (unsigned long **)&mwm_hint))
1308     return;
1309   if (num < PropMwmHintsElements) {
1310     delete [] mwm_hint;
1311     return;
1312   }
1313
1314   if (mwm_hint->flags & MwmHintsDecorations) {
1315     if (mwm_hint->decorations & MwmDecorAll) {
1316       mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
1317                         Decor_Iconify | Decor_Maximize | Decor_Close;
1318     } else {
1319       mwm_decorations = 0;
1320
1321       if (mwm_hint->decorations & MwmDecorBorder)
1322         mwm_decorations |= Decor_Border;
1323       if (mwm_hint->decorations & MwmDecorHandle)
1324         mwm_decorations |= Decor_Handle;
1325       if (mwm_hint->decorations & MwmDecorTitle)
1326         mwm_decorations |= Decor_Titlebar;
1327       if (mwm_hint->decorations & MwmDecorIconify)
1328         mwm_decorations |= Decor_Iconify;
1329       if (mwm_hint->decorations & MwmDecorMaximize)
1330         mwm_decorations |= Decor_Maximize;
1331     }
1332   }
1333
1334   if (mwm_hint->flags & MwmHintsFunctions) {
1335     if (mwm_hint->functions & MwmFuncAll) {
1336       functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
1337                   Func_Close;
1338     } else {
1339       functions = 0;
1340
1341       if (mwm_hint->functions & MwmFuncResize)
1342         functions |= Func_Resize;
1343       if (mwm_hint->functions & MwmFuncMove)
1344         functions |= Func_Move;
1345       if (mwm_hint->functions & MwmFuncIconify)
1346         functions |= Func_Iconify;
1347       if (mwm_hint->functions & MwmFuncMaximize)
1348         functions |= Func_Maximize;
1349       if (mwm_hint->functions & MwmFuncClose)
1350         functions |= Func_Close;
1351     }
1352   }
1353   delete [] mwm_hint;
1354 }
1355
1356
1357 /*
1358  * Gets the blackbox hints from the class' contained window.
1359  * This is used while initializing the window to its first state, and not
1360  * thereafter.
1361  * Returns: true if the hints are successfully retreived and applied; false if
1362  * they are not.
1363  */
1364 bool BlackboxWindow::getBlackboxHints(void) {
1365   unsigned long num;
1366   BlackboxHints *blackbox_hint;
1367
1368   num = PropBlackboxHintsElements;
1369   if (! xatom->getValue(client.window, XAtom::blackbox_hints,
1370                         XAtom::blackbox_hints, num,
1371                         (unsigned long **)&blackbox_hint))
1372     return False;
1373   if (num < PropBlackboxHintsElements) {
1374     delete [] blackbox_hint;
1375     return False;
1376   }
1377
1378   if (blackbox_hint->flags & AttribShaded)
1379     flags.shaded = (blackbox_hint->attrib & AttribShaded);
1380
1381   if ((blackbox_hint->flags & AttribMaxHoriz) &&
1382       (blackbox_hint->flags & AttribMaxVert))
1383     flags.maximized = (blackbox_hint->attrib &
1384                        (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1385   else if (blackbox_hint->flags & AttribMaxVert)
1386     flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1387   else if (blackbox_hint->flags & AttribMaxHoriz)
1388     flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1389
1390   if (blackbox_hint->flags & AttribOmnipresent)
1391     flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
1392
1393   if (blackbox_hint->flags & AttribWorkspace)
1394     blackbox_attrib.workspace = blackbox_hint->workspace;
1395
1396   // if (blackbox_hint->flags & AttribStack)
1397   //   don't yet have always on top/bottom for blackbox yet... working
1398   //   on that
1399
1400   if (blackbox_hint->flags & AttribDecoration) {
1401     switch (blackbox_hint->decoration) {
1402     case DecorNone:
1403       blackbox_attrib.decoration = DecorNone;
1404       break;
1405
1406     case DecorTiny:
1407     case DecorTool:
1408     case DecorNormal:
1409     default:
1410       // blackbox_attrib.decoration defaults to DecorNormal
1411       break;
1412     }
1413   }
1414   
1415   delete [] blackbox_hint;
1416
1417   return True;
1418 }
1419
1420
1421 void BlackboxWindow::getTransientInfo(void) {
1422   if (client.transient_for &&
1423       client.transient_for != (BlackboxWindow *) ~0ul) {
1424     // reset transient_for in preparation of looking for a new owner
1425     client.transient_for->client.transientList.remove(this);
1426   }
1427
1428   // we have no transient_for until we find a new one
1429   client.transient_for = 0;
1430
1431   Window trans_for;
1432   if (! XGetTransientForHint(blackbox->getXDisplay(), client.window,
1433                              &trans_for)) {
1434     // transient_for hint not set
1435     return;
1436   }
1437
1438   if (trans_for == client.window) {
1439     // wierd client... treat this window as a normal window
1440     return;
1441   }
1442
1443   if (trans_for == None || trans_for == screen->getRootWindow()) {
1444     // this is an undocumented interpretation of the ICCCM. a transient
1445     // associated with None/Root/itself is assumed to be a modal root
1446     // transient.  we don't support the concept of a global transient,
1447     // so we just associate this transient with nothing, and perhaps
1448     // we will add support later for global modality.
1449     client.transient_for = (BlackboxWindow *) ~0ul;
1450     flags.modal = True;
1451     return;
1452   }
1453
1454   client.transient_for = blackbox->searchWindow(trans_for);
1455   if (! client.transient_for &&
1456       client.window_group && trans_for == client.window_group) {
1457     // no direct transient_for, perhaps this is a group transient?
1458     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1459     if (group) client.transient_for = group->find(screen);
1460   }
1461
1462   if (! client.transient_for || client.transient_for == this) {
1463     // no transient_for found, or we have a wierd client that wants to be
1464     // a transient for itself, so we treat this window as a normal window
1465     client.transient_for = (BlackboxWindow*) 0;
1466     return;
1467   }
1468
1469   // Check for a circular transient state: this can lock up Blackbox
1470   // when it tries to find the non-transient window for a transient.
1471   BlackboxWindow *w = this;
1472   while(w->client.transient_for &&
1473         w->client.transient_for != (BlackboxWindow *) ~0ul) {
1474     if(w->client.transient_for == this) {
1475       client.transient_for = (BlackboxWindow*) 0;
1476       break;
1477     }
1478     w = w->client.transient_for;
1479   }
1480
1481   if (client.transient_for &&
1482       client.transient_for != (BlackboxWindow *) ~0ul) {
1483     // register ourselves with our new transient_for
1484     client.transient_for->client.transientList.push_back(this);
1485     flags.stuck = client.transient_for->flags.stuck;
1486   }
1487 }
1488
1489
1490 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1491   if (client.transient_for &&
1492       client.transient_for != (BlackboxWindow*) ~0ul)
1493     return client.transient_for;
1494   return 0;
1495 }
1496
1497
1498 /*
1499  * This function is responsible for updating both the client and the frame
1500  * rectangles.
1501  * According to the ICCCM a client message is not sent for a resize, only a
1502  * move.
1503  */
1504 void BlackboxWindow::configure(int dx, int dy,
1505                                unsigned int dw, unsigned int dh) {
1506   bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1507                      ! flags.moving);
1508
1509   if (dw != frame.rect.width() || dh != frame.rect.height()) {
1510     frame.rect.setRect(dx, dy, dw, dh);
1511     frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1512     frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1513
1514     if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1515       frame.rect.setPos(0, 0);
1516
1517     client.rect.setCoords(frame.rect.left() + frame.margin.left,
1518                           frame.rect.top() + frame.margin.top,
1519                           frame.rect.right() - frame.margin.right,
1520                           frame.rect.bottom() - frame.margin.bottom);
1521
1522 #ifdef    SHAPE
1523     if (blackbox->hasShapeExtensions() && flags.shaped) {
1524       configureShape();
1525     }
1526 #endif // SHAPE
1527
1528     positionWindows();
1529     decorate();
1530     redrawWindowFrame();
1531   } else {
1532     frame.rect.setPos(dx, dy);
1533
1534     XMoveWindow(blackbox->getXDisplay(), frame.window,
1535                 frame.rect.x(), frame.rect.y());
1536     /*
1537       we may have been called just after an opaque window move, so even though
1538       the old coords match the new ones no ConfigureNotify has been sent yet.
1539       There are likely other times when this will be relevant as well.
1540     */
1541     if (! flags.moving) send_event = True;
1542   }
1543
1544   if (send_event) {
1545     // if moving, the update and event will occur when the move finishes
1546     client.rect.setPos(frame.rect.left() + frame.margin.left,
1547                        frame.rect.top() + frame.margin.top);
1548
1549     XEvent event;
1550     event.type = ConfigureNotify;
1551
1552     event.xconfigure.display = blackbox->getXDisplay();
1553     event.xconfigure.event = client.window;
1554     event.xconfigure.window = client.window;
1555     event.xconfigure.x = client.rect.x();
1556     event.xconfigure.y = client.rect.y();
1557     event.xconfigure.width = client.rect.width();
1558     event.xconfigure.height = client.rect.height();
1559     event.xconfigure.border_width = client.old_bw;
1560     event.xconfigure.above = frame.window;
1561     event.xconfigure.override_redirect = False;
1562
1563     XSendEvent(blackbox->getXDisplay(), client.window, False,
1564                StructureNotifyMask, &event);
1565     screen->updateNetizenConfigNotify(&event);
1566     XFlush(blackbox->getXDisplay());
1567   }
1568 }
1569
1570
1571 #ifdef SHAPE
1572 void BlackboxWindow::configureShape(void) {
1573   XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1574                      frame.margin.left - frame.border_w,
1575                      frame.margin.top - frame.border_w,
1576                      client.window, ShapeBounding, ShapeSet);
1577
1578   int num = 0;
1579   XRectangle xrect[2];
1580
1581   if (decorations & Decor_Titlebar) {
1582     xrect[0].x = xrect[0].y = -frame.border_w;
1583     xrect[0].width = frame.rect.width();
1584     xrect[0].height = frame.title_h + (frame.border_w * 2);
1585     ++num;
1586   }
1587
1588   if (decorations & Decor_Handle) {
1589     xrect[1].x = -frame.border_w;
1590     xrect[1].y = frame.rect.height() - frame.margin.bottom +
1591                  frame.mwm_border_w - frame.border_w;
1592     xrect[1].width = frame.rect.width();
1593     xrect[1].height = frame.handle_h + (frame.border_w * 2);
1594     ++num;
1595   }
1596
1597   XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1598                           ShapeBounding, 0, 0, xrect, num,
1599                           ShapeUnion, Unsorted);
1600 }
1601 #endif // SHAPE
1602
1603
1604 bool BlackboxWindow::setInputFocus(void) {
1605   if (flags.focused) return True;
1606
1607   assert(flags.stuck ||  // window must be on the current workspace or sticky
1608          blackbox_attrib.workspace == screen->getCurrentWorkspaceID());
1609
1610   /*
1611      We only do this check for normal windows and dialogs because other windows
1612      do this on purpose, such as kde's kicker, and we don't want to go moving
1613      it.
1614   */
1615   if (window_type == Type_Normal || window_type == Type_Dialog)
1616     if (! frame.rect.intersects(screen->getRect())) {
1617       // client is outside the screen, move it to the center
1618       configure((screen->getWidth() - frame.rect.width()) / 2,
1619                 (screen->getHeight() - frame.rect.height()) / 2,
1620                 frame.rect.width(), frame.rect.height());
1621     }
1622
1623   if (client.transientList.size() > 0) {
1624     // transfer focus to any modal transients
1625     BlackboxWindowList::iterator it, end = client.transientList.end();
1626     for (it = client.transientList.begin(); it != end; ++it)
1627       if ((*it)->flags.modal) return (*it)->setInputFocus();
1628   }
1629
1630   bool ret = True;
1631   if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1632     XSetInputFocus(blackbox->getXDisplay(), client.window,
1633                    RevertToPointerRoot, CurrentTime);
1634   } else {
1635     /* we could set the focus to none, since the window doesn't accept focus,
1636      * but we shouldn't set focus to nothing since this would surely make
1637      * someone angry
1638      */
1639     ret = False;
1640   }
1641
1642   if (flags.send_focus_message) {
1643     XEvent ce;
1644     ce.xclient.type = ClientMessage;
1645     ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1646     ce.xclient.display = blackbox->getXDisplay();
1647     ce.xclient.window = client.window;
1648     ce.xclient.format = 32;
1649     ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus);
1650     ce.xclient.data.l[1] = blackbox->getLastTime();
1651     ce.xclient.data.l[2] = 0l;
1652     ce.xclient.data.l[3] = 0l;
1653     ce.xclient.data.l[4] = 0l;
1654     XSendEvent(blackbox->getXDisplay(), client.window, False,
1655                NoEventMask, &ce);
1656     XFlush(blackbox->getXDisplay());
1657   }
1658
1659   return ret;
1660 }
1661
1662
1663 void BlackboxWindow::iconify(void) {
1664   if (flags.iconic) return;
1665
1666   // We don't need to worry about resizing because resizing always grabs the X
1667   // server. This should only ever happen if using opaque moving.
1668   if (flags.moving)
1669     endMove();
1670     
1671   if (windowmenu) windowmenu->hide();
1672
1673   /*
1674    * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1675    * we need to clear the event mask on client.window for a split second.
1676    * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1677    * split second, leaving us with a ghost window... so, we need to do this
1678    * while the X server is grabbed
1679    */
1680   unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1681                              StructureNotifyMask;
1682   XGrabServer(blackbox->getXDisplay());
1683   XSelectInput(blackbox->getXDisplay(), client.window,
1684                event_mask & ~StructureNotifyMask);
1685   XUnmapWindow(blackbox->getXDisplay(), client.window);
1686   XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1687   XUngrabServer(blackbox->getXDisplay());
1688
1689   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1690   flags.visible = False;
1691   flags.iconic = True;
1692
1693   setState(IconicState);
1694
1695   screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1696   if (flags.stuck) {
1697     for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
1698       if (i != blackbox_attrib.workspace)
1699         screen->getWorkspace(i)->removeWindow(this, True);
1700   }
1701
1702   if (isTransient()) {
1703     if (client.transient_for != (BlackboxWindow *) ~0ul &&
1704         ! client.transient_for->flags.iconic) {
1705       // iconify our transient_for
1706       client.transient_for->iconify();
1707     }
1708   }
1709
1710   screen->addIcon(this);
1711
1712   if (client.transientList.size() > 0) {
1713     // iconify all transients
1714     BlackboxWindowList::iterator it, end = client.transientList.end();
1715     for (it = client.transientList.begin(); it != end; ++it) {
1716       if (! (*it)->flags.iconic) (*it)->iconify();
1717     }
1718   }
1719   screen->updateStackingList();
1720 }
1721
1722
1723 void BlackboxWindow::show(void) {
1724   flags.visible = True;
1725   flags.iconic = False;
1726
1727   current_state = (flags.shaded) ? IconicState : NormalState;
1728   setState(current_state);
1729
1730   XMapWindow(blackbox->getXDisplay(), client.window);
1731   XMapSubwindows(blackbox->getXDisplay(), frame.window);
1732   XMapWindow(blackbox->getXDisplay(), frame.window);
1733
1734 #if 0
1735   int real_x, real_y;
1736   Window child;
1737   XTranslateCoordinates(blackbox->getXDisplay(), client.window,
1738                         screen->getRootWindow(),
1739                         0, 0, &real_x, &real_y, &child);
1740   fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
1741           client.rect.left(), client.rect.top(), real_x, real_y);
1742   assert(client.rect.left() == real_x && client.rect.top() == real_y);
1743 #endif
1744 }
1745
1746
1747 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1748   if (flags.iconic || reassoc)
1749     screen->reassociateWindow(this, BSENTINEL, False);
1750   else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
1751     return;
1752
1753   show();
1754
1755   // reassociate and deiconify all transients
1756   if (reassoc && client.transientList.size() > 0) {
1757     BlackboxWindowList::iterator it, end = client.transientList.end();
1758     for (it = client.transientList.begin(); it != end; ++it)
1759       (*it)->deiconify(True, False);
1760   }
1761
1762   if (raise)
1763     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1764 }
1765
1766
1767 void BlackboxWindow::close(void) {
1768   XEvent ce;
1769   ce.xclient.type = ClientMessage;
1770   ce.xclient.message_type =  xatom->getAtom(XAtom::wm_protocols);
1771   ce.xclient.display = blackbox->getXDisplay();
1772   ce.xclient.window = client.window;
1773   ce.xclient.format = 32;
1774   ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
1775   ce.xclient.data.l[1] = CurrentTime;
1776   ce.xclient.data.l[2] = 0l;
1777   ce.xclient.data.l[3] = 0l;
1778   ce.xclient.data.l[4] = 0l;
1779   XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1780   XFlush(blackbox->getXDisplay());
1781 }
1782
1783
1784 void BlackboxWindow::withdraw(void) {
1785   // We don't need to worry about resizing because resizing always grabs the X
1786   // server. This should only ever happen if using opaque moving.
1787   if (flags.moving)
1788     endMove();
1789     
1790   flags.visible = False;
1791   flags.iconic = False;
1792
1793   setState(current_state);
1794
1795   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1796
1797   XGrabServer(blackbox->getXDisplay());
1798
1799   unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1800                              StructureNotifyMask;
1801   XSelectInput(blackbox->getXDisplay(), client.window,
1802                event_mask & ~StructureNotifyMask);
1803   XUnmapWindow(blackbox->getXDisplay(), client.window);
1804   XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1805
1806   XUngrabServer(blackbox->getXDisplay());
1807
1808   if (windowmenu) windowmenu->hide();
1809 }
1810
1811
1812 void BlackboxWindow::maximize(unsigned int button) {
1813   // We don't need to worry about resizing because resizing always grabs the X
1814   // server. This should only ever happen if using opaque moving.
1815   if (flags.moving)
1816     endMove();
1817
1818   // handle case where menu is open then the max button is used instead
1819   if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1820
1821   if (flags.maximized) {
1822     flags.maximized = 0;
1823
1824     blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1825     blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1826
1827     /*
1828       when a resize finishes, maximize(0) is called to clear any maximization
1829       flags currently set.  Otherwise it still thinks it is maximized.
1830       so we do not need to call configure() because resizing will handle it
1831     */
1832     if (! flags.resizing)
1833       configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1834                 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1835
1836     blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1837     blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1838
1839     redrawAllButtons(); // in case it is not called in configure()
1840     setState(current_state);
1841     return;
1842   }
1843
1844   blackbox_attrib.premax_x = frame.rect.x();
1845   blackbox_attrib.premax_y = frame.rect.y();
1846   blackbox_attrib.premax_w = frame.rect.width();
1847   // use client.rect so that clients can be restored even if shaded
1848   blackbox_attrib.premax_h =
1849     client.rect.height() + frame.margin.top + frame.margin.bottom;
1850
1851 #ifdef    XINERAMA
1852   if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) {
1853     // find the area to use
1854     RectList availableAreas = screen->allAvailableAreas();
1855     RectList::iterator it, end = availableAreas.end();
1856
1857     for (it = availableAreas.begin(); it != end; ++it)
1858       if (it->intersects(frame.rect)) break;
1859     if (it == end) // the window isn't inside an area
1860       it = availableAreas.begin(); // so just default to the first one
1861
1862     frame.changing = *it;
1863   } else
1864 #endif // XINERAMA
1865   frame.changing = screen->availableArea();
1866
1867   switch(button) {
1868   case 1:
1869     blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1870     blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1871     break;
1872
1873   case 2:
1874     blackbox_attrib.flags |= AttribMaxVert;
1875     blackbox_attrib.attrib |= AttribMaxVert;
1876
1877     frame.changing.setX(frame.rect.x());
1878     frame.changing.setWidth(frame.rect.width());
1879     break;
1880
1881   case 3:
1882     blackbox_attrib.flags |= AttribMaxHoriz;
1883     blackbox_attrib.attrib |= AttribMaxHoriz;
1884
1885     frame.changing.setY(frame.rect.y());
1886     frame.changing.setHeight(frame.rect.height());
1887     break;
1888   }
1889
1890   constrain(TopLeft);
1891
1892   if (flags.shaded) {
1893     blackbox_attrib.flags ^= AttribShaded;
1894     blackbox_attrib.attrib ^= AttribShaded;
1895     flags.shaded = False;
1896   }
1897
1898   flags.maximized = button;
1899
1900   configure(frame.changing.x(), frame.changing.y(),
1901             frame.changing.width(), frame.changing.height());
1902   if (flags.focused)
1903     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1904   redrawAllButtons(); // in case it is not called in configure()
1905   setState(current_state);
1906 }
1907
1908
1909 // re-maximizes the window to take into account availableArea changes
1910 void BlackboxWindow::remaximize(void) {
1911   if (flags.shaded) {
1912     // we only update the window's attributes otherwise we lose the shade bit
1913     switch(flags.maximized) {
1914     case 1:
1915       blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1916       blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1917       break;
1918
1919     case 2:
1920       blackbox_attrib.flags |= AttribMaxVert;
1921       blackbox_attrib.attrib |= AttribMaxVert;
1922       break;
1923
1924     case 3:
1925       blackbox_attrib.flags |= AttribMaxHoriz;
1926       blackbox_attrib.attrib |= AttribMaxHoriz;
1927       break;
1928     }
1929     return;
1930   }
1931
1932   // save the original dimensions because maximize will wipe them out
1933   int premax_x = blackbox_attrib.premax_x,
1934     premax_y = blackbox_attrib.premax_y,
1935     premax_w = blackbox_attrib.premax_w,
1936     premax_h = blackbox_attrib.premax_h;
1937
1938   unsigned int button = flags.maximized;
1939   flags.maximized = 0; // trick maximize() into working
1940   maximize(button);
1941
1942   // restore saved values
1943   blackbox_attrib.premax_x = premax_x;
1944   blackbox_attrib.premax_y = premax_y;
1945   blackbox_attrib.premax_w = premax_w;
1946   blackbox_attrib.premax_h = premax_h;
1947 }
1948
1949
1950 void BlackboxWindow::setWorkspace(unsigned int n) {
1951   blackbox_attrib.flags |= AttribWorkspace;
1952   blackbox_attrib.workspace = n;
1953   if (n == BSENTINEL) { // iconified window
1954     /*
1955        we set the workspace to 'all workspaces' so that taskbars will show the
1956        window. otherwise, it made uniconifying a window imposible without the
1957        blackbox workspace menu
1958     */
1959     n = 0xffffffff;
1960   }
1961   xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
1962 }
1963
1964
1965 void BlackboxWindow::shade(void) {
1966   if (flags.shaded) {
1967     XResizeWindow(blackbox->getXDisplay(), frame.window,
1968                   frame.inside_w, frame.inside_h);
1969     flags.shaded = False;
1970     blackbox_attrib.flags ^= AttribShaded;
1971     blackbox_attrib.attrib ^= AttribShaded;
1972
1973     setState(NormalState);
1974
1975     // set the frame rect to the normal size
1976     frame.rect.setHeight(client.rect.height() + frame.margin.top +
1977                          frame.margin.bottom);
1978   } else {
1979     if (! (decorations & Decor_Titlebar))
1980       return; // can't shade it without a titlebar!
1981
1982     XResizeWindow(blackbox->getXDisplay(), frame.window,
1983                   frame.inside_w, frame.title_h);
1984     flags.shaded = True;
1985     blackbox_attrib.flags |= AttribShaded;
1986     blackbox_attrib.attrib |= AttribShaded;
1987
1988     setState(IconicState);
1989
1990     // set the frame rect to the shaded size
1991     frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
1992   }
1993 }
1994
1995
1996 /*
1997  * (Un)Sticks a window and its relatives.
1998  */
1999 void BlackboxWindow::stick(void) {
2000   if (flags.stuck) {
2001     blackbox_attrib.flags ^= AttribOmnipresent;
2002     blackbox_attrib.attrib ^= AttribOmnipresent;
2003
2004     flags.stuck = False;
2005     
2006     for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
2007       if (i != blackbox_attrib.workspace)
2008         screen->getWorkspace(i)->removeWindow(this, True);
2009
2010     if (! flags.iconic)
2011       screen->reassociateWindow(this, BSENTINEL, True);
2012     // temporary fix since sticky windows suck. set the hint to what we
2013     // actually hold in our data.
2014     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
2015                     blackbox_attrib.workspace);
2016
2017     setState(current_state);
2018   } else {
2019     flags.stuck = True;
2020
2021     blackbox_attrib.flags |= AttribOmnipresent;
2022     blackbox_attrib.attrib |= AttribOmnipresent;
2023
2024     // temporary fix since sticky windows suck. set the hint to a different
2025     // value than that contained in the class' data.
2026     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
2027                     0xffffffff);
2028     
2029     for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
2030       if (i != blackbox_attrib.workspace)
2031         screen->getWorkspace(i)->addWindow(this, False, True);
2032
2033     setState(current_state);
2034   }
2035   // go up the chain
2036   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
2037       client.transient_for->isStuck() != flags.stuck)
2038     client.transient_for->stick();
2039   // go down the chain
2040   BlackboxWindowList::iterator it;
2041   const BlackboxWindowList::iterator end = client.transientList.end();
2042   for (it = client.transientList.begin(); it != end; ++it)
2043     if ((*it)->isStuck() != flags.stuck)
2044       (*it)->stick();
2045 }
2046
2047
2048 void BlackboxWindow::redrawWindowFrame(void) const {
2049   if (decorations & Decor_Titlebar) {
2050     if (flags.focused) {
2051       if (frame.ftitle)
2052         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2053                                    frame.title, frame.ftitle);
2054       else
2055         XSetWindowBackground(blackbox->getXDisplay(),
2056                              frame.title, frame.ftitle_pixel);
2057     } else {
2058       if (frame.utitle)
2059         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2060                                    frame.title, frame.utitle);
2061       else
2062         XSetWindowBackground(blackbox->getXDisplay(),
2063                              frame.title, frame.utitle_pixel);
2064     }
2065     XClearWindow(blackbox->getXDisplay(), frame.title);
2066
2067     redrawLabel();
2068     redrawAllButtons();
2069   }
2070
2071   if (decorations & Decor_Handle) {
2072     if (flags.focused) {
2073       if (frame.fhandle)
2074         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2075                                    frame.handle, frame.fhandle);
2076       else
2077         XSetWindowBackground(blackbox->getXDisplay(),
2078                              frame.handle, frame.fhandle_pixel);
2079
2080       if (frame.fgrip) {
2081         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2082                                    frame.left_grip, frame.fgrip);
2083         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2084                                    frame.right_grip, frame.fgrip);
2085       } else {
2086         XSetWindowBackground(blackbox->getXDisplay(),
2087                              frame.left_grip, frame.fgrip_pixel);
2088         XSetWindowBackground(blackbox->getXDisplay(),
2089                              frame.right_grip, frame.fgrip_pixel);
2090       }
2091     } else {
2092       if (frame.uhandle)
2093         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2094                                    frame.handle, frame.uhandle);
2095       else
2096         XSetWindowBackground(blackbox->getXDisplay(),
2097                              frame.handle, frame.uhandle_pixel);
2098
2099       if (frame.ugrip) {
2100         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2101                                    frame.left_grip, frame.ugrip);
2102         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2103                                    frame.right_grip, frame.ugrip);
2104       } else {
2105         XSetWindowBackground(blackbox->getXDisplay(),
2106                              frame.left_grip, frame.ugrip_pixel);
2107         XSetWindowBackground(blackbox->getXDisplay(),
2108                              frame.right_grip, frame.ugrip_pixel);
2109       }
2110     }
2111     XClearWindow(blackbox->getXDisplay(), frame.handle);
2112     XClearWindow(blackbox->getXDisplay(), frame.left_grip);
2113     XClearWindow(blackbox->getXDisplay(), frame.right_grip);
2114   }
2115
2116   if (decorations & Decor_Border) {
2117     if (flags.focused)
2118       XSetWindowBorder(blackbox->getXDisplay(),
2119                        frame.plate, frame.fborder_pixel);
2120     else
2121       XSetWindowBorder(blackbox->getXDisplay(),
2122                        frame.plate, frame.uborder_pixel);
2123   }
2124 }
2125
2126
2127 void BlackboxWindow::setFocusFlag(bool focus) {
2128   // only focus a window if it is visible
2129   if (focus && ! flags.visible)
2130     return;
2131
2132   flags.focused = focus;
2133
2134   redrawWindowFrame();
2135
2136   if (flags.focused)
2137     blackbox->setFocusedWindow(this);
2138  
2139   if (! flags.iconic) {
2140     // iconic windows arent in a workspace menu!
2141     if (flags.stuck)
2142       screen->getCurrentWorkspace()->setFocused(this, isFocused());
2143     else
2144       screen->getWorkspace(blackbox_attrib.workspace)->
2145         setFocused(this, flags.focused);
2146   }
2147 }
2148
2149
2150 void BlackboxWindow::installColormap(bool install) {
2151   int i = 0, ncmap = 0;
2152   Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2153                                             client.window, &ncmap);
2154   if (cmaps) {
2155     XWindowAttributes wattrib;
2156     if (XGetWindowAttributes(blackbox->getXDisplay(),
2157                              client.window, &wattrib)) {
2158       if (install) {
2159         // install the window's colormap
2160         for (i = 0; i < ncmap; i++) {
2161           if (*(cmaps + i) == wattrib.colormap)
2162             // this window is using an installed color map... do not install
2163             install = False;
2164         }
2165         // otherwise, install the window's colormap
2166         if (install)
2167           XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2168       } else {
2169         // uninstall the window's colormap
2170         for (i = 0; i < ncmap; i++) {
2171           if (*(cmaps + i) == wattrib.colormap)
2172             // we found the colormap to uninstall
2173             XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2174         }
2175       }
2176     }
2177
2178     XFree(cmaps);
2179   }
2180 }
2181
2182
2183 void BlackboxWindow::setAllowedActions(void) {
2184   Atom actions[7];
2185   int num = 0;
2186   
2187   actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2188   actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2189   actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2190
2191   if (functions & Func_Move)
2192     actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2193   if (functions & Func_Resize)
2194     actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2195   if (functions & Func_Maximize) {
2196     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2197     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2198   }
2199
2200   xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2201                   actions, num);
2202 }
2203
2204
2205 void BlackboxWindow::setState(unsigned long new_state) {
2206   current_state = new_state;
2207
2208   unsigned long state[2];
2209   state[0] = current_state;
2210   state[1] = None;
2211   xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2212  
2213   xatom->setValue(client.window, XAtom::blackbox_attributes,
2214                   XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2215                   PropBlackboxAttributesElements);
2216
2217   Atom netstate[8];
2218   int num = 0;
2219   if (flags.modal)
2220     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2221   if (flags.shaded)
2222     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2223   if (flags.iconic)
2224     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2225   if (flags.skip_taskbar)
2226     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2227   if (flags.skip_pager)
2228     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2229   if (flags.fullscreen)
2230     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2231   if (flags.maximized == 1 || flags.maximized == 2)
2232     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2233   if (flags.maximized == 1 || flags.maximized == 3)
2234     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2235   xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2236                   netstate, num);
2237 }
2238
2239
2240 bool BlackboxWindow::getState(void) {
2241   bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2242                              current_state);
2243   if (! ret) current_state = 0;
2244   return ret;
2245 }
2246
2247
2248 void BlackboxWindow::restoreAttributes(void) {
2249   unsigned long num = PropBlackboxAttributesElements;
2250   BlackboxAttributes *net;
2251   if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2252                         XAtom::blackbox_attributes, num,
2253                         (unsigned long **)&net))
2254     return;
2255   if (num < PropBlackboxAttributesElements) {
2256     delete [] net;
2257     return;
2258   }
2259
2260   if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2261     flags.shaded = False;
2262     unsigned long orig_state = current_state;
2263     shade();
2264
2265     /*
2266       At this point in the life of a window, current_state should only be set
2267       to IconicState if the window was an *icon*, not if it was shaded.
2268     */
2269     if (orig_state != IconicState)
2270       current_state = WithdrawnState;
2271  }
2272
2273   if (net->workspace != screen->getCurrentWorkspaceID() &&
2274       net->workspace < screen->getWorkspaceCount())
2275     screen->reassociateWindow(this, net->workspace, True);
2276
2277   if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2278       (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2279     // set to WithdrawnState so it will be mapped on the new workspace
2280     if (current_state == NormalState) current_state = WithdrawnState;
2281   } else if (current_state == WithdrawnState) {
2282     // the window is on this workspace and is Withdrawn, so it is waiting to
2283     // be mapped
2284     current_state = NormalState;
2285   }
2286
2287   if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent &&
2288       ! flags.stuck) {
2289     stick();
2290
2291     // if the window was on another workspace, it was going to be hidden. this
2292     // specifies that the window should be mapped since it is sticky.
2293     if (current_state == WithdrawnState) current_state = NormalState;
2294   }
2295
2296   if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2297     int x = net->premax_x, y = net->premax_y;
2298     unsigned int w = net->premax_w, h = net->premax_h;
2299     flags.maximized = 0;
2300
2301     unsigned int m = 0;
2302     if ((net->flags & AttribMaxHoriz) &&
2303         (net->flags & AttribMaxVert))
2304       m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2305     else if (net->flags & AttribMaxVert)
2306       m = (net->attrib & AttribMaxVert) ? 2 : 0;
2307     else if (net->flags & AttribMaxHoriz)
2308       m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2309
2310     if (m) maximize(m);
2311
2312     blackbox_attrib.premax_x = x;
2313     blackbox_attrib.premax_y = y;
2314     blackbox_attrib.premax_w = w;
2315     blackbox_attrib.premax_h = h;
2316   }
2317
2318   if (net->flags & AttribDecoration) {
2319     switch (net->decoration) {
2320     case DecorNone:
2321       enableDecor(False);
2322       break;
2323
2324     /* since tools only let you toggle this anyways, we'll just make that all
2325        it supports for now.
2326      */
2327     default:
2328     case DecorNormal:
2329     case DecorTiny:
2330     case DecorTool:
2331       enableDecor(True);
2332       break;
2333     }
2334   }
2335
2336   // with the state set it will then be the map event's job to read the
2337   // window's state and behave accordingly
2338
2339   delete [] net;
2340 }
2341
2342
2343 /*
2344  * Positions the Rect r according the the client window position and
2345  * window gravity.
2346  */
2347 void BlackboxWindow::applyGravity(Rect &r) {
2348   // apply horizontal window gravity
2349   switch (client.win_gravity) {
2350   default:
2351   case NorthWestGravity:
2352   case SouthWestGravity:
2353   case WestGravity:
2354     r.setX(client.rect.x());
2355     break;
2356
2357   case NorthGravity:
2358   case SouthGravity:
2359   case CenterGravity:
2360     r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2361     break;
2362
2363   case NorthEastGravity:
2364   case SouthEastGravity:
2365   case EastGravity:
2366     r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2367     break;
2368
2369   case ForgetGravity:
2370   case StaticGravity:
2371     r.setX(client.rect.x() - frame.margin.left);
2372     break;
2373   }
2374
2375   // apply vertical window gravity
2376   switch (client.win_gravity) {
2377   default:
2378   case NorthWestGravity:
2379   case NorthEastGravity:
2380   case NorthGravity:
2381     r.setY(client.rect.y());
2382     break;
2383
2384   case CenterGravity:
2385   case EastGravity:
2386   case WestGravity:
2387     r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2388     break;
2389
2390   case SouthWestGravity:
2391   case SouthEastGravity:
2392   case SouthGravity:
2393     r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2394     break;
2395
2396   case ForgetGravity:
2397   case StaticGravity:
2398     r.setY(client.rect.y() - frame.margin.top);
2399     break;
2400   }
2401 }
2402
2403
2404 /*
2405  * The reverse of the applyGravity function.
2406  *
2407  * Positions the Rect r according to the frame window position and
2408  * window gravity.
2409  */
2410 void BlackboxWindow::restoreGravity(Rect &r) {
2411   // restore horizontal window gravity
2412   switch (client.win_gravity) {
2413   default:
2414   case NorthWestGravity:
2415   case SouthWestGravity:
2416   case WestGravity:
2417     r.setX(frame.rect.x());
2418     break;
2419
2420   case NorthGravity:
2421   case SouthGravity:
2422   case CenterGravity:
2423     r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2424     break;
2425
2426   case NorthEastGravity:
2427   case SouthEastGravity:
2428   case EastGravity:
2429     r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2430     break;
2431
2432   case ForgetGravity:
2433   case StaticGravity:
2434     r.setX(frame.rect.x() + frame.margin.left);
2435     break;
2436   }
2437
2438   // restore vertical window gravity
2439   switch (client.win_gravity) {
2440   default:
2441   case NorthWestGravity:
2442   case NorthEastGravity:
2443   case NorthGravity:
2444     r.setY(frame.rect.y());
2445     break;
2446
2447   case CenterGravity:
2448   case EastGravity:
2449   case WestGravity:
2450     r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2451     break;
2452
2453   case SouthWestGravity:
2454   case SouthEastGravity:
2455   case SouthGravity:
2456     r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2457     break;
2458
2459   case ForgetGravity:
2460   case StaticGravity:
2461     r.setY(frame.rect.y() + frame.margin.top);
2462     break;
2463   }
2464 }
2465
2466
2467 void BlackboxWindow::redrawLabel(void) const {
2468   if (flags.focused) {
2469     if (frame.flabel)
2470       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2471                                  frame.label, frame.flabel);
2472     else
2473       XSetWindowBackground(blackbox->getXDisplay(),
2474                            frame.label, frame.flabel_pixel);
2475   } else {
2476     if (frame.ulabel)
2477       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2478                                  frame.label, frame.ulabel);
2479     else
2480       XSetWindowBackground(blackbox->getXDisplay(),
2481                            frame.label, frame.ulabel_pixel);
2482   }
2483   XClearWindow(blackbox->getXDisplay(), frame.label);
2484
2485   WindowStyle *style = screen->getWindowStyle();
2486
2487   int pos = frame.bevel_w * 2;
2488   style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2489   style->font->drawString(frame.label, pos, 1,
2490                           (flags.focused ? style->l_text_focus :
2491                            style->l_text_unfocus),
2492                           client.title);
2493 }
2494
2495
2496 void BlackboxWindow::redrawAllButtons(void) const {
2497   if (frame.iconify_button) redrawIconifyButton(False);
2498   if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2499   if (frame.close_button) redrawCloseButton(False);
2500 }
2501
2502
2503 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2504   if (! pressed) {
2505     if (flags.focused) {
2506       if (frame.fbutton)
2507         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2508                                    frame.iconify_button, frame.fbutton);
2509       else
2510         XSetWindowBackground(blackbox->getXDisplay(),
2511                              frame.iconify_button, frame.fbutton_pixel);
2512     } else {
2513       if (frame.ubutton)
2514         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2515                                    frame.iconify_button, frame.ubutton);
2516       else
2517         XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2518                              frame.ubutton_pixel);
2519     }
2520   } else {
2521     if (frame.pbutton)
2522       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2523                                  frame.iconify_button, frame.pbutton);
2524     else
2525       XSetWindowBackground(blackbox->getXDisplay(),
2526                            frame.iconify_button, frame.pbutton_pixel);
2527   }
2528   XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2529
2530   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2531            screen->getWindowStyle()->b_pic_unfocus);
2532   XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2533                  2, (frame.button_w - 5), (frame.button_w - 5), 2);
2534 }
2535
2536
2537 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2538   if (! pressed) {
2539     if (flags.focused) {
2540       if (frame.fbutton)
2541         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2542                                    frame.maximize_button, frame.fbutton);
2543       else
2544         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2545                              frame.fbutton_pixel);
2546     } else {
2547       if (frame.ubutton)
2548         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2549                                    frame.maximize_button, frame.ubutton);
2550       else
2551         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2552                              frame.ubutton_pixel);
2553     }
2554   } else {
2555     if (frame.pbutton)
2556       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2557                                  frame.maximize_button, frame.pbutton);
2558     else
2559       XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2560                            frame.pbutton_pixel);
2561   }
2562   XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2563
2564   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2565            screen->getWindowStyle()->b_pic_unfocus);
2566   XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2567                  2, 2, (frame.button_w - 5), (frame.button_w - 5));
2568   XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2569             2, 3, (frame.button_w - 3), 3);
2570 }
2571
2572
2573 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2574   if (! pressed) {
2575     if (flags.focused) {
2576       if (frame.fbutton)
2577         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2578                                    frame.fbutton);
2579       else
2580         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2581                              frame.fbutton_pixel);
2582     } else {
2583       if (frame.ubutton)
2584         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2585                                    frame.ubutton);
2586       else
2587         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2588                              frame.ubutton_pixel);
2589     }
2590   } else {
2591     if (frame.pbutton)
2592       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2593                                  frame.close_button, frame.pbutton);
2594     else
2595       XSetWindowBackground(blackbox->getXDisplay(),
2596                            frame.close_button, frame.pbutton_pixel);
2597   }
2598   XClearWindow(blackbox->getXDisplay(), frame.close_button);
2599
2600   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2601            screen->getWindowStyle()->b_pic_unfocus);
2602   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2603             2, 2, (frame.button_w - 3), (frame.button_w - 3));
2604   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2605             2, (frame.button_w - 3), (frame.button_w - 3), 2);
2606 }
2607
2608
2609 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2610   if (re->window != client.window)
2611     return;
2612
2613 #ifdef    DEBUG
2614   fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2615           client.window);
2616 #endif // DEBUG
2617
2618   switch (current_state) {
2619   case IconicState:
2620     iconify();
2621     break;
2622
2623   case WithdrawnState:
2624     withdraw();
2625     break;
2626
2627   case NormalState:
2628   case InactiveState:
2629   case ZoomState:
2630   default:
2631     show();
2632     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2633     if (isNormal()) {
2634       if (! blackbox->isStartup()) {
2635         XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2636         if (screen->doFocusNew()|| (isTransient() && getTransientFor() &&
2637                                     getTransientFor()->isFocused())) {
2638           setInputFocus();
2639         }
2640         if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2641           int x, y, rx, ry;
2642           Window c, r;
2643           unsigned int m;
2644           XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2645                         &r, &c, &rx, &ry, &x, &y, &m);
2646           beginMove(rx, ry);
2647         }
2648       }
2649     }
2650     break;
2651   }
2652 }
2653
2654
2655 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2656   if (ue->window != client.window)
2657     return;
2658
2659 #ifdef    DEBUG
2660   fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2661           client.window);
2662 #endif // DEBUG
2663
2664   screen->unmanageWindow(this, False);
2665 }
2666
2667
2668 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2669   if (de->window != client.window)
2670     return;
2671
2672 #ifdef    DEBUG
2673   fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2674           client.window);
2675 #endif // DEBUG
2676
2677   screen->unmanageWindow(this, False);
2678 }
2679
2680
2681 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2682   if (re->window != client.window || re->parent == frame.plate)
2683     return;
2684
2685 #ifdef    DEBUG
2686   fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2687           "0x%lx.\n", client.window, re->parent);
2688 #endif // DEBUG
2689
2690   XEvent ev;
2691   ev.xreparent = *re;
2692   XPutBackEvent(blackbox->getXDisplay(), &ev);
2693   screen->unmanageWindow(this, True);
2694 }
2695
2696
2697 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2698   if (pe->state == PropertyDelete)
2699     return;
2700
2701 #ifdef    DEBUG
2702   fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2703           client.window);
2704 #endif
2705
2706   switch(pe->atom) {
2707   case XA_WM_CLASS:
2708   case XA_WM_CLIENT_MACHINE:
2709   case XA_WM_COMMAND:
2710     break;
2711
2712   case XA_WM_TRANSIENT_FOR: {
2713     // determine if this is a transient window
2714     getTransientInfo();
2715
2716     // adjust the window decorations based on transience
2717     if (isTransient()) {
2718       functions &= ~Func_Maximize;
2719       setAllowedActions();
2720       setupDecor();
2721     }
2722
2723     reconfigure();
2724   }
2725     break;
2726
2727   case XA_WM_HINTS:
2728     getWMHints();
2729     break;
2730
2731   case XA_WM_ICON_NAME:
2732     getWMIconName();
2733     if (flags.iconic) screen->propagateWindowName(this);
2734     break;
2735
2736   case XAtom::net_wm_name:
2737   case XA_WM_NAME:
2738     getWMName();
2739
2740     if (decorations & Decor_Titlebar)
2741       redrawLabel();
2742
2743     screen->propagateWindowName(this);
2744     break;
2745
2746   case XA_WM_NORMAL_HINTS: {
2747     getWMNormalHints();
2748
2749     if ((client.normal_hint_flags & PMinSize) &&
2750         (client.normal_hint_flags & PMaxSize)) {
2751       // the window now can/can't resize itself, so the buttons need to be
2752       // regrabbed.
2753       ungrabButtons();
2754       if (client.max_width <= client.min_width &&
2755           client.max_height <= client.min_height) {
2756         functions &= ~(Func_Resize | Func_Maximize);
2757       } else {
2758         if (! isTransient())
2759           functions |= Func_Maximize;
2760         functions |= Func_Resize;
2761       }
2762       grabButtons();
2763       setAllowedActions();
2764       setupDecor();
2765     }
2766
2767     Rect old_rect = frame.rect;
2768
2769     upsize();
2770
2771     if (old_rect != frame.rect)
2772       reconfigure();
2773
2774     break;
2775   }
2776
2777   default:
2778     if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2779       getWMProtocols();
2780
2781       if ((decorations & Decor_Close) && (! frame.close_button)) {
2782         createCloseButton();
2783         if (decorations & Decor_Titlebar) {
2784           positionButtons(True);
2785           XMapSubwindows(blackbox->getXDisplay(), frame.title);
2786         }
2787         if (windowmenu) windowmenu->reconfigure();
2788       }
2789     } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2790       updateStrut();
2791     }
2792
2793     break;
2794   }
2795 }
2796
2797
2798 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2799 #ifdef DEBUG
2800   fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2801 #endif
2802
2803   if (frame.label == ee->window && (decorations & Decor_Titlebar))
2804     redrawLabel();
2805   else if (frame.close_button == ee->window)
2806     redrawCloseButton(False);
2807   else if (frame.maximize_button == ee->window)
2808     redrawMaximizeButton(flags.maximized);
2809   else if (frame.iconify_button == ee->window)
2810     redrawIconifyButton(False);
2811 }
2812
2813
2814 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2815   if (cr->window != client.window || flags.iconic)
2816     return;
2817
2818   if (cr->value_mask & CWBorderWidth)
2819     client.old_bw = cr->border_width;
2820
2821   if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2822     Rect req = frame.rect;
2823
2824     if (cr->value_mask & (CWX | CWY)) {
2825       if (cr->value_mask & CWX)
2826         client.rect.setX(cr->x);
2827       if (cr->value_mask & CWY)
2828         client.rect.setY(cr->y);
2829
2830       applyGravity(req);
2831     }
2832
2833     if (cr->value_mask & CWWidth)
2834       req.setWidth(cr->width + frame.margin.left + frame.margin.right);
2835
2836     if (cr->value_mask & CWHeight)
2837       req.setHeight(cr->height + frame.margin.top + frame.margin.bottom);
2838
2839     configure(req.x(), req.y(), req.width(), req.height());
2840   }
2841
2842   if (cr->value_mask & CWStackMode && !isDesktop()) {
2843     switch (cr->detail) {
2844     case Below:
2845     case BottomIf:
2846       screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2847       break;
2848
2849     case Above:
2850     case TopIf:
2851     default:
2852       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2853       break;
2854     }
2855   }
2856 }
2857
2858
2859 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
2860 #ifdef DEBUG
2861   fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
2862           client.window);
2863 #endif
2864
2865   if (frame.maximize_button == be->window && be->button <= 3) {
2866     redrawMaximizeButton(True);
2867   } else if (be->button == 1 || (be->button == 3 && be->state == ModMask)) {
2868     if (! flags.focused)
2869       setInputFocus();
2870
2871     if (frame.iconify_button == be->window) {
2872       redrawIconifyButton(True);
2873     } else if (frame.close_button == be->window) {
2874       redrawCloseButton(True);
2875     } else if (frame.plate == be->window) {
2876       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2877
2878       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2879
2880       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2881     } else {
2882       if (frame.title == be->window || frame.label == be->window) {
2883         if (((be->time - lastButtonPressTime) <=
2884              blackbox->getDoubleClickInterval()) ||
2885             (be->state == ControlMask)) {
2886           lastButtonPressTime = 0;
2887           shade();
2888         } else {
2889           lastButtonPressTime = be->time;
2890         }
2891       }
2892
2893       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2894
2895       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2896     }
2897   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2898              (be->window != frame.close_button)) {
2899     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2900   } else if (windowmenu && be->button == 3 &&
2901              (frame.title == be->window || frame.label == be->window ||
2902               frame.handle == be->window || frame.window == be->window)) {
2903     if (windowmenu->isVisible()) {
2904       windowmenu->hide();
2905     } else {
2906       int mx = be->x_root - windowmenu->getWidth() / 2,
2907           my = be->y_root - windowmenu->getHeight() / 2;
2908
2909       // snap the window menu into a corner/side if necessary
2910       int left_edge, right_edge, top_edge, bottom_edge;
2911
2912       /*
2913          the " + (frame.border_w * 2) - 1" bits are to get the proper width
2914          and height of the menu, as the sizes returned by it do not include
2915          the borders.
2916        */
2917       left_edge = frame.rect.x();
2918       right_edge = frame.rect.right() -
2919         (windowmenu->getWidth() + (frame.border_w * 2) - 1);
2920       top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
2921       bottom_edge = client.rect.bottom() -
2922         (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
2923         (frame.border_w + frame.mwm_border_w);
2924
2925       if (mx < left_edge)
2926         mx = left_edge;
2927       if (mx > right_edge)
2928         mx = right_edge;
2929       if (my < top_edge)
2930         my = top_edge;
2931       if (my > bottom_edge)
2932         my = bottom_edge;
2933
2934       windowmenu->move(mx, my);
2935       windowmenu->show();
2936       XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2937       XRaiseWindow(blackbox->getXDisplay(),
2938                    windowmenu->getSendToMenu()->getWindowID());
2939     }
2940   // mouse wheel up
2941   } else if (be->button == 4) {
2942     if ((be->window == frame.label ||
2943          be->window == frame.title ||
2944          be->window == frame.maximize_button ||
2945          be->window == frame.iconify_button ||
2946          be->window == frame.close_button) &&
2947         ! flags.shaded)
2948       shade();
2949   // mouse wheel down
2950   } else if (be->button == 5) {
2951     if ((be->window == frame.label ||
2952          be->window == frame.title ||
2953          be->window == frame.maximize_button ||
2954          be->window == frame.iconify_button ||
2955          be->window == frame.close_button) &&
2956         flags.shaded)
2957       shade();
2958   }
2959 }
2960
2961
2962 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
2963 #ifdef DEBUG
2964   fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
2965           client.window);
2966 #endif
2967
2968   if (re->window == frame.maximize_button &&
2969       re->button >= 1 && re->button <= 3) {
2970     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2971         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2972       maximize(re->button);
2973     } else {
2974       redrawMaximizeButton(flags.maximized);
2975     }
2976   } else if (re->window == frame.iconify_button && re->button == 1) {
2977     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2978         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2979       iconify();
2980     } else {
2981       redrawIconifyButton(False);
2982     }
2983   } else if (re->window == frame.close_button & re->button == 1) {
2984     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2985         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
2986       close();
2987     redrawCloseButton(False);
2988   } else if (flags.moving) {
2989     endMove();
2990   } else if (flags.resizing) {
2991     endResize();
2992   } else if (re->window == frame.window) {
2993     if (re->button == 2 && re->state == ModMask)
2994       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2995   }
2996 }
2997
2998
2999
3000 void BlackboxWindow::beginMove(int x_root, int y_root) {
3001   assert(! (flags.resizing || flags.moving));
3002
3003   /*
3004     Only one window can be moved/resized at a time. If another window is already
3005     being moved or resized, then stop it before whating to work with this one.
3006   */
3007   BlackboxWindow *changing = blackbox->getChangingWindow();
3008   if (changing && changing != this) {
3009     if (changing->flags.moving)
3010       changing->endMove();
3011     else // if (changing->flags.resizing)
3012       changing->endResize();
3013   }
3014   
3015   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3016                PointerMotionMask | ButtonReleaseMask,
3017                GrabModeAsync, GrabModeAsync,
3018                None, blackbox->getMoveCursor(), CurrentTime);
3019
3020   if (windowmenu && windowmenu->isVisible())
3021     windowmenu->hide();
3022
3023   flags.moving = True;
3024   blackbox->setChangingWindow(this);
3025
3026   if (! screen->doOpaqueMove()) {
3027     XGrabServer(blackbox->getXDisplay());
3028
3029     frame.changing = frame.rect;
3030     screen->showPosition(frame.changing.x(), frame.changing.y());
3031
3032     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3033                    screen->getOpGC(),
3034                    frame.changing.x(),
3035                    frame.changing.y(),
3036                    frame.changing.width() - 1,
3037                    frame.changing.height() - 1);
3038   }
3039
3040   frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3041   frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3042 }
3043
3044
3045 void BlackboxWindow::doMove(int x_root, int y_root) {
3046   assert(flags.moving);
3047   assert(blackbox->getChangingWindow() == this);
3048
3049   int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3050   dx -= frame.border_w;
3051   dy -= frame.border_w;
3052
3053   if (screen->doWorkspaceWarping())
3054     if (doWorkspaceWarping(x_root, y_root, dx, dy))
3055       return;
3056
3057   doWindowSnapping(dx, dy);
3058
3059   if (screen->doOpaqueMove()) {
3060     configure(dx, dy, frame.rect.width(), frame.rect.height());
3061   } else {
3062     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3063                    screen->getOpGC(),
3064                    frame.changing.x(),
3065                    frame.changing.y(),
3066                    frame.changing.width() - 1,
3067                    frame.changing.height() - 1);
3068
3069     frame.changing.setPos(dx, dy);
3070
3071     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3072                    screen->getOpGC(),
3073                    frame.changing.x(),
3074                    frame.changing.y(),
3075                    frame.changing.width() - 1,
3076                    frame.changing.height() - 1);
3077   }
3078
3079   screen->showPosition(dx, dy);
3080 }
3081
3082
3083 bool BlackboxWindow::doWorkspaceWarping(int x_root, int y_root,
3084                                         int dx, int dy) {
3085   // workspace warping
3086   bool warp = False;
3087   unsigned int dest = screen->getCurrentWorkspaceID();
3088   if (x_root <= 0) {
3089     warp = True;
3090
3091     if (dest > 0) dest--;
3092     else dest = screen->getNumberOfWorkspaces() - 1;
3093
3094   } else if (x_root >= screen->getRect().right()) {
3095     warp = True;
3096
3097     if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3098     else dest = 0;
3099   }
3100   if (! warp)
3101     return false;
3102
3103   endMove();
3104   bool focus = flags.focused; // had focus while moving?
3105   if (! flags.stuck)
3106     screen->reassociateWindow(this, dest, False);
3107   screen->changeWorkspaceID(dest);
3108   if (focus)
3109     setInputFocus();
3110
3111   /*
3112      We grab the X server here because we are moving the window and then the
3113      mouse cursor. When one moves, it could end up putting the mouse cursor
3114      over another window for a moment. This can cause the warp to iniate a
3115      move on another window.
3116   */
3117   XGrabServer(blackbox->getXDisplay());
3118   int dest_x;
3119   if (x_root <= 0) {
3120     dest_x = screen->getRect().right() - 1;
3121     configure(dx + (screen->getRect().width() - 1), dy,
3122               frame.rect.width(), frame.rect.height());
3123   } else {
3124     dest_x = 0;
3125     configure(dx - (screen->getRect().width() - 1), dy,
3126               frame.rect.width(), frame.rect.height());
3127   }
3128   XWarpPointer(blackbox->getXDisplay(), None, 
3129                screen->getRootWindow(), 0, 0, 0, 0,
3130                dest_x, y_root);
3131   XUngrabServer(blackbox->getXDisplay());
3132
3133   beginMove(dest_x, y_root);
3134   return true;
3135 }
3136
3137
3138 void BlackboxWindow::doWindowSnapping(int &dx, int &dy) {
3139   // how much resistance to edges to provide
3140   const int resistance_size = screen->getResistanceSize();
3141
3142   // how far away to snap
3143   const int snap_distance = screen->getSnapThreshold();
3144
3145   // how to snap windows
3146   const int snap_to_windows = screen->getWindowToWindowSnap();
3147   const int snap_to_edges = screen->getWindowToEdgeSnap();
3148   // the amount of space away from the edge to provide resistance/snap
3149   const int snap_offset = screen->getSnapOffset();
3150
3151   // find the geomeetery where the moving window currently is
3152   const Rect &moving = screen->doOpaqueMove() ? frame.rect : frame.changing;
3153
3154   // window corners
3155   const int wleft = dx,
3156            wright = dx + frame.rect.width() - 1,
3157              wtop = dy,
3158           wbottom = dy + frame.rect.height() - 1;
3159
3160   if (snap_to_windows) {
3161     RectList rectlist;
3162
3163     Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3164     assert(w);
3165
3166     // add windows on the workspace to the rect list
3167     const BlackboxWindowList& stack_list = w->getStackingList();
3168     BlackboxWindowList::const_iterator st_it, st_end = stack_list.end();
3169     for (st_it = stack_list.begin(); st_it != st_end; ++st_it)
3170       if (*st_it != this) // don't snap to ourself
3171         rectlist.push_back( (*st_it)->frameRect() );
3172
3173     // add the toolbar and the slit to the rect list.
3174     // (only if they are not hidden)
3175     Toolbar *tbar = screen->getToolbar();
3176     Slit *slit = screen->getSlit();
3177     Rect tbar_rect, slit_rect;
3178     unsigned int bwidth = screen->getBorderWidth() * 2;
3179
3180     if (! (screen->doHideToolbar() || tbar->isHidden())) {
3181       tbar_rect.setRect(tbar->getX(), tbar->getY(), tbar->getWidth() + bwidth,
3182                         tbar->getHeight() + bwidth);
3183       rectlist.push_back(tbar_rect);
3184     }
3185
3186     if (! slit->isHidden()) {
3187       slit_rect.setRect(slit->getX(), slit->getY(), slit->getWidth() + bwidth,
3188                         slit->getHeight() + bwidth);
3189       rectlist.push_back(slit_rect);
3190     }
3191
3192     RectList::const_iterator it, end = rectlist.end();
3193     for (it = rectlist.begin(); it != end; ++it) {
3194       bool snapped = False;
3195       const Rect &winrect = *it;
3196       Rect offsetrect;
3197       offsetrect.setCoords(winrect.left() - snap_offset,
3198                            winrect.top() - snap_offset,
3199                            winrect.right() + snap_offset,
3200                            winrect.bottom() + snap_offset);
3201
3202       if (snap_to_windows == BScreen::WindowResistance)
3203         // if the window is already over top of this snap target, then
3204         // resistance is futile, so just ignore it
3205         if (winrect.intersects(moving))
3206           continue;
3207
3208       int dleft, dright, dtop, dbottom;
3209
3210       // if the windows are in the same plane vertically
3211       if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3212           wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3213
3214         if (snap_to_windows == BScreen::WindowResistance) {
3215           dleft = wright - offsetrect.left();
3216           dright = offsetrect.right() - wleft;
3217
3218           // snap left of other window?
3219           if (dleft >= 0 && dleft < resistance_size &&
3220               dleft < (wright - wleft)) {
3221             dx = offsetrect.left() - frame.rect.width();
3222             snapped = True;
3223           }
3224           // snap right of other window?
3225           else if (dright >= 0 && dright < resistance_size &&
3226                    dright < (wright - wleft)) {
3227             dx = offsetrect.right() + 1;
3228             snapped = True;
3229           }
3230         } else { // BScreen::WindowSnap
3231           dleft = abs(wright - offsetrect.left());
3232           dright = abs(wleft - offsetrect.right());
3233
3234           // snap left of other window?
3235           if (dleft < snap_distance && dleft <= dright) {
3236             dx = offsetrect.left() - frame.rect.width();
3237             snapped = True;
3238           }
3239           // snap right of other window?
3240           else if (dright < snap_distance) {
3241             dx = offsetrect.right() + 1;
3242             snapped = True;
3243           }            
3244         }
3245
3246         if (snapped) {
3247           if (screen->getWindowCornerSnap()) {
3248             // try corner-snap to its other sides
3249             if (snap_to_windows == BScreen::WindowResistance) {
3250               dtop = winrect.top() - wtop;
3251               dbottom = wbottom - winrect.bottom();
3252               if (dtop > 0 && dtop < resistance_size) {
3253                 // if we're already past the top edge, then don't provide
3254                 // resistance
3255                 if (moving.top() >= winrect.top())
3256                   dy = winrect.top();
3257               } else if (dbottom > 0 && dbottom < resistance_size) {
3258                 // if we're already past the bottom edge, then don't provide
3259                 // resistance
3260                 if (moving.bottom() <= winrect.bottom())
3261                   dy = winrect.bottom() - frame.rect.height() + 1;
3262               }
3263             } else { // BScreen::WindowSnap
3264               dtop = abs(wtop - winrect.top());
3265               dbottom = abs(wbottom - winrect.bottom());
3266               if (dtop < snap_distance && dtop <= dbottom)
3267                 dy = winrect.top();
3268               else if (dbottom < snap_distance)
3269                 dy = winrect.bottom() - frame.rect.height() + 1;
3270             }
3271           }
3272
3273           continue;
3274         }
3275       }
3276
3277       // if the windows are on the same plane horizontally
3278       if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3279           wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3280
3281         if (snap_to_windows == BScreen::WindowResistance) {
3282           dtop = wbottom - offsetrect.top();
3283           dbottom = offsetrect.bottom() - wtop;
3284
3285           // snap top of other window?
3286           if (dtop >= 0 && dtop < resistance_size && dtop < (wbottom - wtop)) {
3287             dy = offsetrect.top() - frame.rect.height();
3288             snapped = True;
3289           }
3290           // snap bottom of other window?
3291           else if (dbottom >= 0 && dbottom < resistance_size &&
3292                    dbottom < (wbottom - wtop)) {
3293             dy = offsetrect.bottom() + 1;
3294             snapped = True;
3295           }
3296         } else { // BScreen::WindowSnap
3297           dtop = abs(wbottom - offsetrect.top());
3298           dbottom = abs(wtop - offsetrect.bottom());
3299
3300           // snap top of other window?
3301           if (dtop < snap_distance && dtop <= dbottom) {
3302             dy = offsetrect.top() - frame.rect.height();
3303             snapped = True;
3304           }
3305           // snap bottom of other window?
3306           else if (dbottom < snap_distance) {
3307             dy = offsetrect.bottom() + 1;
3308             snapped = True;
3309           }
3310
3311         }
3312
3313         if (snapped) {
3314           if (screen->getWindowCornerSnap()) {
3315             // try corner-snap to its other sides
3316             if (snap_to_windows == BScreen::WindowResistance) {
3317               dleft = winrect.left() - wleft;
3318               dright = wright - winrect.right();
3319               if (dleft > 0 && dleft < resistance_size) {
3320                 // if we're already past the left edge, then don't provide
3321                 // resistance
3322                 if (moving.left() >= winrect.left())
3323                   dx = winrect.left();
3324               } else if (dright > 0 && dright < resistance_size) {
3325                 // if we're already past the right edge, then don't provide
3326                 // resistance
3327                 if (moving.right() <= winrect.right())
3328                   dx = winrect.right() - frame.rect.width() + 1;
3329               }
3330             } else { // BScreen::WindowSnap
3331               dleft = abs(wleft - winrect.left());
3332               dright = abs(wright - winrect.right());
3333               if (dleft < snap_distance && dleft <= dright)
3334                 dx = winrect.left();
3335               else if (dright < snap_distance)
3336                 dx = winrect.right() - frame.rect.width() + 1;
3337             }
3338           }
3339
3340           continue;
3341         }
3342       }
3343     }
3344   }
3345
3346   if (snap_to_edges) {
3347     RectList rectlist;
3348
3349     // snap to the screen edges (and screen boundaries for xinerama)
3350 #ifdef    XINERAMA
3351     if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3352       rectlist.insert(rectlist.begin(),
3353                       screen->getXineramaAreas().begin(),
3354                       screen->getXineramaAreas().end());
3355     } else
3356 #endif // XINERAMA
3357       rectlist.push_back(screen->getRect());
3358
3359     RectList::const_iterator it, end = rectlist.end();
3360     for (it = rectlist.begin(); it != end; ++it) {
3361       const Rect &srect = *it;
3362       Rect offsetrect;
3363       offsetrect.setCoords(srect.left() + snap_offset,
3364                            srect.top() + snap_offset,
3365                            srect.right() - snap_offset,
3366                            srect.bottom() - snap_offset);
3367
3368       if (snap_to_edges == BScreen::WindowResistance) {
3369         // if we're not in the rectangle then don't snap to it.
3370         if (! srect.contains(moving))
3371           continue;
3372       } else { // BScreen::WindowSnap
3373         // if we're not in the rectangle then don't snap to it.
3374         if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3375                                     frame.rect.height())))
3376           continue;
3377       }
3378
3379       if (snap_to_edges == BScreen::WindowResistance) {
3380       int dleft = offsetrect.left() - wleft,
3381          dright = wright - offsetrect.right(),
3382            dtop = offsetrect.top() - wtop,
3383         dbottom = wbottom - offsetrect.bottom();
3384
3385         // snap left?
3386         if (dleft > 0 && dleft < resistance_size)
3387           dx = offsetrect.left();
3388         // snap right?
3389         else if (dright > 0 && dright < resistance_size)
3390           dx = offsetrect.right() - frame.rect.width() + 1;
3391
3392         // snap top?
3393         if (dtop > 0 && dtop < resistance_size)
3394           dy = offsetrect.top();
3395         // snap bottom?
3396         else if (dbottom > 0 && dbottom < resistance_size)
3397           dy = offsetrect.bottom() - frame.rect.height() + 1;
3398       } else { // BScreen::WindowSnap
3399         int dleft = abs(wleft - offsetrect.left()),
3400            dright = abs(wright - offsetrect.right()),
3401              dtop = abs(wtop - offsetrect.top()),
3402           dbottom = abs(wbottom - offsetrect.bottom());
3403
3404         // snap left?
3405         if (dleft < snap_distance && dleft <= dright)
3406           dx = offsetrect.left();
3407         // snap right?
3408         else if (dright < snap_distance)
3409           dx = offsetrect.right() - frame.rect.width() + 1;
3410
3411         // snap top?
3412         if (dtop < snap_distance && dtop <= dbottom)
3413           dy = offsetrect.top();
3414         // snap bottom?
3415         else if (dbottom < snap_distance)
3416           dy = offsetrect.bottom() - frame.rect.height() + 1;
3417       }
3418     }
3419   }
3420 }
3421
3422
3423 void BlackboxWindow::endMove(void) {
3424   assert(flags.moving);
3425   assert(blackbox->getChangingWindow() == this);
3426
3427   flags.moving = False;
3428   blackbox->setChangingWindow(0);
3429
3430   if (! screen->doOpaqueMove()) {
3431     /* when drawing the rubber band, we need to make sure we only draw inside
3432      * the frame... frame.changing_* contain the new coords for the window,
3433      * so we need to subtract 1 from changing_w/changing_h every where we
3434      * draw the rubber band (for both moving and resizing)
3435      */
3436     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3437                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3438                    frame.changing.width() - 1, frame.changing.height() - 1);
3439       XUngrabServer(blackbox->getXDisplay());
3440   
3441       configure(frame.changing.x(), frame.changing.y(),
3442                 frame.changing.width(), frame.changing.height());
3443   } else {
3444     configure(frame.rect.x(), frame.rect.y(),
3445               frame.rect.width(), frame.rect.height());
3446   }
3447   screen->hideGeometry();
3448
3449   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3450
3451   // if there are any left over motions from the move, drop them now
3452   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3453   XEvent e;
3454   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3455                                 MotionNotify, &e));
3456 }
3457
3458
3459 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3460   assert(! (flags.resizing || flags.moving));
3461
3462   /*
3463     Only one window can be moved/resized at a time. If another window is already
3464     being moved or resized, then stop it before whating to work with this one.
3465   */
3466   BlackboxWindow *changing = blackbox->getChangingWindow();
3467   if (changing && changing != this) {
3468     if (changing->flags.moving)
3469       changing->endMove();
3470     else // if (changing->flags.resizing)
3471       changing->endResize();
3472   }
3473
3474   resize_dir = dir;
3475
3476   Cursor cursor;
3477   Corner anchor;
3478   
3479   switch (resize_dir) {
3480   case BottomLeft:
3481     anchor = TopRight;
3482     cursor = blackbox->getLowerLeftAngleCursor();
3483     break;
3484
3485   case BottomRight:
3486     anchor = TopLeft;
3487     cursor = blackbox->getLowerRightAngleCursor();
3488     break;
3489
3490   case TopLeft:
3491     anchor = BottomRight;
3492     cursor = blackbox->getUpperLeftAngleCursor();
3493     break;
3494
3495   case TopRight:
3496     anchor = BottomLeft;
3497     cursor = blackbox->getUpperRightAngleCursor();
3498     break;
3499
3500   default:
3501     assert(false); // unhandled Corner
3502     return;        // unreachable, for the compiler
3503   }
3504   
3505   XGrabServer(blackbox->getXDisplay());
3506   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3507                PointerMotionMask | ButtonReleaseMask,
3508                GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3509
3510   flags.resizing = True;
3511   blackbox->setChangingWindow(this);
3512
3513   unsigned int gw, gh;
3514   frame.changing = frame.rect;
3515
3516   constrain(anchor,  &gw, &gh);
3517
3518   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3519                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3520                  frame.changing.width() - 1, frame.changing.height() - 1);
3521
3522   screen->showGeometry(gw, gh);
3523   
3524   frame.grab_x = x_root;
3525   frame.grab_y = y_root;
3526 }
3527
3528
3529 void BlackboxWindow::doResize(int x_root, int y_root) {
3530   assert(flags.resizing);
3531   assert(blackbox->getChangingWindow() == this);
3532
3533   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3534                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3535                  frame.changing.width() - 1, frame.changing.height() - 1);
3536
3537   unsigned int gw, gh;
3538   Corner anchor;
3539
3540   switch (resize_dir) {
3541   case BottomLeft:
3542     anchor = TopRight;
3543     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3544                            frame.rect.height() + (y_root - frame.grab_y));
3545     break;
3546   case BottomRight:
3547     anchor = TopLeft;
3548     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3549                            frame.rect.height() + (y_root - frame.grab_y));
3550     break;
3551   case TopLeft:
3552     anchor = BottomRight;
3553     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3554                            frame.rect.height() - (y_root - frame.grab_y));
3555     break;
3556   case TopRight:
3557     anchor = BottomLeft;
3558     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3559                            frame.rect.height() - (y_root - frame.grab_y));
3560     break;
3561
3562   default:
3563     assert(false); // unhandled Corner
3564     return;        // unreachable, for the compiler
3565   }
3566   
3567   constrain(anchor, &gw, &gh);
3568
3569   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3570                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3571                  frame.changing.width() - 1, frame.changing.height() - 1);
3572
3573   screen->showGeometry(gw, gh);
3574 }
3575
3576
3577 void BlackboxWindow::endResize(void) {
3578   assert(flags.resizing);
3579   assert(blackbox->getChangingWindow() == this);
3580
3581   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3582                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3583                  frame.changing.width() - 1, frame.changing.height() - 1);
3584   XUngrabServer(blackbox->getXDisplay());
3585
3586   // unset maximized state after resized when fully maximized
3587   if (flags.maximized == 1)
3588     maximize(0);
3589   
3590   flags.resizing = False;
3591   blackbox->setChangingWindow(0);
3592
3593   configure(frame.changing.x(), frame.changing.y(),
3594             frame.changing.width(), frame.changing.height());
3595   screen->hideGeometry();
3596
3597   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3598   
3599   // if there are any left over motions from the resize, drop them now
3600   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3601   XEvent e;
3602   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3603                                 MotionNotify, &e));
3604 }
3605
3606
3607 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3608 #ifdef DEBUG
3609   fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3610           client.window);
3611 #endif
3612
3613   if (flags.moving) {
3614     doMove(me->x_root, me->y_root);
3615   } else if (flags.resizing) {
3616     doResize(me->x_root, me->y_root);
3617   } else {
3618     if (!flags.resizing && me->state & Button1Mask && (functions & Func_Move) &&
3619         (frame.title == me->window || frame.label == me->window ||
3620          frame.handle == me->window || frame.window == me->window)) {
3621       beginMove(me->x_root, me->y_root);
3622     } else if ((functions & Func_Resize) &&
3623                (me->state & Button1Mask && (me->window == frame.right_grip ||
3624                                             me->window == frame.left_grip)) ||
3625                (me->state & Button3Mask && me->state & ModMask &&
3626                 me->window == frame.window)) {
3627       unsigned int zones = screen->getResizeZones();
3628       Corner corner;
3629       
3630       if (me->window == frame.left_grip) {
3631         corner = BottomLeft;
3632       } else if (me->window == frame.right_grip || zones == 1) {
3633         corner = BottomRight;
3634       } else {
3635         bool top;
3636         bool left = (me->x_root - frame.rect.x() <=
3637                      static_cast<signed>(frame.rect.width() / 2));
3638         if (zones == 2)
3639           top = False;
3640         else // (zones == 4)
3641           top = (me->y_root - frame.rect.y() <=
3642                  static_cast<signed>(frame.rect.height() / 2));
3643         corner = (top ? (left ? TopLeft : TopRight) :
3644                         (left ? BottomLeft : BottomRight));
3645       }
3646
3647       beginResize(me->x_root, me->y_root, corner);
3648     }
3649   }
3650 }
3651
3652
3653 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) {
3654   if (! (screen->isSloppyFocus() && isVisible() && isNormal()))
3655     return;
3656
3657   XEvent e;
3658   bool leave = False, inferior = False;
3659
3660   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), ce->window,
3661                                 LeaveNotify, &e)) {
3662     if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) {
3663       leave = True;
3664       inferior = (e.xcrossing.detail == NotifyInferior);
3665     }
3666   }
3667
3668   if ((! leave || inferior) && ! isFocused()) {
3669     bool success = setInputFocus();
3670     if (success)    // if focus succeeded install the colormap
3671       installColormap(True); // XXX: shouldnt we honour no install?
3672   }
3673
3674   if (screen->doAutoRaise())
3675     timer->start();
3676 }
3677
3678
3679 void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) {
3680   if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal()))
3681     return;
3682
3683   installColormap(False);
3684
3685   if (timer->isTiming())
3686     timer->stop();
3687 }
3688
3689
3690 #ifdef    SHAPE
3691 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3692   if (blackbox->hasShapeExtensions() && flags.shaped) {
3693     configureShape();
3694   }
3695 }
3696 #endif // SHAPE
3697
3698
3699 bool BlackboxWindow::validateClient(void) const {
3700   XSync(blackbox->getXDisplay(), False);
3701
3702   XEvent e;
3703   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3704                              DestroyNotify, &e) ||
3705       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3706                              UnmapNotify, &e)) {
3707     XPutBackEvent(blackbox->getXDisplay(), &e);
3708
3709     return False;
3710   }
3711
3712   return True;
3713 }
3714
3715
3716 void BlackboxWindow::restore(bool remap) {
3717   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3718   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3719   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3720
3721   // do not leave a shaded window as an icon unless it was an icon
3722   if (flags.shaded && ! flags.iconic)
3723     setState(NormalState);
3724
3725   restoreGravity(client.rect);
3726
3727   XUnmapWindow(blackbox->getXDisplay(), frame.window);
3728   XUnmapWindow(blackbox->getXDisplay(), client.window);
3729
3730   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3731
3732   XEvent ev;
3733   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3734                              ReparentNotify, &ev)) {
3735     remap = True;
3736   } else {
3737     // according to the ICCCM - if the client doesn't reparent to
3738     // root, then we have to do it for them
3739     XReparentWindow(blackbox->getXDisplay(), client.window,
3740                     screen->getRootWindow(),
3741                     client.rect.x(), client.rect.y());
3742   }
3743
3744   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3745 }
3746
3747
3748 // timer for autoraise
3749 void BlackboxWindow::timeout(void) {
3750   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3751 }
3752
3753
3754 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
3755   if ((net->flags & AttribShaded) &&
3756       ((blackbox_attrib.attrib & AttribShaded) !=
3757        (net->attrib & AttribShaded)))
3758     shade();
3759
3760   if (flags.visible && // watch out for requests when we can not be seen
3761       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3762       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3763        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3764     if (flags.maximized) {
3765       maximize(0);
3766     } else {
3767       int button = 0;
3768
3769       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3770         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
3771       else if (net->flags & AttribMaxVert)
3772         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3773       else if (net->flags & AttribMaxHoriz)
3774         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3775
3776       maximize(button);
3777     }
3778   }
3779
3780   if ((net->flags & AttribOmnipresent) &&
3781       ((blackbox_attrib.attrib & AttribOmnipresent) !=
3782        (net->attrib & AttribOmnipresent)))
3783     stick();
3784
3785   if ((net->flags & AttribWorkspace) &&
3786       (blackbox_attrib.workspace != net->workspace)) {
3787     screen->reassociateWindow(this, net->workspace, True);
3788
3789     if (screen->getCurrentWorkspaceID() != net->workspace) {
3790       withdraw();
3791     } else {
3792       show();
3793       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3794     }
3795   }
3796
3797   if (net->flags & AttribDecoration) {
3798     switch (net->decoration) {
3799     case DecorNone:
3800       enableDecor(False);
3801       break;
3802
3803     default:
3804     case DecorNormal:
3805     case DecorTiny:
3806     case DecorTool:
3807       enableDecor(True);
3808       break;
3809     }
3810   }
3811 }
3812
3813
3814 /*
3815  * Set the sizes of all components of the window frame
3816  * (the window decorations).
3817  * These values are based upon the current style settings and the client
3818  * window's dimensions.
3819  */
3820 void BlackboxWindow::upsize(void) {
3821   frame.bevel_w = screen->getBevelWidth();
3822
3823   if (decorations & Decor_Border) {
3824     frame.border_w = screen->getBorderWidth();
3825     if (! isTransient())
3826       frame.mwm_border_w = screen->getFrameWidth();
3827     else
3828       frame.mwm_border_w = 0;
3829   } else {
3830     frame.mwm_border_w = frame.border_w = 0;
3831   }
3832
3833   if (decorations & Decor_Titlebar) {
3834     // the height of the titlebar is based upon the height of the font being
3835     // used to display the window's title
3836     WindowStyle *style = screen->getWindowStyle();
3837     frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3838
3839     frame.label_h = frame.title_h - (frame.bevel_w * 2);
3840     frame.button_w = (frame.label_h - 2);
3841
3842     // set the top frame margin
3843     frame.margin.top = frame.border_w + frame.title_h +
3844                        frame.border_w + frame.mwm_border_w;
3845   } else {
3846     frame.title_h = 0;
3847     frame.label_h = 0;
3848     frame.button_w = 0;
3849
3850     // set the top frame margin
3851     frame.margin.top = frame.border_w + frame.mwm_border_w;
3852   }
3853
3854   // set the left/right frame margin
3855   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3856
3857   if (decorations & Decor_Handle) {
3858     frame.grip_w = frame.button_w * 2;
3859     frame.handle_h = screen->getHandleWidth();
3860
3861     // set the bottom frame margin
3862     frame.margin.bottom = frame.border_w + frame.handle_h +
3863                           frame.border_w + frame.mwm_border_w;
3864   } else {
3865     frame.handle_h = 0;
3866     frame.grip_w = 0;
3867
3868     // set the bottom frame margin
3869     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3870   }
3871
3872   /*
3873     We first get the normal dimensions and use this to define the inside_w/h
3874     then we modify the height if shading is in effect.
3875     If the shade state is not considered then frame.rect gets reset to the
3876     normal window size on a reconfigure() call resulting in improper
3877     dimensions appearing in move/resize and other events.
3878   */
3879   unsigned int
3880     height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3881     width = client.rect.width() + frame.margin.left + frame.margin.right;
3882
3883   frame.inside_w = width - (frame.border_w * 2);
3884   frame.inside_h = height - (frame.border_w * 2);
3885
3886   if (flags.shaded)
3887     height = frame.title_h + (frame.border_w * 2);
3888   frame.rect.setSize(width, height);
3889 }
3890
3891
3892 /*
3893  * Calculate the size of the client window and constrain it to the
3894  * size specified by the size hints of the client window.
3895  *
3896  * The logical width and height are placed into pw and ph, if they
3897  * are non-zero.  Logical size refers to the users perception of
3898  * the window size (for example an xterm resizes in cells, not in pixels).
3899  * pw and ph are then used to display the geometry during window moves, resize,
3900  * etc.
3901  *
3902  * The physical geometry is placed into frame.changing_{x,y,width,height}.
3903  * Physical geometry refers to the geometry of the window in pixels.
3904  */
3905 void BlackboxWindow::constrain(Corner anchor,
3906                                unsigned int *pw, unsigned int *ph) {
3907   // frame.changing represents the requested frame size, we need to
3908   // strip the frame margin off and constrain the client size
3909   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3910                            frame.changing.top() + frame.margin.top,
3911                            frame.changing.right() - frame.margin.right,
3912                            frame.changing.bottom() - frame.margin.bottom);
3913
3914   unsigned int dw = frame.changing.width(), dh = frame.changing.height(),
3915     base_width = (client.base_width) ? client.base_width : client.min_width,
3916     base_height = (client.base_height) ? client.base_height :
3917                                          client.min_height;
3918
3919   // constrain
3920   if (dw < client.min_width) dw = client.min_width;
3921   if (dh < client.min_height) dh = client.min_height;
3922   if (dw > client.max_width) dw = client.max_width;
3923   if (dh > client.max_height) dh = client.max_height;
3924
3925   assert(dw >= base_width && dh >= base_height);
3926
3927   if (client.width_inc > 1) {
3928     dw -= base_width;
3929     dw /= client.width_inc;
3930   }
3931   if (client.height_inc > 1) {
3932     dh -= base_height;
3933     dh /= client.height_inc;
3934   }
3935
3936   if (pw)
3937     *pw = dw;
3938
3939   if (ph)
3940     *ph = dh;
3941
3942   if (client.width_inc > 1) {
3943     dw *= client.width_inc;
3944     dw += base_width;
3945   }
3946   if (client.height_inc > 1) {
3947     dh *= client.height_inc;
3948     dh += base_height;
3949   }
3950
3951   frame.changing.setSize(dw, dh);
3952
3953   // add the frame margin back onto frame.changing
3954   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3955                            frame.changing.top() - frame.margin.top,
3956                            frame.changing.right() + frame.margin.right,
3957                            frame.changing.bottom() + frame.margin.bottom);
3958
3959   // move frame.changing to the specified anchor
3960   int dx = 0,
3961       dy = 0;
3962   switch (anchor) {
3963   case TopLeft:
3964     break;
3965
3966   case TopRight:
3967     dx = frame.rect.right() - frame.changing.right();
3968     break;
3969
3970   case BottomLeft:
3971     dy = frame.rect.bottom() - frame.changing.bottom();
3972     break;
3973
3974   case BottomRight:
3975     dx = frame.rect.right() - frame.changing.right();
3976     dy = frame.rect.bottom() - frame.changing.bottom();
3977     break;
3978
3979   default:
3980     assert(false);  // unhandled corner
3981   }
3982   frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
3983 }
3984
3985
3986 void WindowStyle::doJustify(const std::string &text, int &start_pos,
3987                             unsigned int max_length,
3988                             unsigned int modifier) const {
3989   size_t text_len = text.size();
3990   unsigned int length;
3991
3992   do {
3993     length = font->measureString(string(text, 0, text_len)) + modifier;
3994   } while (length > max_length && text_len-- > 0);
3995
3996   switch (justify) {
3997   case RightJustify:
3998     start_pos += max_length - length;
3999     break;
4000
4001   case CenterJustify:
4002     start_pos += (max_length - length) / 2;
4003     break;
4004
4005   case LeftJustify:
4006   default:
4007     break;
4008   }
4009 }
4010
4011
4012 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
4013   : blackbox(b), group(_group) {
4014   XWindowAttributes wattrib;
4015   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
4016     // group window doesn't seem to exist anymore
4017     delete this;
4018     return;
4019   }
4020
4021   XSelectInput(blackbox->getXDisplay(), group,
4022                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
4023
4024   blackbox->saveGroupSearch(group, this);
4025 }
4026
4027
4028 BWindowGroup::~BWindowGroup(void) {
4029   blackbox->removeGroupSearch(group);
4030 }
4031
4032
4033 BlackboxWindow *
4034 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
4035   BlackboxWindow *ret = blackbox->getFocusedWindow();
4036
4037   // does the focus window match (or any transient_fors)?
4038   for (; ret; ret = ret->getTransientFor()) {
4039     if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4040         (! ret->isTransient() || allow_transients))
4041       break;
4042   }
4043
4044   if (ret) return ret;
4045
4046   // the focus window didn't match, look in the group's window list
4047   BlackboxWindowList::const_iterator it, end = windowList.end();
4048   for (it = windowList.begin(); it != end; ++it) {
4049     ret = *it;
4050     if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4051         (! ret->isTransient() || allow_transients))
4052       break;
4053   }
4054
4055   return ret;
4056 }