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