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