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