]> icculus.org git repositories - dana/openbox.git/blob - src/Window.cc
don't actually focus windows when adding them to the current workspace. this is alrea...
[dana/openbox.git] / src / Window.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Window.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh at debian.org>
4 // Copyright (c) 1997 - 2000, 2002 Brad Hughes <bhughes at trolltech.com>
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <X11/Xatom.h>
30 #include <X11/keysym.h>
31
32 #ifdef HAVE_STRING_H
33 #  include <string.h>
34 #endif // HAVE_STRING_H
35
36 #ifdef    DEBUG
37 #  ifdef    HAVE_STDIO_H
38 #    include <stdio.h>
39 #  endif // HAVE_STDIO_H
40 #endif // DEBUG
41
42 #ifdef HAVE_STDLIB_H
43    #include <stdlib.h>
44 #endif // HAVE_STDLIB_H
45 }
46
47 #include "i18n.hh"
48 #include "blackbox.hh"
49 #include "Clientmenu.hh"
50 #include "Font.hh"
51 #include "GCCache.hh"
52 #include "Iconmenu.hh"
53 #include "Image.hh"
54 #include "Screen.hh"
55 #include "Toolbar.hh"
56 #include "Util.hh"
57 #include "Window.hh"
58 #include "Windowmenu.hh"
59 #include "Workspace.hh"
60 #include "Slit.hh"
61
62 using std::string;
63 using std::abs;
64
65 /*
66  * Initializes the class with default values/the window's set initial values.
67  */
68 BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
69   // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
70   // sizeof(BlackboxWindow));
71
72 #ifdef    DEBUG
73   fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
74 #endif // DEBUG
75
76   /*
77     set timer to zero... it is initialized properly later, so we check
78     if timer is zero in the destructor, and assume that the window is not
79     fully constructed if timer is zero...
80   */
81   timer = 0;
82   blackbox = b;
83   client.window = w;
84   screen = s;
85   xatom = blackbox->getXAtom();
86
87   if (! validateClient()) {
88     delete this;
89     return;
90   }
91
92   // fetch client size and placement
93   XWindowAttributes wattrib;
94   if (! XGetWindowAttributes(blackbox->getXDisplay(),
95                              client.window, &wattrib) ||
96       ! wattrib.screen || wattrib.override_redirect) {
97 #ifdef    DEBUG
98     fprintf(stderr,
99             "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
100 #endif // DEBUG
101
102     delete this;
103     return;
104   }
105
106   // set the eventmask early in the game so that we make sure we get
107   // all the events we are interested in
108   XSetWindowAttributes attrib_set;
109   attrib_set.event_mask = PropertyChangeMask | FocusChangeMask |
110                           StructureNotifyMask;
111   attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
112                                      ButtonMotionMask;
113   XChangeWindowAttributes(blackbox->getXDisplay(), client.window,
114                           CWEventMask|CWDontPropagate, &attrib_set);
115
116   flags.moving = flags.resizing = flags.shaded = flags.visible =
117     flags.iconic = flags.focused = flags.stuck = flags.modal =
118     flags.send_focus_message = flags.shaped = flags.skip_taskbar =
119     flags.skip_pager = flags.fullscreen = False;
120   flags.maximized = 0;
121
122   blackbox_attrib.workspace = window_number = BSENTINEL;
123
124   blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack = 0l;
125   blackbox_attrib.decoration = DecorNormal;
126   blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
127   blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
128
129   frame.border_w = 1;
130   frame.window = frame.plate = frame.title = frame.handle = None;
131   frame.close_button = frame.iconify_button = frame.maximize_button = None;
132   frame.right_grip = frame.left_grip = None;
133
134   frame.ulabel_pixel = frame.flabel_pixel = frame.utitle_pixel =
135   frame.ftitle_pixel = frame.uhandle_pixel = frame.fhandle_pixel =
136     frame.ubutton_pixel = frame.fbutton_pixel = frame.pbutton_pixel =
137     frame.uborder_pixel = frame.fborder_pixel = frame.ugrip_pixel =
138     frame.fgrip_pixel = 0;
139   frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
140   frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
141   frame.pbutton = frame.ugrip = frame.fgrip = None;
142
143   functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
144   mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
145                     Decor_Iconify | Decor_Maximize;
146
147   client.normal_hint_flags = 0;
148   client.window_group = None;
149   client.transient_for = 0;
150
151   current_state = NormalState;
152
153   windowmenu = 0;
154
155   /*
156     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   switch (current_state) {
2615   case IconicState:
2616     iconify();
2617     break;
2618
2619   case WithdrawnState:
2620     withdraw();
2621     break;
2622
2623   case NormalState:
2624   case InactiveState:
2625   case ZoomState:
2626   default:
2627     show();
2628     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2629     if (isNormal()) {
2630       if (! blackbox->isStartup()) {
2631         XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2632         if (screen->doFocusNew() || (isTransient() && getTransientFor() &&
2633                                      getTransientFor()->isFocused())) {
2634           setInputFocus();
2635         }
2636         if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2637           int x, y, rx, ry;
2638           Window c, r;
2639           unsigned int m;
2640           XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2641                         &r, &c, &rx, &ry, &x, &y, &m);
2642           beginMove(rx, ry);
2643         }
2644       }
2645     }
2646     break;
2647   }
2648 }
2649
2650
2651 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2652   if (ue->window != client.window)
2653     return;
2654
2655 #ifdef    DEBUG
2656   fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2657           client.window);
2658 #endif // DEBUG
2659
2660   screen->unmanageWindow(this, False);
2661 }
2662
2663
2664 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2665   if (de->window != client.window)
2666     return;
2667
2668 #ifdef    DEBUG
2669   fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2670           client.window);
2671 #endif // DEBUG
2672
2673   screen->unmanageWindow(this, False);
2674 }
2675
2676
2677 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2678   if (re->window != client.window || re->parent == frame.plate)
2679     return;
2680
2681 #ifdef    DEBUG
2682   fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2683           "0x%lx.\n", client.window, re->parent);
2684 #endif // DEBUG
2685
2686   XEvent ev;
2687   ev.xreparent = *re;
2688   XPutBackEvent(blackbox->getXDisplay(), &ev);
2689   screen->unmanageWindow(this, True);
2690 }
2691
2692
2693 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2694   if (pe->state == PropertyDelete)
2695     return;
2696
2697 #ifdef    DEBUG
2698   fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2699           client.window);
2700 #endif
2701
2702   switch(pe->atom) {
2703   case XA_WM_CLASS:
2704   case XA_WM_CLIENT_MACHINE:
2705   case XA_WM_COMMAND:
2706     break;
2707
2708   case XA_WM_TRANSIENT_FOR: {
2709     bool s = flags.stuck;
2710     
2711     // determine if this is a transient window
2712     getTransientInfo();
2713
2714     // adjust the window decorations based on transience
2715     if (isTransient()) {
2716       functions &= ~Func_Maximize;
2717       setAllowedActions();
2718       setupDecor();
2719       if (flags.stuck != s) stick();
2720     }
2721
2722     reconfigure();
2723   }
2724     break;
2725
2726   case XA_WM_HINTS:
2727     getWMHints();
2728     break;
2729
2730   case XA_WM_ICON_NAME:
2731     getWMIconName();
2732     if (flags.iconic) screen->propagateWindowName(this);
2733     break;
2734
2735   case XAtom::net_wm_name:
2736   case XA_WM_NAME:
2737     getWMName();
2738
2739     if (decorations & Decor_Titlebar)
2740       redrawLabel();
2741
2742     screen->propagateWindowName(this);
2743     break;
2744
2745   case XA_WM_NORMAL_HINTS: {
2746     getWMNormalHints();
2747
2748     if ((client.normal_hint_flags & PMinSize) &&
2749         (client.normal_hint_flags & PMaxSize)) {
2750       // the window now can/can't resize itself, so the buttons need to be
2751       // regrabbed.
2752       ungrabButtons();
2753       if (client.max_width <= client.min_width &&
2754           client.max_height <= client.min_height) {
2755         functions &= ~(Func_Resize | Func_Maximize);
2756       } else {
2757         if (! isTransient())
2758           functions |= Func_Maximize;
2759         functions |= Func_Resize;
2760       }
2761       grabButtons();
2762       setAllowedActions();
2763       setupDecor();
2764     }
2765
2766     Rect old_rect = frame.rect;
2767
2768     upsize();
2769
2770     if (old_rect != frame.rect)
2771       reconfigure();
2772
2773     break;
2774   }
2775
2776   default:
2777     if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2778       getWMProtocols();
2779
2780       if ((decorations & Decor_Close) && (! frame.close_button)) {
2781         createCloseButton();
2782         if (decorations & Decor_Titlebar) {
2783           positionButtons(True);
2784           XMapSubwindows(blackbox->getXDisplay(), frame.title);
2785         }
2786         if (windowmenu) windowmenu->reconfigure();
2787       }
2788     } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2789       updateStrut();
2790     }
2791
2792     break;
2793   }
2794 }
2795
2796
2797 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2798 #ifdef DEBUG
2799   fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2800 #endif
2801
2802   if (frame.label == ee->window && (decorations & Decor_Titlebar))
2803     redrawLabel();
2804   else if (frame.close_button == ee->window)
2805     redrawCloseButton(False);
2806   else if (frame.maximize_button == ee->window)
2807     redrawMaximizeButton(flags.maximized);
2808   else if (frame.iconify_button == ee->window)
2809     redrawIconifyButton(False);
2810 }
2811
2812
2813 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2814   if (cr->window != client.window || flags.iconic)
2815     return;
2816
2817   if (cr->value_mask & CWBorderWidth)
2818     client.old_bw = cr->border_width;
2819
2820   if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2821     Rect req = frame.rect;
2822
2823     if (cr->value_mask & (CWX | CWY)) {
2824       if (cr->value_mask & CWX)
2825         client.rect.setX(cr->x);
2826       if (cr->value_mask & CWY)
2827         client.rect.setY(cr->y);
2828
2829       applyGravity(req);
2830     }
2831
2832     if (cr->value_mask & CWWidth)
2833       req.setWidth(cr->width + frame.margin.left + frame.margin.right);
2834
2835     if (cr->value_mask & CWHeight)
2836       req.setHeight(cr->height + frame.margin.top + frame.margin.bottom);
2837
2838     configure(req.x(), req.y(), req.width(), req.height());
2839   }
2840
2841   if (cr->value_mask & CWStackMode && !isDesktop()) {
2842     switch (cr->detail) {
2843     case Below:
2844     case BottomIf:
2845       screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2846       break;
2847
2848     case Above:
2849     case TopIf:
2850     default:
2851       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2852       break;
2853     }
2854   }
2855 }
2856
2857
2858 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
2859 #ifdef DEBUG
2860   fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
2861           client.window);
2862 #endif
2863
2864   if (frame.maximize_button == be->window && be->button <= 3) {
2865     redrawMaximizeButton(True);
2866   } else if (be->button == 1 || (be->button == 3 && be->state == mod_mask)) {
2867     if (! flags.focused)
2868       setInputFocus();
2869
2870     if (frame.iconify_button == be->window) {
2871       redrawIconifyButton(True);
2872     } else if (frame.close_button == be->window) {
2873       redrawCloseButton(True);
2874     } else if (frame.plate == be->window) {
2875       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2876
2877       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2878
2879       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2880     } else {
2881       if (frame.title == be->window || frame.label == be->window) {
2882         if (((be->time - lastButtonPressTime) <=
2883              blackbox->getDoubleClickInterval()) ||
2884             (be->state == ControlMask)) {
2885           lastButtonPressTime = 0;
2886           shade();
2887         } else {
2888           lastButtonPressTime = be->time;
2889         }
2890       }
2891
2892       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2893
2894       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2895     }
2896   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2897              (be->window != frame.close_button)) {
2898     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2899   } else if (windowmenu && be->button == 3 &&
2900              (frame.title == be->window || frame.label == be->window ||
2901               frame.handle == be->window || frame.window == be->window)) {
2902     if (windowmenu->isVisible()) {
2903       windowmenu->hide();
2904     } else {
2905       int mx = be->x_root - windowmenu->getWidth() / 2,
2906           my = be->y_root - windowmenu->getHeight() / 2;
2907
2908       // snap the window menu into a corner/side if necessary
2909       int left_edge, right_edge, top_edge, bottom_edge;
2910
2911       /*
2912          the " + (frame.border_w * 2) - 1" bits are to get the proper width
2913          and height of the menu, as the sizes returned by it do not include
2914          the borders.
2915        */
2916       left_edge = frame.rect.x();
2917       right_edge = frame.rect.right() -
2918         (windowmenu->getWidth() + (frame.border_w * 2) - 1);
2919       top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
2920       bottom_edge = client.rect.bottom() -
2921         (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
2922         (frame.border_w + frame.mwm_border_w);
2923
2924       if (mx < left_edge)
2925         mx = left_edge;
2926       if (mx > right_edge)
2927         mx = right_edge;
2928       if (my < top_edge)
2929         my = top_edge;
2930       if (my > bottom_edge)
2931         my = bottom_edge;
2932
2933       windowmenu->move(mx, my);
2934       windowmenu->show();
2935       XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2936       XRaiseWindow(blackbox->getXDisplay(),
2937                    windowmenu->getSendToMenu()->getWindowID());
2938     }
2939   // mouse wheel up
2940   } else if (be->button == 4) {
2941     if ((be->window == frame.label ||
2942          be->window == frame.title ||
2943          be->window == frame.maximize_button ||
2944          be->window == frame.iconify_button ||
2945          be->window == frame.close_button) &&
2946         ! flags.shaded)
2947       shade();
2948   // mouse wheel down
2949   } else if (be->button == 5) {
2950     if ((be->window == frame.label ||
2951          be->window == frame.title ||
2952          be->window == frame.maximize_button ||
2953          be->window == frame.iconify_button ||
2954          be->window == frame.close_button) &&
2955         flags.shaded)
2956       shade();
2957   }
2958 }
2959
2960
2961 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
2962 #ifdef DEBUG
2963   fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
2964           client.window);
2965 #endif
2966
2967   if (re->window == frame.maximize_button &&
2968       re->button >= 1 && re->button <= 3) {
2969     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2970         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2971       maximize(re->button);
2972     } else {
2973       redrawMaximizeButton(flags.maximized);
2974     }
2975   } else if (re->window == frame.iconify_button && re->button == 1) {
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       iconify();
2979     } else {
2980       redrawIconifyButton(False);
2981     }
2982   } else if (re->window == frame.close_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       close();
2986     redrawCloseButton(False);
2987   } else if (flags.moving) {
2988     endMove();
2989   } else if (flags.resizing) {
2990     endResize();
2991   } else if (re->window == frame.window) {
2992     if (re->button == 2 && re->state == mod_mask)
2993       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2994   }
2995 }
2996
2997
2998
2999 void BlackboxWindow::beginMove(int x_root, int y_root) {
3000   assert(! (flags.resizing || flags.moving));
3001
3002   /*
3003     Only one window can be moved/resized at a time. If another window is already
3004     being moved or resized, then stop it before whating to work with this one.
3005   */
3006   BlackboxWindow *changing = blackbox->getChangingWindow();
3007   if (changing && changing != this) {
3008     if (changing->flags.moving)
3009       changing->endMove();
3010     else // if (changing->flags.resizing)
3011       changing->endResize();
3012   }
3013   
3014   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3015                PointerMotionMask | ButtonReleaseMask,
3016                GrabModeAsync, GrabModeAsync,
3017                None, blackbox->getMoveCursor(), CurrentTime);
3018
3019   if (windowmenu && windowmenu->isVisible())
3020     windowmenu->hide();
3021
3022   flags.moving = True;
3023   blackbox->setChangingWindow(this);
3024
3025   if (! screen->doOpaqueMove()) {
3026     XGrabServer(blackbox->getXDisplay());
3027
3028     frame.changing = frame.rect;
3029     screen->showPosition(frame.changing.x(), frame.changing.y());
3030
3031     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3032                    screen->getOpGC(),
3033                    frame.changing.x(),
3034                    frame.changing.y(),
3035                    frame.changing.width() - 1,
3036                    frame.changing.height() - 1);
3037   }
3038
3039   frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3040   frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3041 }
3042
3043
3044 void BlackboxWindow::doMove(int x_root, int y_root) {
3045   assert(flags.moving);
3046   assert(blackbox->getChangingWindow() == this);
3047
3048   int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3049   dx -= frame.border_w;
3050   dy -= frame.border_w;
3051
3052   if (screen->doWorkspaceWarping())
3053     if (doWorkspaceWarping(x_root, y_root, dx, dy))
3054       return;
3055
3056   doWindowSnapping(dx, dy);
3057
3058   if (screen->doOpaqueMove()) {
3059     configure(dx, dy, frame.rect.width(), frame.rect.height());
3060   } else {
3061     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3062                    screen->getOpGC(),
3063                    frame.changing.x(),
3064                    frame.changing.y(),
3065                    frame.changing.width() - 1,
3066                    frame.changing.height() - 1);
3067
3068     frame.changing.setPos(dx, dy);
3069
3070     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3071                    screen->getOpGC(),
3072                    frame.changing.x(),
3073                    frame.changing.y(),
3074                    frame.changing.width() - 1,
3075                    frame.changing.height() - 1);
3076   }
3077
3078   screen->showPosition(dx, dy);
3079 }
3080
3081
3082 bool BlackboxWindow::doWorkspaceWarping(int x_root, int y_root,
3083                                         int dx, int dy) {
3084   // workspace warping
3085   bool warp = False;
3086   unsigned int dest = screen->getCurrentWorkspaceID();
3087   if (x_root <= 0) {
3088     warp = True;
3089
3090     if (dest > 0) dest--;
3091     else dest = screen->getNumberOfWorkspaces() - 1;
3092
3093   } else if (x_root >= screen->getRect().right()) {
3094     warp = True;
3095
3096     if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3097     else dest = 0;
3098   }
3099   if (! warp)
3100     return false;
3101
3102   endMove();
3103   bool focus = flags.focused; // had focus while moving?
3104   if (! flags.stuck)
3105     screen->reassociateWindow(this, dest, False);
3106   screen->changeWorkspaceID(dest);
3107   if (focus)
3108     setInputFocus();
3109
3110   /*
3111      We grab the X server here because we are moving the window and then the
3112      mouse cursor. When one moves, it could end up putting the mouse cursor
3113      over another window for a moment. This can cause the warp to iniate a
3114      move on another window.
3115   */
3116   XGrabServer(blackbox->getXDisplay());
3117   int dest_x;
3118   if (x_root <= 0) {
3119     dest_x = screen->getRect().right() - 1;
3120     configure(dx + (screen->getRect().width() - 1), dy,
3121               frame.rect.width(), frame.rect.height());
3122   } else {
3123     dest_x = 0;
3124     configure(dx - (screen->getRect().width() - 1), dy,
3125               frame.rect.width(), frame.rect.height());
3126   }
3127   XWarpPointer(blackbox->getXDisplay(), None, 
3128                screen->getRootWindow(), 0, 0, 0, 0,
3129                dest_x, y_root);
3130   XUngrabServer(blackbox->getXDisplay());
3131
3132   beginMove(dest_x, y_root);
3133   return true;
3134 }
3135
3136
3137 void BlackboxWindow::doWindowSnapping(int &dx, int &dy) {
3138   // how much resistance to edges to provide
3139   const int resistance_size = screen->getResistanceSize();
3140
3141   // how far away to snap
3142   const int snap_distance = screen->getSnapThreshold();
3143
3144   // how to snap windows
3145   const int snap_to_windows = screen->getWindowToWindowSnap();
3146   const int snap_to_edges = screen->getWindowToEdgeSnap();
3147   // the amount of space away from the edge to provide resistance/snap
3148   const int snap_offset = screen->getSnapOffset();
3149
3150   // find the geomeetery where the moving window currently is
3151   const Rect &moving = screen->doOpaqueMove() ? frame.rect : frame.changing;
3152
3153   // window corners
3154   const int wleft = dx,
3155            wright = dx + frame.rect.width() - 1,
3156              wtop = dy,
3157           wbottom = dy + frame.rect.height() - 1;
3158
3159   if (snap_to_windows) {
3160     RectList rectlist;
3161
3162     Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3163     assert(w);
3164
3165     // add windows on the workspace to the rect list
3166     const BlackboxWindowList& stack_list = w->getStackingList();
3167     BlackboxWindowList::const_iterator st_it, st_end = stack_list.end();
3168     for (st_it = stack_list.begin(); st_it != st_end; ++st_it)
3169       if (*st_it != this) // don't snap to ourself
3170         rectlist.push_back( (*st_it)->frameRect() );
3171
3172     // add the toolbar and the slit to the rect list.
3173     // (only if they are not hidden)
3174     Toolbar *tbar = screen->getToolbar();
3175     Slit *slit = screen->getSlit();
3176     Rect tbar_rect, slit_rect;
3177     unsigned int bwidth = screen->getBorderWidth() * 2;
3178
3179     if (! (screen->doHideToolbar() || tbar->isHidden())) {
3180       tbar_rect.setRect(tbar->getX(), tbar->getY(), tbar->getWidth() + bwidth,
3181                         tbar->getHeight() + bwidth);
3182       rectlist.push_back(tbar_rect);
3183     }
3184
3185     if (! slit->isHidden()) {
3186       slit_rect.setRect(slit->getX(), slit->getY(), slit->getWidth() + bwidth,
3187                         slit->getHeight() + bwidth);
3188       rectlist.push_back(slit_rect);
3189     }
3190
3191     RectList::const_iterator it, end = rectlist.end();
3192     for (it = rectlist.begin(); it != end; ++it) {
3193       bool snapped = False;
3194       const Rect &winrect = *it;
3195       Rect offsetrect;
3196       offsetrect.setCoords(winrect.left() - snap_offset,
3197                            winrect.top() - snap_offset,
3198                            winrect.right() + snap_offset,
3199                            winrect.bottom() + snap_offset);
3200
3201       if (snap_to_windows == BScreen::WindowResistance)
3202         // if the window is already over top of this snap target, then
3203         // resistance is futile, so just ignore it
3204         if (winrect.intersects(moving))
3205           continue;
3206
3207       int dleft, dright, dtop, dbottom;
3208
3209       // if the windows are in the same plane vertically
3210       if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3211           wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3212
3213         if (snap_to_windows == BScreen::WindowResistance) {
3214           dleft = wright - offsetrect.left();
3215           dright = offsetrect.right() - wleft;
3216
3217           // snap left of other window?
3218           if (dleft >= 0 && dleft < resistance_size &&
3219               dleft < (wright - wleft)) {
3220             dx = offsetrect.left() - frame.rect.width();
3221             snapped = True;
3222           }
3223           // snap right of other window?
3224           else if (dright >= 0 && dright < resistance_size &&
3225                    dright < (wright - wleft)) {
3226             dx = offsetrect.right() + 1;
3227             snapped = True;
3228           }
3229         } else { // BScreen::WindowSnap
3230           dleft = abs(wright - offsetrect.left());
3231           dright = abs(wleft - offsetrect.right());
3232
3233           // snap left of other window?
3234           if (dleft < snap_distance && dleft <= dright) {
3235             dx = offsetrect.left() - frame.rect.width();
3236             snapped = True;
3237           }
3238           // snap right of other window?
3239           else if (dright < snap_distance) {
3240             dx = offsetrect.right() + 1;
3241             snapped = True;
3242           }            
3243         }
3244
3245         if (snapped) {
3246           if (screen->getWindowCornerSnap()) {
3247             // try corner-snap to its other sides
3248             if (snap_to_windows == BScreen::WindowResistance) {
3249               dtop = winrect.top() - wtop;
3250               dbottom = wbottom - winrect.bottom();
3251               if (dtop > 0 && dtop < resistance_size) {
3252                 // if we're already past the top edge, then don't provide
3253                 // resistance
3254                 if (moving.top() >= winrect.top())
3255                   dy = winrect.top();
3256               } else if (dbottom > 0 && dbottom < resistance_size) {
3257                 // if we're already past the bottom edge, then don't provide
3258                 // resistance
3259                 if (moving.bottom() <= winrect.bottom())
3260                   dy = winrect.bottom() - frame.rect.height() + 1;
3261               }
3262             } else { // BScreen::WindowSnap
3263               dtop = abs(wtop - winrect.top());
3264               dbottom = abs(wbottom - winrect.bottom());
3265               if (dtop < snap_distance && dtop <= dbottom)
3266                 dy = winrect.top();
3267               else if (dbottom < snap_distance)
3268                 dy = winrect.bottom() - frame.rect.height() + 1;
3269             }
3270           }
3271
3272           continue;
3273         }
3274       }
3275
3276       // if the windows are on the same plane horizontally
3277       if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3278           wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3279
3280         if (snap_to_windows == BScreen::WindowResistance) {
3281           dtop = wbottom - offsetrect.top();
3282           dbottom = offsetrect.bottom() - wtop;
3283
3284           // snap top of other window?
3285           if (dtop >= 0 && dtop < resistance_size && dtop < (wbottom - wtop)) {
3286             dy = offsetrect.top() - frame.rect.height();
3287             snapped = True;
3288           }
3289           // snap bottom of other window?
3290           else if (dbottom >= 0 && dbottom < resistance_size &&
3291                    dbottom < (wbottom - wtop)) {
3292             dy = offsetrect.bottom() + 1;
3293             snapped = True;
3294           }
3295         } else { // BScreen::WindowSnap
3296           dtop = abs(wbottom - offsetrect.top());
3297           dbottom = abs(wtop - offsetrect.bottom());
3298
3299           // snap top of other window?
3300           if (dtop < snap_distance && dtop <= dbottom) {
3301             dy = offsetrect.top() - frame.rect.height();
3302             snapped = True;
3303           }
3304           // snap bottom of other window?
3305           else if (dbottom < snap_distance) {
3306             dy = offsetrect.bottom() + 1;
3307             snapped = True;
3308           }
3309
3310         }
3311
3312         if (snapped) {
3313           if (screen->getWindowCornerSnap()) {
3314             // try corner-snap to its other sides
3315             if (snap_to_windows == BScreen::WindowResistance) {
3316               dleft = winrect.left() - wleft;
3317               dright = wright - winrect.right();
3318               if (dleft > 0 && dleft < resistance_size) {
3319                 // if we're already past the left edge, then don't provide
3320                 // resistance
3321                 if (moving.left() >= winrect.left())
3322                   dx = winrect.left();
3323               } else if (dright > 0 && dright < resistance_size) {
3324                 // if we're already past the right edge, then don't provide
3325                 // resistance
3326                 if (moving.right() <= winrect.right())
3327                   dx = winrect.right() - frame.rect.width() + 1;
3328               }
3329             } else { // BScreen::WindowSnap
3330               dleft = abs(wleft - winrect.left());
3331               dright = abs(wright - winrect.right());
3332               if (dleft < snap_distance && dleft <= dright)
3333                 dx = winrect.left();
3334               else if (dright < snap_distance)
3335                 dx = winrect.right() - frame.rect.width() + 1;
3336             }
3337           }
3338
3339           continue;
3340         }
3341       }
3342     }
3343   }
3344
3345   if (snap_to_edges) {
3346     RectList rectlist;
3347
3348     // snap to the screen edges (and screen boundaries for xinerama)
3349 #ifdef    XINERAMA
3350     if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3351       rectlist.insert(rectlist.begin(),
3352                       screen->getXineramaAreas().begin(),
3353                       screen->getXineramaAreas().end());
3354     } else
3355 #endif // XINERAMA
3356       rectlist.push_back(screen->getRect());
3357
3358     RectList::const_iterator it, end = rectlist.end();
3359     for (it = rectlist.begin(); it != end; ++it) {
3360       const Rect &srect = *it;
3361       Rect offsetrect;
3362       offsetrect.setCoords(srect.left() + snap_offset,
3363                            srect.top() + snap_offset,
3364                            srect.right() - snap_offset,
3365                            srect.bottom() - snap_offset);
3366
3367       if (snap_to_edges == BScreen::WindowResistance) {
3368         // if we're not in the rectangle then don't snap to it.
3369         if (! srect.contains(moving))
3370           continue;
3371       } else { // BScreen::WindowSnap
3372         // if we're not in the rectangle then don't snap to it.
3373         if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3374                                     frame.rect.height())))
3375           continue;
3376       }
3377
3378       if (snap_to_edges == BScreen::WindowResistance) {
3379       int dleft = offsetrect.left() - wleft,
3380          dright = wright - offsetrect.right(),
3381            dtop = offsetrect.top() - wtop,
3382         dbottom = wbottom - offsetrect.bottom();
3383
3384         // snap left?
3385         if (dleft > 0 && dleft < resistance_size)
3386           dx = offsetrect.left();
3387         // snap right?
3388         else if (dright > 0 && dright < resistance_size)
3389           dx = offsetrect.right() - frame.rect.width() + 1;
3390
3391         // snap top?
3392         if (dtop > 0 && dtop < resistance_size)
3393           dy = offsetrect.top();
3394         // snap bottom?
3395         else if (dbottom > 0 && dbottom < resistance_size)
3396           dy = offsetrect.bottom() - frame.rect.height() + 1;
3397       } else { // BScreen::WindowSnap
3398         int dleft = abs(wleft - offsetrect.left()),
3399            dright = abs(wright - offsetrect.right()),
3400              dtop = abs(wtop - offsetrect.top()),
3401           dbottom = abs(wbottom - offsetrect.bottom());
3402
3403         // snap left?
3404         if (dleft < snap_distance && dleft <= dright)
3405           dx = offsetrect.left();
3406         // snap right?
3407         else if (dright < snap_distance)
3408           dx = offsetrect.right() - frame.rect.width() + 1;
3409
3410         // snap top?
3411         if (dtop < snap_distance && dtop <= dbottom)
3412           dy = offsetrect.top();
3413         // snap bottom?
3414         else if (dbottom < snap_distance)
3415           dy = offsetrect.bottom() - frame.rect.height() + 1;
3416       }
3417     }
3418   }
3419 }
3420
3421
3422 void BlackboxWindow::endMove(void) {
3423   assert(flags.moving);
3424   assert(blackbox->getChangingWindow() == this);
3425
3426   flags.moving = False;
3427   blackbox->setChangingWindow(0);
3428
3429   if (! screen->doOpaqueMove()) {
3430     /* when drawing the rubber band, we need to make sure we only draw inside
3431      * the frame... frame.changing_* contain the new coords for the window,
3432      * so we need to subtract 1 from changing_w/changing_h every where we
3433      * draw the rubber band (for both moving and resizing)
3434      */
3435     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3436                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3437                    frame.changing.width() - 1, frame.changing.height() - 1);
3438       XUngrabServer(blackbox->getXDisplay());
3439   
3440       configure(frame.changing.x(), frame.changing.y(),
3441                 frame.changing.width(), frame.changing.height());
3442   } else {
3443     configure(frame.rect.x(), frame.rect.y(),
3444               frame.rect.width(), frame.rect.height());
3445   }
3446   screen->hideGeometry();
3447
3448   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3449
3450   // if there are any left over motions from the move, drop them now
3451   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3452   XEvent e;
3453   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3454                                 MotionNotify, &e));
3455 }
3456
3457
3458 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3459   assert(! (flags.resizing || flags.moving));
3460
3461   /*
3462     Only one window can be moved/resized at a time. If another window is already
3463     being moved or resized, then stop it before whating to work with this one.
3464   */
3465   BlackboxWindow *changing = blackbox->getChangingWindow();
3466   if (changing && changing != this) {
3467     if (changing->flags.moving)
3468       changing->endMove();
3469     else // if (changing->flags.resizing)
3470       changing->endResize();
3471   }
3472
3473   resize_dir = dir;
3474
3475   Cursor cursor;
3476   Corner anchor;
3477   
3478   switch (resize_dir) {
3479   case BottomLeft:
3480     anchor = TopRight;
3481     cursor = blackbox->getLowerLeftAngleCursor();
3482     break;
3483
3484   case BottomRight:
3485     anchor = TopLeft;
3486     cursor = blackbox->getLowerRightAngleCursor();
3487     break;
3488
3489   case TopLeft:
3490     anchor = BottomRight;
3491     cursor = blackbox->getUpperLeftAngleCursor();
3492     break;
3493
3494   case TopRight:
3495     anchor = BottomLeft;
3496     cursor = blackbox->getUpperRightAngleCursor();
3497     break;
3498
3499   default:
3500     assert(false); // unhandled Corner
3501     return;        // unreachable, for the compiler
3502   }
3503   
3504   XGrabServer(blackbox->getXDisplay());
3505   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3506                PointerMotionMask | ButtonReleaseMask,
3507                GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3508
3509   flags.resizing = True;
3510   blackbox->setChangingWindow(this);
3511
3512   unsigned int gw, gh;
3513   frame.changing = frame.rect;
3514
3515   constrain(anchor,  &gw, &gh);
3516
3517   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3518                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3519                  frame.changing.width() - 1, frame.changing.height() - 1);
3520
3521   screen->showGeometry(gw, gh);
3522   
3523   frame.grab_x = x_root;
3524   frame.grab_y = y_root;
3525 }
3526
3527
3528 void BlackboxWindow::doResize(int x_root, int y_root) {
3529   assert(flags.resizing);
3530   assert(blackbox->getChangingWindow() == this);
3531
3532   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3533                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3534                  frame.changing.width() - 1, frame.changing.height() - 1);
3535
3536   unsigned int gw, gh;
3537   Corner anchor;
3538
3539   switch (resize_dir) {
3540   case BottomLeft:
3541     anchor = TopRight;
3542     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3543                            frame.rect.height() + (y_root - frame.grab_y));
3544     break;
3545   case BottomRight:
3546     anchor = TopLeft;
3547     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3548                            frame.rect.height() + (y_root - frame.grab_y));
3549     break;
3550   case TopLeft:
3551     anchor = BottomRight;
3552     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3553                            frame.rect.height() - (y_root - frame.grab_y));
3554     break;
3555   case TopRight:
3556     anchor = BottomLeft;
3557     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3558                            frame.rect.height() - (y_root - frame.grab_y));
3559     break;
3560
3561   default:
3562     assert(false); // unhandled Corner
3563     return;        // unreachable, for the compiler
3564   }
3565   
3566   constrain(anchor, &gw, &gh);
3567
3568   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3569                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3570                  frame.changing.width() - 1, frame.changing.height() - 1);
3571
3572   screen->showGeometry(gw, gh);
3573 }
3574
3575
3576 void BlackboxWindow::endResize(void) {
3577   assert(flags.resizing);
3578   assert(blackbox->getChangingWindow() == this);
3579
3580   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3581                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3582                  frame.changing.width() - 1, frame.changing.height() - 1);
3583   XUngrabServer(blackbox->getXDisplay());
3584
3585   // unset maximized state after resized when fully maximized
3586   if (flags.maximized == 1)
3587     maximize(0);
3588   
3589   flags.resizing = False;
3590   blackbox->setChangingWindow(0);
3591
3592   configure(frame.changing.x(), frame.changing.y(),
3593             frame.changing.width(), frame.changing.height());
3594   screen->hideGeometry();
3595
3596   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3597   
3598   // if there are any left over motions from the resize, drop them now
3599   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3600   XEvent e;
3601   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3602                                 MotionNotify, &e));
3603 }
3604
3605
3606 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3607 #ifdef DEBUG
3608   fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3609           client.window);
3610 #endif
3611
3612   if (flags.moving) {
3613     doMove(me->x_root, me->y_root);
3614   } else if (flags.resizing) {
3615     doResize(me->x_root, me->y_root);
3616   } else {
3617     if ((functions & Func_Move) &&
3618        (me->state & Button1Mask) &&
3619         (frame.title == me->window || frame.label == me->window ||
3620          frame.handle == me->window || frame.window == me->window)) {
3621       beginMove(me->x_root, me->y_root);
3622     } else if ((functions & Func_Resize) &&
3623                ((me->state & Button1Mask) && (me->window == frame.right_grip ||
3624                                               me->window == frame.left_grip)) ||
3625                ((me->state & Button3Mask) && (me->state & mod_mask) &&
3626                 (frame.title == me->window || frame.label == me->window ||
3627                  frame.handle == me->window || frame.window == me->window))) {
3628       unsigned int zones = screen->getResizeZones();
3629       Corner corner;
3630       
3631       if (me->window == frame.left_grip) {
3632         corner = BottomLeft;
3633       } else if (me->window == frame.right_grip || zones == 1) {
3634         corner = BottomRight;
3635       } else {
3636         bool top;
3637         bool left = (me->x_root - frame.rect.x() <=
3638                      static_cast<signed>(frame.rect.width() / 2));
3639         if (zones == 2)
3640           top = False;
3641         else // (zones == 4)
3642           top = (me->y_root - frame.rect.y() <=
3643                  static_cast<signed>(frame.rect.height() / 2));
3644         corner = (top ? (left ? TopLeft : TopRight) :
3645                         (left ? BottomLeft : BottomRight));
3646       }
3647
3648       beginResize(me->x_root, me->y_root, corner);
3649     }
3650   }
3651 }
3652
3653
3654 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) {
3655   if (! (screen->isSloppyFocus() && isVisible() && isNormal()))
3656     return;
3657
3658   XEvent e;
3659   bool leave = False, inferior = False;
3660
3661   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), ce->window,
3662                                 LeaveNotify, &e)) {
3663     if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) {
3664       leave = True;
3665       inferior = (e.xcrossing.detail == NotifyInferior);
3666     }
3667   }
3668
3669   if ((! leave || inferior) && ! isFocused()) {
3670     bool success = setInputFocus();
3671     if (success)    // if focus succeeded install the colormap
3672       installColormap(True); // XXX: shouldnt we honour no install?
3673   }
3674
3675   if (screen->doAutoRaise())
3676     timer->start();
3677 }
3678
3679
3680 void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) {
3681   if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal()))
3682     return;
3683
3684   installColormap(False);
3685
3686   if (timer->isTiming())
3687     timer->stop();
3688 }
3689
3690
3691 #ifdef    SHAPE
3692 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3693   if (blackbox->hasShapeExtensions() && flags.shaped) {
3694     configureShape();
3695   }
3696 }
3697 #endif // SHAPE
3698
3699
3700 bool BlackboxWindow::validateClient(void) const {
3701   XSync(blackbox->getXDisplay(), False);
3702
3703   XEvent e;
3704   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3705                              DestroyNotify, &e) ||
3706       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3707                              UnmapNotify, &e)) {
3708     XPutBackEvent(blackbox->getXDisplay(), &e);
3709
3710     return False;
3711   }
3712
3713   return True;
3714 }
3715
3716
3717 void BlackboxWindow::restore(bool remap) {
3718   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3719   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3720   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3721
3722   // do not leave a shaded window as an icon unless it was an icon
3723   if (flags.shaded && ! flags.iconic)
3724     setState(NormalState);
3725
3726   restoreGravity(client.rect);
3727
3728   XUnmapWindow(blackbox->getXDisplay(), frame.window);
3729   XUnmapWindow(blackbox->getXDisplay(), client.window);
3730
3731   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3732
3733   XEvent ev;
3734   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3735                              ReparentNotify, &ev)) {
3736     remap = True;
3737   } else {
3738     // according to the ICCCM - if the client doesn't reparent to
3739     // root, then we have to do it for them
3740     XReparentWindow(blackbox->getXDisplay(), client.window,
3741                     screen->getRootWindow(),
3742                     client.rect.x(), client.rect.y());
3743   }
3744
3745   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3746 }
3747
3748
3749 // timer for autoraise
3750 void BlackboxWindow::timeout(void) {
3751   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3752 }
3753
3754
3755 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
3756   if ((net->flags & AttribShaded) &&
3757       ((blackbox_attrib.attrib & AttribShaded) !=
3758        (net->attrib & AttribShaded)))
3759     shade();
3760
3761   if (flags.visible && // watch out for requests when we can not be seen
3762       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3763       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3764        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3765     if (flags.maximized) {
3766       maximize(0);
3767     } else {
3768       int button = 0;
3769
3770       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3771         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
3772       else if (net->flags & AttribMaxVert)
3773         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3774       else if (net->flags & AttribMaxHoriz)
3775         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3776
3777       maximize(button);
3778     }
3779   }
3780
3781   if ((net->flags & AttribOmnipresent) &&
3782       ((blackbox_attrib.attrib & AttribOmnipresent) !=
3783        (net->attrib & AttribOmnipresent)))
3784     stick();
3785
3786   if ((net->flags & AttribWorkspace) &&
3787       (blackbox_attrib.workspace != net->workspace)) {
3788     screen->reassociateWindow(this, net->workspace, True);
3789
3790     if (screen->getCurrentWorkspaceID() != net->workspace) {
3791       withdraw();
3792     } else {
3793       show();
3794       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3795     }
3796   }
3797
3798   if (net->flags & AttribDecoration) {
3799     switch (net->decoration) {
3800     case DecorNone:
3801       enableDecor(False);
3802       break;
3803
3804     default:
3805     case DecorNormal:
3806     case DecorTiny:
3807     case DecorTool:
3808       enableDecor(True);
3809       break;
3810     }
3811   }
3812 }
3813
3814
3815 /*
3816  * Set the sizes of all components of the window frame
3817  * (the window decorations).
3818  * These values are based upon the current style settings and the client
3819  * window's dimensions.
3820  */
3821 void BlackboxWindow::upsize(void) {
3822   frame.bevel_w = screen->getBevelWidth();
3823
3824   if (decorations & Decor_Border) {
3825     frame.border_w = screen->getBorderWidth();
3826     if (! isTransient())
3827       frame.mwm_border_w = screen->getFrameWidth();
3828     else
3829       frame.mwm_border_w = 0;
3830   } else {
3831     frame.mwm_border_w = frame.border_w = 0;
3832   }
3833
3834   if (decorations & Decor_Titlebar) {
3835     // the height of the titlebar is based upon the height of the font being
3836     // used to display the window's title
3837     WindowStyle *style = screen->getWindowStyle();
3838     frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3839
3840     frame.label_h = frame.title_h - (frame.bevel_w * 2);
3841     frame.button_w = (frame.label_h - 2);
3842
3843     // set the top frame margin
3844     frame.margin.top = frame.border_w + frame.title_h +
3845                        frame.border_w + frame.mwm_border_w;
3846   } else {
3847     frame.title_h = 0;
3848     frame.label_h = 0;
3849     frame.button_w = 0;
3850
3851     // set the top frame margin
3852     frame.margin.top = frame.border_w + frame.mwm_border_w;
3853   }
3854
3855   // set the left/right frame margin
3856   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3857
3858   if (decorations & Decor_Handle) {
3859     frame.grip_w = frame.button_w * 2;
3860     frame.handle_h = screen->getHandleWidth();
3861
3862     // set the bottom frame margin
3863     frame.margin.bottom = frame.border_w + frame.handle_h +
3864                           frame.border_w + frame.mwm_border_w;
3865   } else {
3866     frame.handle_h = 0;
3867     frame.grip_w = 0;
3868
3869     // set the bottom frame margin
3870     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3871   }
3872
3873   /*
3874     We first get the normal dimensions and use this to define the inside_w/h
3875     then we modify the height if shading is in effect.
3876     If the shade state is not considered then frame.rect gets reset to the
3877     normal window size on a reconfigure() call resulting in improper
3878     dimensions appearing in move/resize and other events.
3879   */
3880   unsigned int
3881     height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3882     width = client.rect.width() + frame.margin.left + frame.margin.right;
3883
3884   frame.inside_w = width - (frame.border_w * 2);
3885   frame.inside_h = height - (frame.border_w * 2);
3886
3887   if (flags.shaded)
3888     height = frame.title_h + (frame.border_w * 2);
3889   frame.rect.setSize(width, height);
3890 }
3891
3892
3893 /*
3894  * Calculate the size of the client window and constrain it to the
3895  * size specified by the size hints of the client window.
3896  *
3897  * The logical width and height are placed into pw and ph, if they
3898  * are non-zero.  Logical size refers to the users perception of
3899  * the window size (for example an xterm resizes in cells, not in pixels).
3900  * pw and ph are then used to display the geometry during window moves, resize,
3901  * etc.
3902  *
3903  * The physical geometry is placed into frame.changing_{x,y,width,height}.
3904  * Physical geometry refers to the geometry of the window in pixels.
3905  */
3906 void BlackboxWindow::constrain(Corner anchor,
3907                                unsigned int *pw, unsigned int *ph) {
3908   // frame.changing represents the requested frame size, we need to
3909   // strip the frame margin off and constrain the client size
3910   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3911                            frame.changing.top() + frame.margin.top,
3912                            frame.changing.right() - frame.margin.right,
3913                            frame.changing.bottom() - frame.margin.bottom);
3914
3915   unsigned int dw = frame.changing.width(), dh = frame.changing.height(),
3916     base_width = (client.base_width) ? client.base_width : client.min_width,
3917     base_height = (client.base_height) ? client.base_height :
3918                                          client.min_height;
3919
3920   // constrain
3921   if (dw < client.min_width) dw = client.min_width;
3922   if (dh < client.min_height) dh = client.min_height;
3923   if (dw > client.max_width) dw = client.max_width;
3924   if (dh > client.max_height) dh = client.max_height;
3925
3926   assert(dw >= base_width && dh >= base_height);
3927
3928   if (client.width_inc > 1) {
3929     dw -= base_width;
3930     dw /= client.width_inc;
3931   }
3932   if (client.height_inc > 1) {
3933     dh -= base_height;
3934     dh /= client.height_inc;
3935   }
3936
3937   if (pw)
3938     *pw = dw;
3939
3940   if (ph)
3941     *ph = dh;
3942
3943   if (client.width_inc > 1) {
3944     dw *= client.width_inc;
3945     dw += base_width;
3946   }
3947   if (client.height_inc > 1) {
3948     dh *= client.height_inc;
3949     dh += base_height;
3950   }
3951
3952   frame.changing.setSize(dw, dh);
3953
3954   // add the frame margin back onto frame.changing
3955   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3956                            frame.changing.top() - frame.margin.top,
3957                            frame.changing.right() + frame.margin.right,
3958                            frame.changing.bottom() + frame.margin.bottom);
3959
3960   // move frame.changing to the specified anchor
3961   int dx = 0,
3962       dy = 0;
3963   switch (anchor) {
3964   case TopLeft:
3965     break;
3966
3967   case TopRight:
3968     dx = frame.rect.right() - frame.changing.right();
3969     break;
3970
3971   case BottomLeft:
3972     dy = frame.rect.bottom() - frame.changing.bottom();
3973     break;
3974
3975   case BottomRight:
3976     dx = frame.rect.right() - frame.changing.right();
3977     dy = frame.rect.bottom() - frame.changing.bottom();
3978     break;
3979
3980   default:
3981     assert(false);  // unhandled corner
3982   }
3983   frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
3984 }
3985
3986
3987 void WindowStyle::doJustify(const std::string &text, int &start_pos,
3988                             unsigned int max_length,
3989                             unsigned int modifier) const {
3990   size_t text_len = text.size();
3991   unsigned int length;
3992
3993   do {
3994     length = font->measureString(string(text, 0, text_len)) + modifier;
3995   } while (length > max_length && text_len-- > 0);
3996
3997   switch (justify) {
3998   case RightJustify:
3999     start_pos += max_length - length;
4000     break;
4001
4002   case CenterJustify:
4003     start_pos += (max_length - length) / 2;
4004     break;
4005
4006   case LeftJustify:
4007   default:
4008     break;
4009   }
4010 }
4011
4012
4013 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
4014   : blackbox(b), group(_group) {
4015   XWindowAttributes wattrib;
4016   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
4017     // group window doesn't seem to exist anymore
4018     delete this;
4019     return;
4020   }
4021
4022   XSelectInput(blackbox->getXDisplay(), group,
4023                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
4024
4025   blackbox->saveGroupSearch(group, this);
4026 }
4027
4028
4029 BWindowGroup::~BWindowGroup(void) {
4030   blackbox->removeGroupSearch(group);
4031 }
4032
4033
4034 BlackboxWindow *
4035 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
4036   BlackboxWindow *ret = blackbox->getFocusedWindow();
4037
4038   // does the focus window match (or any transient_fors)?
4039   for (; ret; ret = ret->getTransientFor()) {
4040     if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4041         (! ret->isTransient() || allow_transients))
4042       break;
4043   }
4044
4045   if (ret) return ret;
4046
4047   // the focus window didn't match, look in the group's window list
4048   BlackboxWindowList::const_iterator it, end = windowList.end();
4049   for (it = windowList.begin(); it != end; ++it) {
4050     ret = *it;
4051     if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4052         (! ret->isTransient() || allow_transients))
4053       break;
4054   }
4055
4056   return ret;
4057 }