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