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