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