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