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