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