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