]> icculus.org git repositories - mikachu/openbox.git/blob - src/Window.cc
don't let windows snap to themself
[mikachu/openbox.git] / src / Window.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Window.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh at debian.org>
4 // Copyright (c) 1997 - 2000, 2002 Brad Hughes <bhughes at trolltech.com>
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <X11/Xatom.h>
30 #include <X11/keysym.h>
31
32 #ifdef HAVE_STRING_H
33 #  include <string.h>
34 #endif // HAVE_STRING_H
35
36 #ifdef    DEBUG
37 #  ifdef    HAVE_STDIO_H
38 #    include <stdio.h>
39 #  endif // HAVE_STDIO_H
40 #endif // DEBUG
41
42 #ifdef HAVE_STDLIB_H
43    #include <stdlib.h>
44 #endif // HAVE_STDLIB_H
45 }
46
47 #include "i18n.hh"
48 #include "blackbox.hh"
49 #include "Clientmenu.hh"
50 #include "Font.hh"
51 #include "GCCache.hh"
52 #include "Iconmenu.hh"
53 #include "Image.hh"
54 #include "Screen.hh"
55 #include "Toolbar.hh"
56 #include "Util.hh"
57 #include "Window.hh"
58 #include "Windowmenu.hh"
59 #include "Workspace.hh"
60 #include "Slit.hh"
61
62 using std::string;
63 using std::abs;
64
65 // 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   windowmenu = 0;
159
160   /*
161     get the initial size and location of client window (relative to the
162     _root window_). This position is the reference point used with the
163     window's gravity to find the window's initial position.
164   */
165   client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
166   client.old_bw = wattrib.border_width;
167
168   lastButtonPressTime = 0;
169
170   timer = new BTimer(blackbox, this);
171   timer->setTimeout(blackbox->getAutoRaiseDelay());
172
173   // get size, aspect, minimum/maximum size and other hints set by the
174   // client
175
176   if (! getBlackboxHints()) {
177     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   /*
261     the server needs to be grabbed here to prevent client's from sending
262     events while we are in the process of configuring their window.
263     We hold the grab until after we are done moving the window around.
264   */
265
266   XGrabServer(blackbox->getXDisplay());
267
268   associateClientWindow();
269
270   blackbox->saveWindowSearch(client.window, this);
271
272   if (blackbox_attrib.workspace >= screen->getWorkspaceCount())
273     screen->getCurrentWorkspace()->addWindow(this, place_window);
274   else
275     screen->getWorkspace(blackbox_attrib.workspace)->
276       addWindow(this, place_window);
277
278   if (! place_window) {
279     // don't need to call configure if we are letting the workspace
280     // place the window
281     configure(frame.rect.x(), frame.rect.y(),
282               frame.rect.width(), frame.rect.height());
283
284   }
285
286   positionWindows();
287
288   XUngrabServer(blackbox->getXDisplay());
289
290 #ifdef    SHAPE
291   if (blackbox->hasShapeExtensions() && flags.shaped)
292     configureShape();
293 #endif // SHAPE
294
295   // now that we know where to put the window and what it should look like
296   // we apply the decorations
297   decorate();
298
299   grabButtons();
300
301   XMapSubwindows(blackbox->getXDisplay(), frame.window);
302
303   // this ensures the title, buttons, and other decor are properly displayed
304   redrawWindowFrame();
305
306   // preserve the window's initial state on first map, and its current state
307   // across a restart
308   unsigned long initial_state = current_state;
309   if (! getState())
310     current_state = initial_state;
311
312   // get sticky state from our parent window if we've got one
313   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
314       client.transient_for->isStuck() != flags.stuck)
315     flags.stuck = True;
316
317   if (flags.shaded) {
318     flags.shaded = False;
319     initial_state = current_state;
320     shade();
321
322     /*
323       At this point in the life of a window, current_state should only be set
324       to IconicState if the window was an *icon*, not if it was shaded.
325     */
326     if (initial_state != IconicState)
327       current_state = NormalState;
328   }
329
330   if (flags.stuck) {
331     flags.stuck = False;
332     stick();
333   }
334
335   if (flags.maximized && (functions & Func_Maximize))
336     remaximize();
337
338   // create this last so it only needs to be configured once
339   windowmenu = new Windowmenu(this);
340 }
341
342
343 BlackboxWindow::~BlackboxWindow(void) {
344 #ifdef    DEBUG
345   fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
346           client.window);
347 #endif // DEBUG
348
349   if (! timer) // window not managed...
350     return;
351
352   screen->removeStrut(&client.strut);
353   screen->updateAvailableArea();
354
355   // We don't need to worry about resizing because resizing always grabs the X
356   // server. This should only ever happen if using opaque moving.
357   if (flags.moving)
358     endMove();
359
360   delete timer;
361
362   delete windowmenu;
363
364   if (client.window_group) {
365     BWindowGroup *group = blackbox->searchGroup(client.window_group);
366     if (group) group->removeWindow(this);
367   }
368
369   // remove ourselves from our transient_for
370   if (isTransient()) {
371     if (client.transient_for != (BlackboxWindow *) ~0ul)
372       client.transient_for->client.transientList.remove(this);
373     client.transient_for = (BlackboxWindow*) 0;
374   }
375
376   if (client.transientList.size() > 0) {
377     // reset transient_for for all transients
378     BlackboxWindowList::iterator it, end = client.transientList.end();
379     for (it = client.transientList.begin(); it != end; ++it)
380       (*it)->client.transient_for = (BlackboxWindow*) 0;
381   }
382
383   if (frame.title)
384     destroyTitlebar();
385
386   if (frame.handle)
387     destroyHandle();
388
389   if (frame.plate) {
390     blackbox->removeWindowSearch(frame.plate);
391     XDestroyWindow(blackbox->getXDisplay(), frame.plate);
392   }
393
394   if (frame.window) {
395     blackbox->removeWindowSearch(frame.window);
396     XDestroyWindow(blackbox->getXDisplay(), frame.window);
397   }
398
399   blackbox->removeWindowSearch(client.window);
400 }
401
402
403 /*
404  * Creates a new top level window, with a given location, size, and border
405  * width.
406  * Returns: the newly created window
407  */
408 Window BlackboxWindow::createToplevelWindow(void) {
409   XSetWindowAttributes attrib_create;
410   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
411                               CWOverrideRedirect | CWEventMask;
412
413   attrib_create.background_pixmap = None;
414   attrib_create.colormap = screen->getColormap();
415   attrib_create.override_redirect = True;
416   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
417                              ButtonMotionMask |
418                              EnterWindowMask | LeaveWindowMask;
419
420   return XCreateWindow(blackbox->getXDisplay(), screen->getRootWindow(),
421                        0, 0, 1, 1, frame.border_w, screen->getDepth(),
422                        InputOutput, screen->getVisual(), create_mask,
423                        &attrib_create);
424 }
425
426
427 /*
428  * Creates a child window, and optionally associates a given cursor with
429  * the new window.
430  */
431 Window BlackboxWindow::createChildWindow(Window parent, Cursor cursor) {
432   XSetWindowAttributes attrib_create;
433   unsigned long create_mask = CWBackPixmap | CWBorderPixel |
434                               CWEventMask;
435
436   attrib_create.background_pixmap = None;
437   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
438                              ButtonMotionMask | ExposureMask;
439
440   if (cursor) {
441     create_mask |= CWCursor;
442     attrib_create.cursor = cursor;
443   }
444
445   return XCreateWindow(blackbox->getXDisplay(), parent, 0, 0, 1, 1, 0,
446                        screen->getDepth(), InputOutput, screen->getVisual(),
447                        create_mask, &attrib_create);
448 }
449
450
451 void BlackboxWindow::associateClientWindow(void) {
452   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, 0);
453   getWMName();
454   getWMIconName();
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   // Check for a circular transient state: this can lock up Blackbox
1421   // when it tries to find the non-transient window for a transient.
1422   BlackboxWindow *w = this;
1423   while(w->client.transient_for &&
1424         w->client.transient_for != (BlackboxWindow *) ~0ul) {
1425     if(w->client.transient_for == this) {
1426       client.transient_for = (BlackboxWindow*) 0;
1427       break;
1428     }
1429     w = w->client.transient_for;
1430   }
1431
1432   if (client.transient_for &&
1433       client.transient_for != (BlackboxWindow *) ~0ul) {
1434     // register ourselves with our new transient_for
1435     client.transient_for->client.transientList.push_back(this);
1436     flags.stuck = client.transient_for->flags.stuck;
1437   }
1438 }
1439
1440
1441 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1442   if (client.transient_for &&
1443       client.transient_for != (BlackboxWindow*) ~0ul)
1444     return client.transient_for;
1445   return 0;
1446 }
1447
1448
1449 /*
1450  * This function is responsible for updating both the client and the frame
1451  * rectangles.
1452  * According to the ICCCM a client message is not sent for a resize, only a
1453  * move.
1454  */
1455 void BlackboxWindow::configure(int dx, int dy,
1456                                unsigned int dw, unsigned int dh) {
1457   bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1458                      ! flags.moving);
1459
1460   if (dw != frame.rect.width() || dh != frame.rect.height()) {
1461     frame.rect.setRect(dx, dy, dw, dh);
1462     frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1463     frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1464
1465     if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1466       frame.rect.setPos(0, 0);
1467
1468     client.rect.setCoords(frame.rect.left() + frame.margin.left,
1469                           frame.rect.top() + frame.margin.top,
1470                           frame.rect.right() - frame.margin.right,
1471                           frame.rect.bottom() - frame.margin.bottom);
1472
1473 #ifdef    SHAPE
1474     if (blackbox->hasShapeExtensions() && flags.shaped) {
1475       configureShape();
1476     }
1477 #endif // SHAPE
1478
1479     positionWindows();
1480     decorate();
1481     redrawWindowFrame();
1482   } else {
1483     frame.rect.setPos(dx, dy);
1484
1485     XMoveWindow(blackbox->getXDisplay(), frame.window,
1486                 frame.rect.x(), frame.rect.y());
1487     /*
1488       we may have been called just after an opaque window move, so even though
1489       the old coords match the new ones no ConfigureNotify has been sent yet.
1490       There are likely other times when this will be relevant as well.
1491     */
1492     if (! flags.moving) send_event = True;
1493   }
1494
1495   if (send_event) {
1496     // if moving, the update and event will occur when the move finishes
1497     client.rect.setPos(frame.rect.left() + frame.margin.left,
1498                        frame.rect.top() + frame.margin.top);
1499
1500     XEvent event;
1501     event.type = ConfigureNotify;
1502
1503     event.xconfigure.display = blackbox->getXDisplay();
1504     event.xconfigure.event = client.window;
1505     event.xconfigure.window = client.window;
1506     event.xconfigure.x = client.rect.x();
1507     event.xconfigure.y = client.rect.y();
1508     event.xconfigure.width = client.rect.width();
1509     event.xconfigure.height = client.rect.height();
1510     event.xconfigure.border_width = client.old_bw;
1511     event.xconfigure.above = frame.window;
1512     event.xconfigure.override_redirect = False;
1513
1514     XSendEvent(blackbox->getXDisplay(), client.window, False,
1515                StructureNotifyMask, &event);
1516     screen->updateNetizenConfigNotify(&event);
1517     XFlush(blackbox->getXDisplay());
1518   }
1519 }
1520
1521
1522 #ifdef SHAPE
1523 void BlackboxWindow::configureShape(void) {
1524   XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1525                      frame.margin.left - frame.border_w,
1526                      frame.margin.top - frame.border_w,
1527                      client.window, ShapeBounding, ShapeSet);
1528
1529   int num = 0;
1530   XRectangle xrect[2];
1531
1532   if (decorations & Decor_Titlebar) {
1533     xrect[0].x = xrect[0].y = -frame.border_w;
1534     xrect[0].width = frame.rect.width();
1535     xrect[0].height = frame.title_h + (frame.border_w * 2);
1536     ++num;
1537   }
1538
1539   if (decorations & Decor_Handle) {
1540     xrect[1].x = -frame.border_w;
1541     xrect[1].y = frame.rect.height() - frame.margin.bottom +
1542                  frame.mwm_border_w - frame.border_w;
1543     xrect[1].width = frame.rect.width();
1544     xrect[1].height = frame.handle_h + (frame.border_w * 2);
1545     ++num;
1546   }
1547
1548   XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1549                           ShapeBounding, 0, 0, xrect, num,
1550                           ShapeUnion, Unsorted);
1551 }
1552 #endif // SHAPE
1553
1554
1555 bool BlackboxWindow::setInputFocus(void) {
1556   if (flags.focused) return True;
1557
1558   assert(flags.stuck ||  // window must be on the current workspace or sticky
1559          blackbox_attrib.workspace == screen->getCurrentWorkspaceID());
1560
1561   /*
1562      We only do this check for normal windows and dialogs because other windows
1563      do this on purpose, such as kde's kicker, and we don't want to go moving
1564      it.
1565   */
1566   if (window_type == Type_Normal || window_type == Type_Dialog)
1567     if (! frame.rect.intersects(screen->getRect())) {
1568       // client is outside the screen, move it to the center
1569       configure((screen->getWidth() - frame.rect.width()) / 2,
1570                 (screen->getHeight() - frame.rect.height()) / 2,
1571                 frame.rect.width(), frame.rect.height());
1572     }
1573
1574   if (client.transientList.size() > 0) {
1575     // transfer focus to any modal transients
1576     BlackboxWindowList::iterator it, end = client.transientList.end();
1577     for (it = client.transientList.begin(); it != end; ++it)
1578       if ((*it)->flags.modal) return (*it)->setInputFocus();
1579   }
1580
1581   bool ret = True;
1582   if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1583     XSetInputFocus(blackbox->getXDisplay(), client.window,
1584                    RevertToPointerRoot, CurrentTime);
1585   } else {
1586     /* we could set the focus to none, since the window doesn't accept focus,
1587      * but we shouldn't set focus to nothing since this would surely make
1588      * someone angry
1589      */
1590     ret = False;
1591   }
1592
1593   if (flags.send_focus_message) {
1594     XEvent ce;
1595     ce.xclient.type = ClientMessage;
1596     ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1597     ce.xclient.display = blackbox->getXDisplay();
1598     ce.xclient.window = client.window;
1599     ce.xclient.format = 32;
1600     ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus);
1601     ce.xclient.data.l[1] = blackbox->getLastTime();
1602     ce.xclient.data.l[2] = 0l;
1603     ce.xclient.data.l[3] = 0l;
1604     ce.xclient.data.l[4] = 0l;
1605     XSendEvent(blackbox->getXDisplay(), client.window, False,
1606                NoEventMask, &ce);
1607     XFlush(blackbox->getXDisplay());
1608   }
1609
1610   return ret;
1611 }
1612
1613
1614 void BlackboxWindow::iconify(void) {
1615   if (flags.iconic) return;
1616
1617   // We don't need to worry about resizing because resizing always grabs the X
1618   // server. This should only ever happen if using opaque moving.
1619   if (flags.moving)
1620     endMove();
1621     
1622   if (windowmenu) windowmenu->hide();
1623
1624   /*
1625    * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1626    * we need to clear the event mask on client.window for a split second.
1627    * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1628    * split second, leaving us with a ghost window... so, we need to do this
1629    * while the X server is grabbed
1630    */
1631   unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1632                              StructureNotifyMask;
1633   XGrabServer(blackbox->getXDisplay());
1634   XSelectInput(blackbox->getXDisplay(), client.window,
1635                event_mask & ~StructureNotifyMask);
1636   XUnmapWindow(blackbox->getXDisplay(), client.window);
1637   XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1638   XUngrabServer(blackbox->getXDisplay());
1639
1640   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1641   flags.visible = False;
1642   flags.iconic = True;
1643
1644   setState(IconicState);
1645
1646   screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1647   if (flags.stuck) {
1648     for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
1649       if (i != blackbox_attrib.workspace)
1650         screen->getWorkspace(i)->removeWindow(this, True);
1651   }
1652
1653   if (isTransient()) {
1654     if (client.transient_for != (BlackboxWindow *) ~0ul &&
1655         ! client.transient_for->flags.iconic) {
1656       // iconify our transient_for
1657       client.transient_for->iconify();
1658     }
1659   }
1660
1661   screen->addIcon(this);
1662
1663   if (client.transientList.size() > 0) {
1664     // iconify all transients
1665     BlackboxWindowList::iterator it, end = client.transientList.end();
1666     for (it = client.transientList.begin(); it != end; ++it) {
1667       if (! (*it)->flags.iconic) (*it)->iconify();
1668     }
1669   }
1670   screen->updateStackingList();
1671 }
1672
1673
1674 void BlackboxWindow::show(void) {
1675   flags.visible = True;
1676   flags.iconic = False;
1677
1678   current_state = (flags.shaded) ? IconicState : NormalState;
1679   setState(current_state);
1680
1681   XMapWindow(blackbox->getXDisplay(), client.window);
1682   XMapSubwindows(blackbox->getXDisplay(), frame.window);
1683   XMapWindow(blackbox->getXDisplay(), frame.window);
1684
1685 #if 0
1686   int real_x, real_y;
1687   Window child;
1688   XTranslateCoordinates(blackbox->getXDisplay(), client.window,
1689                         screen->getRootWindow(),
1690                         0, 0, &real_x, &real_y, &child);
1691   fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
1692           client.rect.left(), client.rect.top(), real_x, real_y);
1693   assert(client.rect.left() == real_x && client.rect.top() == real_y);
1694 #endif
1695 }
1696
1697
1698 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1699   if (flags.iconic || reassoc)
1700     screen->reassociateWindow(this, BSENTINEL, False);
1701   else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
1702     return;
1703
1704   show();
1705
1706   // reassociate and deiconify all transients
1707   if (reassoc && client.transientList.size() > 0) {
1708     BlackboxWindowList::iterator it, end = client.transientList.end();
1709     for (it = client.transientList.begin(); it != end; ++it)
1710       (*it)->deiconify(True, False);
1711   }
1712
1713   if (raise)
1714     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1715 }
1716
1717
1718 void BlackboxWindow::close(void) {
1719   XEvent ce;
1720   ce.xclient.type = ClientMessage;
1721   ce.xclient.message_type =  xatom->getAtom(XAtom::wm_protocols);
1722   ce.xclient.display = blackbox->getXDisplay();
1723   ce.xclient.window = client.window;
1724   ce.xclient.format = 32;
1725   ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
1726   ce.xclient.data.l[1] = CurrentTime;
1727   ce.xclient.data.l[2] = 0l;
1728   ce.xclient.data.l[3] = 0l;
1729   ce.xclient.data.l[4] = 0l;
1730   XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1731   XFlush(blackbox->getXDisplay());
1732 }
1733
1734
1735 void BlackboxWindow::withdraw(void) {
1736   // We don't need to worry about resizing because resizing always grabs the X
1737   // server. This should only ever happen if using opaque moving.
1738   if (flags.moving)
1739     endMove();
1740     
1741   flags.visible = False;
1742   flags.iconic = False;
1743
1744   setState(current_state);
1745
1746   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1747
1748   XGrabServer(blackbox->getXDisplay());
1749
1750   unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1751                              StructureNotifyMask;
1752   XSelectInput(blackbox->getXDisplay(), client.window,
1753                event_mask & ~StructureNotifyMask);
1754   XUnmapWindow(blackbox->getXDisplay(), client.window);
1755   XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1756
1757   XUngrabServer(blackbox->getXDisplay());
1758
1759   if (windowmenu) windowmenu->hide();
1760 }
1761
1762
1763 void BlackboxWindow::maximize(unsigned int button) {
1764   // We don't need to worry about resizing because resizing always grabs the X
1765   // server. This should only ever happen if using opaque moving.
1766   if (flags.moving)
1767     endMove();
1768
1769   // handle case where menu is open then the max button is used instead
1770   if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1771
1772   if (flags.maximized) {
1773     flags.maximized = 0;
1774
1775     blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1776     blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1777
1778     /*
1779       when a resize finishes, maximize(0) is called to clear any maximization
1780       flags currently set.  Otherwise it still thinks it is maximized.
1781       so we do not need to call configure() because resizing will handle it
1782     */
1783     if (! flags.resizing)
1784       configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1785                 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1786
1787     blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1788     blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1789
1790     redrawAllButtons(); // in case it is not called in configure()
1791     setState(current_state);
1792     return;
1793   }
1794
1795   blackbox_attrib.premax_x = frame.rect.x();
1796   blackbox_attrib.premax_y = frame.rect.y();
1797   blackbox_attrib.premax_w = frame.rect.width();
1798   // use client.rect so that clients can be restored even if shaded
1799   blackbox_attrib.premax_h =
1800     client.rect.height() + frame.margin.top + frame.margin.bottom;
1801
1802 #ifdef    XINERAMA
1803   if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) {
1804     // find the area to use
1805     RectList availableAreas = screen->allAvailableAreas();
1806     RectList::iterator it, end = availableAreas.end();
1807
1808     for (it = availableAreas.begin(); it != end; ++it)
1809       if (it->intersects(frame.rect)) break;
1810     if (it == end) // the window isn't inside an area
1811       it = availableAreas.begin(); // so just default to the first one
1812
1813     frame.changing = *it;
1814   } else
1815 #endif // XINERAMA
1816   frame.changing = screen->availableArea();
1817
1818   switch(button) {
1819   case 1:
1820     blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1821     blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1822     break;
1823
1824   case 2:
1825     blackbox_attrib.flags |= AttribMaxVert;
1826     blackbox_attrib.attrib |= AttribMaxVert;
1827
1828     frame.changing.setX(frame.rect.x());
1829     frame.changing.setWidth(frame.rect.width());
1830     break;
1831
1832   case 3:
1833     blackbox_attrib.flags |= AttribMaxHoriz;
1834     blackbox_attrib.attrib |= AttribMaxHoriz;
1835
1836     frame.changing.setY(frame.rect.y());
1837     frame.changing.setHeight(frame.rect.height());
1838     break;
1839   }
1840
1841   constrain(TopLeft);
1842
1843   if (flags.shaded) {
1844     blackbox_attrib.flags ^= AttribShaded;
1845     blackbox_attrib.attrib ^= AttribShaded;
1846     flags.shaded = False;
1847   }
1848
1849   flags.maximized = button;
1850
1851   configure(frame.changing.x(), frame.changing.y(),
1852             frame.changing.width(), frame.changing.height());
1853   if (flags.focused)
1854     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1855   redrawAllButtons(); // in case it is not called in configure()
1856   setState(current_state);
1857 }
1858
1859
1860 // re-maximizes the window to take into account availableArea changes
1861 void BlackboxWindow::remaximize(void) {
1862   if (flags.shaded) {
1863     // we only update the window's attributes otherwise we lose the shade bit
1864     switch(flags.maximized) {
1865     case 1:
1866       blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1867       blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1868       break;
1869
1870     case 2:
1871       blackbox_attrib.flags |= AttribMaxVert;
1872       blackbox_attrib.attrib |= AttribMaxVert;
1873       break;
1874
1875     case 3:
1876       blackbox_attrib.flags |= AttribMaxHoriz;
1877       blackbox_attrib.attrib |= AttribMaxHoriz;
1878       break;
1879     }
1880     return;
1881   }
1882
1883   // save the original dimensions because maximize will wipe them out
1884   int premax_x = blackbox_attrib.premax_x,
1885     premax_y = blackbox_attrib.premax_y,
1886     premax_w = blackbox_attrib.premax_w,
1887     premax_h = blackbox_attrib.premax_h;
1888
1889   unsigned int button = flags.maximized;
1890   flags.maximized = 0; // trick maximize() into working
1891   maximize(button);
1892
1893   // restore saved values
1894   blackbox_attrib.premax_x = premax_x;
1895   blackbox_attrib.premax_y = premax_y;
1896   blackbox_attrib.premax_w = premax_w;
1897   blackbox_attrib.premax_h = premax_h;
1898 }
1899
1900
1901 void BlackboxWindow::setWorkspace(unsigned int n) {
1902   blackbox_attrib.flags |= AttribWorkspace;
1903   blackbox_attrib.workspace = n;
1904   if (n == BSENTINEL) { // iconified window
1905     /*
1906        we set the workspace to 'all workspaces' so that taskbars will show the
1907        window. otherwise, it made uniconifying a window imposible without the
1908        blackbox workspace menu
1909     */
1910     n = 0xffffffff;
1911   }
1912   xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
1913 }
1914
1915
1916 void BlackboxWindow::shade(void) {
1917   if (flags.shaded) {
1918     XResizeWindow(blackbox->getXDisplay(), frame.window,
1919                   frame.inside_w, frame.inside_h);
1920     flags.shaded = False;
1921     blackbox_attrib.flags ^= AttribShaded;
1922     blackbox_attrib.attrib ^= AttribShaded;
1923
1924     setState(NormalState);
1925
1926     // set the frame rect to the normal size
1927     frame.rect.setHeight(client.rect.height() + frame.margin.top +
1928                          frame.margin.bottom);
1929   } else {
1930     if (! (decorations & Decor_Titlebar))
1931       return; // can't shade it without a titlebar!
1932
1933     XResizeWindow(blackbox->getXDisplay(), frame.window,
1934                   frame.inside_w, frame.title_h);
1935     flags.shaded = True;
1936     blackbox_attrib.flags |= AttribShaded;
1937     blackbox_attrib.attrib |= AttribShaded;
1938
1939     setState(IconicState);
1940
1941     // set the frame rect to the shaded size
1942     frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
1943   }
1944 }
1945
1946
1947 /*
1948  * (Un)Sticks a window and its relatives.
1949  */
1950 void BlackboxWindow::stick(void) {
1951   if (flags.stuck) {
1952     blackbox_attrib.flags ^= AttribOmnipresent;
1953     blackbox_attrib.attrib ^= AttribOmnipresent;
1954
1955     flags.stuck = False;
1956     
1957     for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
1958       if (i != blackbox_attrib.workspace)
1959         screen->getWorkspace(i)->removeWindow(this, True);
1960
1961     if (! flags.iconic)
1962       screen->reassociateWindow(this, BSENTINEL, True);
1963     // temporary fix since sticky windows suck. set the hint to what we
1964     // actually hold in our data.
1965     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1966                     blackbox_attrib.workspace);
1967
1968     setState(current_state);
1969   } else {
1970     flags.stuck = True;
1971
1972     blackbox_attrib.flags |= AttribOmnipresent;
1973     blackbox_attrib.attrib |= AttribOmnipresent;
1974
1975     // temporary fix since sticky windows suck. set the hint to a different
1976     // value than that contained in the class' data.
1977     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1978                     0xffffffff);
1979     
1980     for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
1981       if (i != blackbox_attrib.workspace)
1982         screen->getWorkspace(i)->addWindow(this, False, True);
1983
1984     setState(current_state);
1985   }
1986   // go up the chain
1987   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
1988       client.transient_for->isStuck() != flags.stuck)
1989     client.transient_for->stick();
1990   // go down the chain
1991   BlackboxWindowList::iterator it;
1992   const BlackboxWindowList::iterator end = client.transientList.end();
1993   for (it = client.transientList.begin(); it != end; ++it)
1994     if ((*it)->isStuck() != flags.stuck)
1995       (*it)->stick();
1996 }
1997
1998
1999 void BlackboxWindow::redrawWindowFrame(void) const {
2000   if (decorations & Decor_Titlebar) {
2001     if (flags.focused) {
2002       if (frame.ftitle)
2003         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2004                                    frame.title, frame.ftitle);
2005       else
2006         XSetWindowBackground(blackbox->getXDisplay(),
2007                              frame.title, frame.ftitle_pixel);
2008     } else {
2009       if (frame.utitle)
2010         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2011                                    frame.title, frame.utitle);
2012       else
2013         XSetWindowBackground(blackbox->getXDisplay(),
2014                              frame.title, frame.utitle_pixel);
2015     }
2016     XClearWindow(blackbox->getXDisplay(), frame.title);
2017
2018     redrawLabel();
2019     redrawAllButtons();
2020   }
2021
2022   if (decorations & Decor_Handle) {
2023     if (flags.focused) {
2024       if (frame.fhandle)
2025         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2026                                    frame.handle, frame.fhandle);
2027       else
2028         XSetWindowBackground(blackbox->getXDisplay(),
2029                              frame.handle, frame.fhandle_pixel);
2030
2031       if (frame.fgrip) {
2032         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2033                                    frame.left_grip, frame.fgrip);
2034         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2035                                    frame.right_grip, frame.fgrip);
2036       } else {
2037         XSetWindowBackground(blackbox->getXDisplay(),
2038                              frame.left_grip, frame.fgrip_pixel);
2039         XSetWindowBackground(blackbox->getXDisplay(),
2040                              frame.right_grip, frame.fgrip_pixel);
2041       }
2042     } else {
2043       if (frame.uhandle)
2044         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2045                                    frame.handle, frame.uhandle);
2046       else
2047         XSetWindowBackground(blackbox->getXDisplay(),
2048                              frame.handle, frame.uhandle_pixel);
2049
2050       if (frame.ugrip) {
2051         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2052                                    frame.left_grip, frame.ugrip);
2053         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2054                                    frame.right_grip, frame.ugrip);
2055       } else {
2056         XSetWindowBackground(blackbox->getXDisplay(),
2057                              frame.left_grip, frame.ugrip_pixel);
2058         XSetWindowBackground(blackbox->getXDisplay(),
2059                              frame.right_grip, frame.ugrip_pixel);
2060       }
2061     }
2062     XClearWindow(blackbox->getXDisplay(), frame.handle);
2063     XClearWindow(blackbox->getXDisplay(), frame.left_grip);
2064     XClearWindow(blackbox->getXDisplay(), frame.right_grip);
2065   }
2066
2067   if (decorations & Decor_Border) {
2068     if (flags.focused)
2069       XSetWindowBorder(blackbox->getXDisplay(),
2070                        frame.plate, frame.fborder_pixel);
2071     else
2072       XSetWindowBorder(blackbox->getXDisplay(),
2073                        frame.plate, frame.uborder_pixel);
2074   }
2075 }
2076
2077
2078 void BlackboxWindow::setFocusFlag(bool focus) {
2079   // only focus a window if it is visible
2080   if (focus && ! flags.visible)
2081     return;
2082
2083   flags.focused = focus;
2084
2085   redrawWindowFrame();
2086
2087   if (flags.focused)
2088     blackbox->setFocusedWindow(this);
2089  
2090   if (! flags.iconic) {
2091     // iconic windows arent in a workspace menu!
2092     if (flags.stuck)
2093       screen->getCurrentWorkspace()->setFocused(this, isFocused());
2094     else
2095       screen->getWorkspace(blackbox_attrib.workspace)->
2096         setFocused(this, flags.focused);
2097   }
2098 }
2099
2100
2101 void BlackboxWindow::installColormap(bool install) {
2102   int i = 0, ncmap = 0;
2103   Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2104                                             client.window, &ncmap);
2105   if (cmaps) {
2106     XWindowAttributes wattrib;
2107     if (XGetWindowAttributes(blackbox->getXDisplay(),
2108                              client.window, &wattrib)) {
2109       if (install) {
2110         // install the window's colormap
2111         for (i = 0; i < ncmap; i++) {
2112           if (*(cmaps + i) == wattrib.colormap)
2113             // this window is using an installed color map... do not install
2114             install = False;
2115         }
2116         // otherwise, install the window's colormap
2117         if (install)
2118           XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2119       } else {
2120         // uninstall the window's colormap
2121         for (i = 0; i < ncmap; i++) {
2122           if (*(cmaps + i) == wattrib.colormap)
2123             // we found the colormap to uninstall
2124             XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2125         }
2126       }
2127     }
2128
2129     XFree(cmaps);
2130   }
2131 }
2132
2133
2134 void BlackboxWindow::setAllowedActions(void) {
2135   Atom actions[7];
2136   int num = 0;
2137   
2138   actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2139   actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2140   actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2141
2142   if (functions & Func_Move)
2143     actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2144   if (functions & Func_Resize)
2145     actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2146   if (functions & Func_Maximize) {
2147     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2148     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2149   }
2150
2151   xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2152                   actions, num);
2153 }
2154
2155
2156 void BlackboxWindow::setState(unsigned long new_state) {
2157   current_state = new_state;
2158
2159   unsigned long state[2];
2160   state[0] = current_state;
2161   state[1] = None;
2162   xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2163  
2164   xatom->setValue(client.window, XAtom::blackbox_attributes,
2165                   XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2166                   PropBlackboxAttributesElements);
2167
2168   Atom netstate[8];
2169   int num = 0;
2170   if (flags.modal)
2171     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2172   if (flags.shaded)
2173     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2174   if (flags.iconic)
2175     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2176   if (flags.skip_taskbar)
2177     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2178   if (flags.skip_pager)
2179     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2180   if (flags.fullscreen)
2181     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2182   if (flags.maximized == 1 || flags.maximized == 2)
2183     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2184   if (flags.maximized == 1 || flags.maximized == 3)
2185     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2186   xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2187                   netstate, num);
2188 }
2189
2190
2191 bool BlackboxWindow::getState(void) {
2192   bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2193                              current_state);
2194   if (! ret) current_state = 0;
2195   return ret;
2196 }
2197
2198
2199 void BlackboxWindow::restoreAttributes(void) {
2200   unsigned long num = PropBlackboxAttributesElements;
2201   BlackboxAttributes *net;
2202   if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2203                         XAtom::blackbox_attributes, num,
2204                         (unsigned long **)&net))
2205     return;
2206   if (num < PropBlackboxAttributesElements) {
2207     delete [] net;
2208     return;
2209   }
2210
2211   if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2212     flags.shaded = False;
2213     unsigned long orig_state = current_state;
2214     shade();
2215
2216     /*
2217       At this point in the life of a window, current_state should only be set
2218       to IconicState if the window was an *icon*, not if it was shaded.
2219     */
2220     if (orig_state != IconicState)
2221       current_state = WithdrawnState;
2222  }
2223
2224   if (net->workspace != screen->getCurrentWorkspaceID() &&
2225       net->workspace < screen->getWorkspaceCount())
2226     screen->reassociateWindow(this, net->workspace, True);
2227
2228   if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2229       (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2230     // set to WithdrawnState so it will be mapped on the new workspace
2231     if (current_state == NormalState) current_state = WithdrawnState;
2232   } else if (current_state == WithdrawnState) {
2233     // the window is on this workspace and is Withdrawn, so it is waiting to
2234     // be mapped
2235     current_state = NormalState;
2236   }
2237
2238   if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent &&
2239       ! flags.stuck) {
2240     stick();
2241
2242     // if the window was on another workspace, it was going to be hidden. this
2243     // specifies that the window should be mapped since it is sticky.
2244     if (current_state == WithdrawnState) current_state = NormalState;
2245   }
2246
2247   if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2248     int x = net->premax_x, y = net->premax_y;
2249     unsigned int w = net->premax_w, h = net->premax_h;
2250     flags.maximized = 0;
2251
2252     unsigned int m = 0;
2253     if ((net->flags & AttribMaxHoriz) &&
2254         (net->flags & AttribMaxVert))
2255       m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2256     else if (net->flags & AttribMaxVert)
2257       m = (net->attrib & AttribMaxVert) ? 2 : 0;
2258     else if (net->flags & AttribMaxHoriz)
2259       m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2260
2261     if (m) maximize(m);
2262
2263     blackbox_attrib.premax_x = x;
2264     blackbox_attrib.premax_y = y;
2265     blackbox_attrib.premax_w = w;
2266     blackbox_attrib.premax_h = h;
2267   }
2268
2269   if (net->flags & AttribDecoration) {
2270     switch (net->decoration) {
2271     case DecorNone:
2272       decorations = 0;
2273
2274       break;
2275
2276     default:
2277     case DecorNormal:
2278       decorations |= Decor_Titlebar | Decor_Handle | Decor_Border |
2279         Decor_Iconify | Decor_Maximize;
2280
2281       break;
2282
2283     case DecorTiny:
2284       decorations |= Decor_Titlebar | Decor_Iconify;
2285       decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
2286
2287       break;
2288
2289     case DecorTool:
2290       decorations |= Decor_Titlebar;
2291       decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
2292
2293       break;
2294     }
2295
2296     // sanity check the new decor
2297     if (! (functions & Func_Resize) || isTransient())
2298       decorations &= ~(Decor_Maximize | Decor_Handle);
2299     if (! (functions & Func_Maximize))
2300       decorations &= ~Decor_Maximize;
2301
2302     if (decorations & Decor_Titlebar) {
2303       if (functions & Func_Close)   // close button is controlled by function
2304         decorations |= Decor_Close; // not decor type
2305     } else { 
2306       if (flags.shaded) // we can not be shaded if we lack a titlebar
2307         shade();
2308     }
2309
2310     if (flags.visible && frame.window) {
2311       XMapSubwindows(blackbox->getXDisplay(), frame.window);
2312       XMapWindow(blackbox->getXDisplay(), frame.window);
2313     }
2314
2315     reconfigure();
2316     setState(current_state);
2317   }
2318
2319   // with the state set it will then be the map event's job to read the
2320   // window's state and behave accordingly
2321
2322   delete [] net;
2323 }
2324
2325
2326 /*
2327  * Positions the Rect r according the the client window position and
2328  * window gravity.
2329  */
2330 void BlackboxWindow::applyGravity(Rect &r) {
2331   // apply horizontal window gravity
2332   switch (client.win_gravity) {
2333   default:
2334   case NorthWestGravity:
2335   case SouthWestGravity:
2336   case WestGravity:
2337     r.setX(client.rect.x());
2338     break;
2339
2340   case NorthGravity:
2341   case SouthGravity:
2342   case CenterGravity:
2343     r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2344     break;
2345
2346   case NorthEastGravity:
2347   case SouthEastGravity:
2348   case EastGravity:
2349     r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2350     break;
2351
2352   case ForgetGravity:
2353   case StaticGravity:
2354     r.setX(client.rect.x() - frame.margin.left);
2355     break;
2356   }
2357
2358   // apply vertical window gravity
2359   switch (client.win_gravity) {
2360   default:
2361   case NorthWestGravity:
2362   case NorthEastGravity:
2363   case NorthGravity:
2364     r.setY(client.rect.y());
2365     break;
2366
2367   case CenterGravity:
2368   case EastGravity:
2369   case WestGravity:
2370     r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2371     break;
2372
2373   case SouthWestGravity:
2374   case SouthEastGravity:
2375   case SouthGravity:
2376     r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2377     break;
2378
2379   case ForgetGravity:
2380   case StaticGravity:
2381     r.setY(client.rect.y() - frame.margin.top);
2382     break;
2383   }
2384 }
2385
2386
2387 /*
2388  * The reverse of the applyGravity function.
2389  *
2390  * Positions the Rect r according to the frame window position and
2391  * window gravity.
2392  */
2393 void BlackboxWindow::restoreGravity(Rect &r) {
2394   // restore horizontal window gravity
2395   switch (client.win_gravity) {
2396   default:
2397   case NorthWestGravity:
2398   case SouthWestGravity:
2399   case WestGravity:
2400     r.setX(frame.rect.x());
2401     break;
2402
2403   case NorthGravity:
2404   case SouthGravity:
2405   case CenterGravity:
2406     r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2407     break;
2408
2409   case NorthEastGravity:
2410   case SouthEastGravity:
2411   case EastGravity:
2412     r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2413     break;
2414
2415   case ForgetGravity:
2416   case StaticGravity:
2417     r.setX(frame.rect.x() + frame.margin.left);
2418     break;
2419   }
2420
2421   // restore vertical window gravity
2422   switch (client.win_gravity) {
2423   default:
2424   case NorthWestGravity:
2425   case NorthEastGravity:
2426   case NorthGravity:
2427     r.setY(frame.rect.y());
2428     break;
2429
2430   case CenterGravity:
2431   case EastGravity:
2432   case WestGravity:
2433     r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2434     break;
2435
2436   case SouthWestGravity:
2437   case SouthEastGravity:
2438   case SouthGravity:
2439     r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2440     break;
2441
2442   case ForgetGravity:
2443   case StaticGravity:
2444     r.setY(frame.rect.y() + frame.margin.top);
2445     break;
2446   }
2447 }
2448
2449
2450 void BlackboxWindow::redrawLabel(void) const {
2451   if (flags.focused) {
2452     if (frame.flabel)
2453       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2454                                  frame.label, frame.flabel);
2455     else
2456       XSetWindowBackground(blackbox->getXDisplay(),
2457                            frame.label, frame.flabel_pixel);
2458   } else {
2459     if (frame.ulabel)
2460       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2461                                  frame.label, frame.ulabel);
2462     else
2463       XSetWindowBackground(blackbox->getXDisplay(),
2464                            frame.label, frame.ulabel_pixel);
2465   }
2466   XClearWindow(blackbox->getXDisplay(), frame.label);
2467
2468   WindowStyle *style = screen->getWindowStyle();
2469
2470   int pos = frame.bevel_w * 2;
2471   style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2472   style->font->drawString(frame.label, pos, 1,
2473                           (flags.focused ? style->l_text_focus :
2474                            style->l_text_unfocus),
2475                           client.title);
2476 }
2477
2478
2479 void BlackboxWindow::redrawAllButtons(void) const {
2480   if (frame.iconify_button) redrawIconifyButton(False);
2481   if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2482   if (frame.close_button) redrawCloseButton(False);
2483 }
2484
2485
2486 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2487   if (! pressed) {
2488     if (flags.focused) {
2489       if (frame.fbutton)
2490         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2491                                    frame.iconify_button, frame.fbutton);
2492       else
2493         XSetWindowBackground(blackbox->getXDisplay(),
2494                              frame.iconify_button, frame.fbutton_pixel);
2495     } else {
2496       if (frame.ubutton)
2497         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2498                                    frame.iconify_button, frame.ubutton);
2499       else
2500         XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2501                              frame.ubutton_pixel);
2502     }
2503   } else {
2504     if (frame.pbutton)
2505       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2506                                  frame.iconify_button, frame.pbutton);
2507     else
2508       XSetWindowBackground(blackbox->getXDisplay(),
2509                            frame.iconify_button, frame.pbutton_pixel);
2510   }
2511   XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2512
2513   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2514            screen->getWindowStyle()->b_pic_unfocus);
2515   XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2516                  2, (frame.button_w - 5), (frame.button_w - 5), 2);
2517 }
2518
2519
2520 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2521   if (! pressed) {
2522     if (flags.focused) {
2523       if (frame.fbutton)
2524         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2525                                    frame.maximize_button, frame.fbutton);
2526       else
2527         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2528                              frame.fbutton_pixel);
2529     } else {
2530       if (frame.ubutton)
2531         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2532                                    frame.maximize_button, frame.ubutton);
2533       else
2534         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2535                              frame.ubutton_pixel);
2536     }
2537   } else {
2538     if (frame.pbutton)
2539       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2540                                  frame.maximize_button, frame.pbutton);
2541     else
2542       XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2543                            frame.pbutton_pixel);
2544   }
2545   XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2546
2547   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2548            screen->getWindowStyle()->b_pic_unfocus);
2549   XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2550                  2, 2, (frame.button_w - 5), (frame.button_w - 5));
2551   XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2552             2, 3, (frame.button_w - 3), 3);
2553 }
2554
2555
2556 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2557   if (! pressed) {
2558     if (flags.focused) {
2559       if (frame.fbutton)
2560         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2561                                    frame.fbutton);
2562       else
2563         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2564                              frame.fbutton_pixel);
2565     } else {
2566       if (frame.ubutton)
2567         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2568                                    frame.ubutton);
2569       else
2570         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2571                              frame.ubutton_pixel);
2572     }
2573   } else {
2574     if (frame.pbutton)
2575       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2576                                  frame.close_button, frame.pbutton);
2577     else
2578       XSetWindowBackground(blackbox->getXDisplay(),
2579                            frame.close_button, frame.pbutton_pixel);
2580   }
2581   XClearWindow(blackbox->getXDisplay(), frame.close_button);
2582
2583   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2584            screen->getWindowStyle()->b_pic_unfocus);
2585   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2586             2, 2, (frame.button_w - 3), (frame.button_w - 3));
2587   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2588             2, (frame.button_w - 3), (frame.button_w - 3), 2);
2589 }
2590
2591
2592 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2593   if (re->window != client.window)
2594     return;
2595
2596 #ifdef    DEBUG
2597   fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2598           client.window);
2599 #endif // DEBUG
2600
2601   switch (current_state) {
2602   case IconicState:
2603     iconify();
2604     break;
2605
2606   case WithdrawnState:
2607     withdraw();
2608     break;
2609
2610   case NormalState:
2611   case InactiveState:
2612   case ZoomState:
2613   default:
2614     show();
2615     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2616     if (isNormal()) {
2617       if (! blackbox->isStartup()) {
2618         XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2619         if (screen->doFocusNew()|| (isTransient() && getTransientFor() &&
2620                                     getTransientFor()->isFocused())) {
2621           setInputFocus();
2622         }
2623         if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2624           int x, y, rx, ry;
2625           Window c, r;
2626           unsigned int m;
2627           XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2628                         &r, &c, &rx, &ry, &x, &y, &m);
2629           beginMove(rx, ry);
2630         }
2631       }
2632     }
2633     break;
2634   }
2635 }
2636
2637
2638 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2639   if (ue->window != client.window)
2640     return;
2641
2642 #ifdef    DEBUG
2643   fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2644           client.window);
2645 #endif // DEBUG
2646
2647   screen->unmanageWindow(this, False);
2648 }
2649
2650
2651 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2652   if (de->window != client.window)
2653     return;
2654
2655 #ifdef    DEBUG
2656   fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2657           client.window);
2658 #endif // DEBUG
2659
2660   screen->unmanageWindow(this, False);
2661 }
2662
2663
2664 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2665   if (re->window != client.window || re->parent == frame.plate)
2666     return;
2667
2668 #ifdef    DEBUG
2669   fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2670           "0x%lx.\n", client.window, re->parent);
2671 #endif // DEBUG
2672
2673   XEvent ev;
2674   ev.xreparent = *re;
2675   XPutBackEvent(blackbox->getXDisplay(), &ev);
2676   screen->unmanageWindow(this, True);
2677 }
2678
2679
2680 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2681   if (pe->state == PropertyDelete)
2682     return;
2683
2684 #ifdef    DEBUG
2685   fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2686           client.window);
2687 #endif
2688
2689   switch(pe->atom) {
2690   case XA_WM_CLASS:
2691   case XA_WM_CLIENT_MACHINE:
2692   case XA_WM_COMMAND:
2693     break;
2694
2695   case XA_WM_TRANSIENT_FOR: {
2696     // determine if this is a transient window
2697     getTransientInfo();
2698
2699     // adjust the window decorations based on transience
2700     if (isTransient()) {
2701       decorations &= ~(Decor_Maximize | Decor_Handle);
2702       functions &= ~Func_Maximize;
2703       setAllowedActions();
2704     }
2705
2706     reconfigure();
2707   }
2708     break;
2709
2710   case XA_WM_HINTS:
2711     getWMHints();
2712     break;
2713
2714   case XA_WM_ICON_NAME:
2715     getWMIconName();
2716     if (flags.iconic) screen->propagateWindowName(this);
2717     break;
2718
2719   case XAtom::net_wm_name:
2720   case XA_WM_NAME:
2721     getWMName();
2722
2723     if (decorations & Decor_Titlebar)
2724       redrawLabel();
2725
2726     screen->propagateWindowName(this);
2727     break;
2728
2729   case XA_WM_NORMAL_HINTS: {
2730     getWMNormalHints();
2731
2732     if ((client.normal_hint_flags & PMinSize) &&
2733         (client.normal_hint_flags & PMaxSize)) {
2734       // the window now can/can't resize itself, so the buttons need to be
2735       // regrabbed.
2736       ungrabButtons();
2737       if (client.max_width <= client.min_width &&
2738           client.max_height <= client.min_height) {
2739         decorations &= ~(Decor_Maximize | Decor_Handle);
2740         functions &= ~(Func_Resize | Func_Maximize);
2741       } else {
2742         if (! isTransient()) {
2743           decorations |= Decor_Maximize | Decor_Handle;
2744           functions |= Func_Maximize;
2745         }
2746         functions |= Func_Resize;
2747       }
2748       grabButtons();
2749       setAllowedActions();
2750     }
2751
2752     Rect old_rect = frame.rect;
2753
2754     upsize();
2755
2756     if (old_rect != frame.rect)
2757       reconfigure();
2758
2759     break;
2760   }
2761
2762   default:
2763     if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2764       getWMProtocols();
2765
2766       if ((decorations & Decor_Close) && (! frame.close_button)) {
2767         createCloseButton();
2768         if (decorations & Decor_Titlebar) {
2769           positionButtons(True);
2770           XMapSubwindows(blackbox->getXDisplay(), frame.title);
2771         }
2772         if (windowmenu) windowmenu->reconfigure();
2773       }
2774     } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2775       updateStrut();
2776     }
2777
2778     break;
2779   }
2780 }
2781
2782
2783 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2784 #ifdef DEBUG
2785   fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2786 #endif
2787
2788   if (frame.label == ee->window && (decorations & Decor_Titlebar))
2789     redrawLabel();
2790   else if (frame.close_button == ee->window)
2791     redrawCloseButton(False);
2792   else if (frame.maximize_button == ee->window)
2793     redrawMaximizeButton(flags.maximized);
2794   else if (frame.iconify_button == ee->window)
2795     redrawIconifyButton(False);
2796 }
2797
2798
2799 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2800   if (cr->window != client.window || flags.iconic)
2801     return;
2802
2803   if (cr->value_mask & CWBorderWidth)
2804     client.old_bw = cr->border_width;
2805
2806   if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2807     Rect req = frame.rect;
2808
2809     if (cr->value_mask & (CWX | CWY)) {
2810       if (cr->value_mask & CWX)
2811         client.rect.setX(cr->x);
2812       if (cr->value_mask & CWY)
2813         client.rect.setY(cr->y);
2814
2815       applyGravity(req);
2816     }
2817
2818     if (cr->value_mask & CWWidth)
2819       req.setWidth(cr->width + frame.margin.left + frame.margin.right);
2820
2821     if (cr->value_mask & CWHeight)
2822       req.setHeight(cr->height + frame.margin.top + frame.margin.bottom);
2823
2824     configure(req.x(), req.y(), req.width(), req.height());
2825   }
2826
2827   if (cr->value_mask & CWStackMode && !isDesktop()) {
2828     switch (cr->detail) {
2829     case Below:
2830     case BottomIf:
2831       screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2832       break;
2833
2834     case Above:
2835     case TopIf:
2836     default:
2837       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2838       break;
2839     }
2840   }
2841 }
2842
2843
2844 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
2845 #ifdef DEBUG
2846   fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
2847           client.window);
2848 #endif
2849
2850   if (frame.maximize_button == be->window && be->button <= 3) {
2851     redrawMaximizeButton(True);
2852   } else if (be->button == 1 || (be->button == 3 && be->state == ModMask)) {
2853     if (! flags.focused)
2854       setInputFocus();
2855
2856     if (frame.iconify_button == be->window) {
2857       redrawIconifyButton(True);
2858     } else if (frame.close_button == be->window) {
2859       redrawCloseButton(True);
2860     } else if (frame.plate == be->window) {
2861       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2862
2863       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2864
2865       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2866     } else {
2867       if (frame.title == be->window || frame.label == be->window) {
2868         if (((be->time - lastButtonPressTime) <=
2869              blackbox->getDoubleClickInterval()) ||
2870             (be->state == ControlMask)) {
2871           lastButtonPressTime = 0;
2872           shade();
2873         } else {
2874           lastButtonPressTime = be->time;
2875         }
2876       }
2877
2878       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2879
2880       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2881     }
2882   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2883              (be->window != frame.close_button)) {
2884     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2885   } else if (windowmenu && be->button == 3 &&
2886              (frame.title == be->window || frame.label == be->window ||
2887               frame.handle == be->window || frame.window == be->window)) {
2888     if (windowmenu->isVisible()) {
2889       windowmenu->hide();
2890     } else {
2891       int mx = be->x_root - windowmenu->getWidth() / 2,
2892           my = be->y_root - windowmenu->getHeight() / 2;
2893
2894       // snap the window menu into a corner/side if necessary
2895       int left_edge, right_edge, top_edge, bottom_edge;
2896
2897       /*
2898          the " + (frame.border_w * 2) - 1" bits are to get the proper width
2899          and height of the menu, as the sizes returned by it do not include
2900          the borders.
2901        */
2902       left_edge = frame.rect.x();
2903       right_edge = frame.rect.right() -
2904         (windowmenu->getWidth() + (frame.border_w * 2) - 1);
2905       top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
2906       bottom_edge = client.rect.bottom() -
2907         (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
2908         (frame.border_w + frame.mwm_border_w);
2909
2910       if (mx < left_edge)
2911         mx = left_edge;
2912       if (mx > right_edge)
2913         mx = right_edge;
2914       if (my < top_edge)
2915         my = top_edge;
2916       if (my > bottom_edge)
2917         my = bottom_edge;
2918
2919       windowmenu->move(mx, my);
2920       windowmenu->show();
2921       XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2922       XRaiseWindow(blackbox->getXDisplay(),
2923                    windowmenu->getSendToMenu()->getWindowID());
2924     }
2925   // mouse wheel up
2926   } else if (be->button == 4) {
2927     if ((be->window == frame.label ||
2928          be->window == frame.title ||
2929          be->window == frame.maximize_button ||
2930          be->window == frame.iconify_button ||
2931          be->window == frame.close_button) &&
2932         ! flags.shaded)
2933       shade();
2934   // mouse wheel down
2935   } else if (be->button == 5) {
2936     if ((be->window == frame.label ||
2937          be->window == frame.title ||
2938          be->window == frame.maximize_button ||
2939          be->window == frame.iconify_button ||
2940          be->window == frame.close_button) &&
2941         flags.shaded)
2942       shade();
2943   }
2944 }
2945
2946
2947 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
2948 #ifdef DEBUG
2949   fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
2950           client.window);
2951 #endif
2952
2953   if (re->window == frame.maximize_button &&
2954       re->button >= 1 && re->button <= 3) {
2955     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2956         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2957       maximize(re->button);
2958     } else {
2959       redrawMaximizeButton(flags.maximized);
2960     }
2961   } else if (re->window == frame.iconify_button && re->button == 1) {
2962     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2963         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2964       iconify();
2965     } else {
2966       redrawIconifyButton(False);
2967     }
2968   } else if (re->window == frame.close_button & re->button == 1) {
2969     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2970         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
2971       close();
2972     redrawCloseButton(False);
2973   } else if (flags.moving) {
2974     endMove();
2975   } else if (flags.resizing) {
2976     endResize();
2977   } else if (re->window == frame.window) {
2978     if (re->button == 2 && re->state == ModMask)
2979       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2980   }
2981 }
2982
2983
2984
2985 void BlackboxWindow::beginMove(int x_root, int y_root) {
2986   assert(! (flags.resizing || flags.moving));
2987
2988   /*
2989     Only one window can be moved/resized at a time. If another window is already
2990     being moved or resized, then stop it before whating to work with this one.
2991   */
2992   BlackboxWindow *changing = blackbox->getChangingWindow();
2993   if (changing && changing != this) {
2994     if (changing->flags.moving)
2995       changing->endMove();
2996     else // if (changing->flags.resizing)
2997       changing->endResize();
2998   }
2999   
3000   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3001                PointerMotionMask | ButtonReleaseMask,
3002                GrabModeAsync, GrabModeAsync,
3003                None, blackbox->getMoveCursor(), CurrentTime);
3004
3005   if (windowmenu && windowmenu->isVisible())
3006     windowmenu->hide();
3007
3008   flags.moving = True;
3009   blackbox->setChangingWindow(this);
3010
3011   if (! screen->doOpaqueMove()) {
3012     XGrabServer(blackbox->getXDisplay());
3013
3014     frame.changing = frame.rect;
3015     screen->showPosition(frame.changing.x(), frame.changing.y());
3016
3017     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3018                    screen->getOpGC(),
3019                    frame.changing.x(),
3020                    frame.changing.y(),
3021                    frame.changing.width() - 1,
3022                    frame.changing.height() - 1);
3023   }
3024
3025   frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3026   frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3027 }
3028
3029
3030 void BlackboxWindow::doMove(int x_root, int y_root) {
3031   assert(flags.moving);
3032   assert(blackbox->getChangingWindow() == this);
3033
3034   int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3035   dx -= frame.border_w;
3036   dy -= frame.border_w;
3037
3038   if (screen->doWorkspaceWarping())
3039     if (doWorkspaceWarping(x_root, y_root, dx, dy))
3040       return;
3041
3042   doWindowSnapping(dx, dy);
3043
3044   if (screen->doOpaqueMove()) {
3045     configure(dx, dy, frame.rect.width(), frame.rect.height());
3046   } else {
3047     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3048                    screen->getOpGC(),
3049                    frame.changing.x(),
3050                    frame.changing.y(),
3051                    frame.changing.width() - 1,
3052                    frame.changing.height() - 1);
3053
3054     frame.changing.setPos(dx, dy);
3055
3056     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3057                    screen->getOpGC(),
3058                    frame.changing.x(),
3059                    frame.changing.y(),
3060                    frame.changing.width() - 1,
3061                    frame.changing.height() - 1);
3062   }
3063
3064   screen->showPosition(dx, dy);
3065 }
3066
3067
3068 bool BlackboxWindow::doWorkspaceWarping(int x_root, int y_root,
3069                                         int dx, int dy) {
3070   // workspace warping
3071   bool warp = False;
3072   unsigned int dest = screen->getCurrentWorkspaceID();
3073   if (x_root <= 0) {
3074     warp = True;
3075
3076     if (dest > 0) dest--;
3077     else dest = screen->getNumberOfWorkspaces() - 1;
3078
3079   } else if (x_root >= screen->getRect().right()) {
3080     warp = True;
3081
3082     if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3083     else dest = 0;
3084   }
3085   if (! warp)
3086     return false;
3087
3088   endMove();
3089   bool focus = flags.focused; // had focus while moving?
3090   if (! flags.stuck)
3091     screen->reassociateWindow(this, dest, False);
3092   screen->changeWorkspaceID(dest);
3093   if (focus)
3094     setInputFocus();
3095
3096   /*
3097      If the XWarpPointer is done after the configure, we can end up
3098      grabbing another window, so made sure you do it first.
3099      */
3100   int dest_x;
3101   if (x_root <= 0) {
3102     dest_x = screen->getRect().right() - 1;
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   } else {
3110     dest_x = 0;
3111     XWarpPointer(blackbox->getXDisplay(), None, 
3112                  screen->getRootWindow(), 0, 0, 0, 0,
3113                  dest_x, y_root);
3114
3115     configure(dx - (screen->getRect().width() - 1), dy,
3116               frame.rect.width(), frame.rect.height());
3117   }
3118
3119   beginMove(dest_x, y_root);
3120   return true;
3121 }
3122
3123
3124 void BlackboxWindow::doWindowSnapping(int &dx, int &dy) {
3125   // how much resistance to edges to provide
3126   const int resistance_size = screen->getResistanceSize();
3127
3128   // how far away to snap
3129   const int snap_distance = screen->getSnapThreshold();
3130
3131   // how to snap windows
3132   const int snap_to_windows = screen->getWindowToWindowSnap();
3133   const int snap_to_edges = screen->getWindowToEdgeSnap();
3134   // the amount of space away from the edge to provide resistance/snap
3135   const int snap_offset = screen->getSnapOffset();
3136
3137   // find the geomeetery where the moving window currently is
3138   const Rect &moving = screen->doOpaqueMove() ? frame.rect : frame.changing;
3139
3140   // window corners
3141   const int wleft = dx,
3142            wright = dx + frame.rect.width() - 1,
3143              wtop = dy,
3144           wbottom = dy + frame.rect.height() - 1;
3145
3146   if (snap_to_windows) {
3147     RectList rectlist;
3148
3149     Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3150     assert(w);
3151
3152     // add windows on the workspace to the rect list
3153     const BlackboxWindowList& stack_list = w->getStackingList();
3154     BlackboxWindowList::const_iterator st_it, st_end = stack_list.end();
3155     for (st_it = stack_list.begin(); st_it != st_end; ++st_it)
3156       if (*st_it != this) // don't snap to ourself
3157         rectlist.push_back( (*st_it)->frameRect() );
3158
3159     // add the toolbar and the slit to the rect list.
3160     // (only if they are not hidden)
3161     Toolbar *tbar = screen->getToolbar();
3162     Slit *slit = screen->getSlit();
3163     Rect tbar_rect, slit_rect;
3164     unsigned int bwidth = screen->getBorderWidth() * 2;
3165
3166     if (! (screen->doHideToolbar() || tbar->isHidden())) {
3167       tbar_rect.setRect(tbar->getX(), tbar->getY(), tbar->getWidth() + bwidth,
3168                         tbar->getHeight() + bwidth);
3169       rectlist.push_back(tbar_rect);
3170     }
3171
3172     if (! slit->isHidden()) {
3173       slit_rect.setRect(slit->getX(), slit->getY(), slit->getWidth() + bwidth,
3174                         slit->getHeight() + bwidth);
3175       rectlist.push_back(slit_rect);
3176     }
3177
3178     RectList::const_iterator it, end = rectlist.end();
3179     for (it = rectlist.begin(); it != end; ++it) {
3180       bool snapped = False;
3181       const Rect &winrect = *it;
3182       Rect offsetrect;
3183       offsetrect.setCoords(winrect.left() - snap_offset,
3184                            winrect.top() - snap_offset,
3185                            winrect.right() + snap_offset,
3186                            winrect.bottom() + snap_offset);
3187
3188       if (snap_to_windows == BScreen::WindowResistance)
3189         // if the window is already over top of this snap target, then
3190         // resistance is futile, so just ignore it
3191         if (winrect.intersects(moving))
3192           continue;
3193
3194       int dleft, dright, dtop, dbottom;
3195
3196       // if the windows are in the same plane vertically
3197       if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3198           wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3199
3200         if (snap_to_windows == BScreen::WindowResistance) {
3201           dleft = wright - offsetrect.left();
3202           dright = offsetrect.right() - wleft;
3203
3204           // snap left of other window?
3205           if (dleft >= 0 && dleft < resistance_size &&
3206               dleft < (wright - wleft)) {
3207             dx = offsetrect.left() - frame.rect.width();
3208             snapped = True;
3209           }
3210           // snap right of other window?
3211           else if (dright >= 0 && dright < resistance_size &&
3212                    dright < (wright - wleft)) {
3213             dx = offsetrect.right() + 1;
3214             snapped = True;
3215           }
3216         } else { // BScreen::WindowSnap
3217           dleft = abs(wright - offsetrect.left());
3218           dright = abs(wleft - offsetrect.right());
3219
3220           // snap left of other window?
3221           if (dleft < snap_distance && dleft <= dright) {
3222             dx = offsetrect.left() - frame.rect.width();
3223             snapped = True;
3224           }
3225           // snap right of other window?
3226           else if (dright < snap_distance) {
3227             dx = offsetrect.right() + 1;
3228             snapped = True;
3229           }            
3230         }
3231
3232         if (snapped) {
3233           if (screen->getWindowCornerSnap()) {
3234             // try corner-snap to its other sides
3235             if (snap_to_windows == BScreen::WindowResistance) {
3236               dtop = winrect.top() - wtop;
3237               dbottom = wbottom - winrect.bottom();
3238               if (dtop > 0 && dtop < resistance_size) {
3239                 // if we're already past the top edge, then don't provide
3240                 // resistance
3241                 if (moving.top() >= winrect.top())
3242                   dy = winrect.top();
3243               } else if (dbottom > 0 && dbottom < resistance_size) {
3244                 // if we're already past the bottom edge, then don't provide
3245                 // resistance
3246                 if (moving.bottom() <= winrect.bottom())
3247                   dy = winrect.bottom() - frame.rect.height() + 1;
3248               }
3249             } else { // BScreen::WindowSnap
3250               dtop = abs(wtop - winrect.top());
3251               dbottom = abs(wbottom - winrect.bottom());
3252               if (dtop < snap_distance && dtop <= dbottom)
3253                 dy = winrect.top();
3254               else if (dbottom < snap_distance)
3255                 dy = winrect.bottom() - frame.rect.height() + 1;
3256             }
3257           }
3258
3259           continue;
3260         }
3261       }
3262
3263       // if the windows are on the same plane horizontally
3264       if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3265           wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3266
3267         if (snap_to_windows == BScreen::WindowResistance) {
3268           dtop = wbottom - offsetrect.top();
3269           dbottom = offsetrect.bottom() - wtop;
3270
3271           // snap top of other window?
3272           if (dtop >= 0 && dtop < resistance_size && dtop < (wbottom - wtop)) {
3273             dy = offsetrect.top() - frame.rect.height();
3274             snapped = True;
3275           }
3276           // snap bottom of other window?
3277           else if (dbottom >= 0 && dbottom < resistance_size &&
3278                    dbottom < (wbottom - wtop)) {
3279             dy = offsetrect.bottom() + 1;
3280             snapped = True;
3281           }
3282         } else { // BScreen::WindowSnap
3283           dtop = abs(wbottom - offsetrect.top());
3284           dbottom = abs(wtop - offsetrect.bottom());
3285
3286           // snap top of other window?
3287           if (dtop < snap_distance && dtop <= dbottom) {
3288             dy = offsetrect.top() - frame.rect.height();
3289             snapped = True;
3290           }
3291           // snap bottom of other window?
3292           else if (dbottom < snap_distance) {
3293             dy = offsetrect.bottom() + 1;
3294             snapped = True;
3295           }
3296
3297         }
3298
3299         if (snapped) {
3300           if (screen->getWindowCornerSnap()) {
3301             // try corner-snap to its other sides
3302             if (snap_to_windows == BScreen::WindowResistance) {
3303               dleft = winrect.left() - wleft;
3304               dright = wright - winrect.right();
3305               if (dleft > 0 && dleft < resistance_size) {
3306                 // if we're already past the left edge, then don't provide
3307                 // resistance
3308                 if (moving.left() >= winrect.left())
3309                   dx = winrect.left();
3310               } else if (dright > 0 && dright < resistance_size) {
3311                 // if we're already past the right edge, then don't provide
3312                 // resistance
3313                 if (moving.right() <= winrect.right())
3314                   dx = winrect.right() - frame.rect.width() + 1;
3315               }
3316             } else { // BScreen::WindowSnap
3317               dleft = abs(wleft - winrect.left());
3318               dright = abs(wright - winrect.right());
3319               if (dleft < snap_distance && dleft <= dright)
3320                 dx = winrect.left();
3321               else if (dright < snap_distance)
3322                 dx = winrect.right() - frame.rect.width() + 1;
3323             }
3324           }
3325
3326           continue;
3327         }
3328       }
3329     }
3330   }
3331
3332   if (snap_to_edges) {
3333     RectList rectlist;
3334
3335     // snap to the screen edges (and screen boundaries for xinerama)
3336 #ifdef    XINERAMA
3337     if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3338       rectlist.insert(rectlist.begin(),
3339                       screen->getXineramaAreas().begin(),
3340                       screen->getXineramaAreas().end());
3341     } else
3342 #endif // XINERAMA
3343       rectlist.push_back(screen->getRect());
3344
3345     RectList::const_iterator it, end = rectlist.end();
3346     for (it = rectlist.begin(); it != end; ++it) {
3347       const Rect &srect = *it;
3348       Rect offsetrect;
3349       offsetrect.setCoords(srect.left() + snap_offset,
3350                            srect.top() + snap_offset,
3351                            srect.right() - snap_offset,
3352                            srect.bottom() - snap_offset);
3353
3354       if (snap_to_edges == BScreen::WindowResistance) {
3355         // if we're not in the rectangle then don't snap to it.
3356         if (! srect.contains(moving))
3357           continue;
3358       } else { // BScreen::WindowSnap
3359         // if we're not in the rectangle then don't snap to it.
3360         if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3361                                     frame.rect.height())))
3362           continue;
3363       }
3364
3365       if (snap_to_edges == BScreen::WindowResistance) {
3366       int dleft = offsetrect.left() - wleft,
3367          dright = wright - offsetrect.right(),
3368            dtop = offsetrect.top() - wtop,
3369         dbottom = wbottom - offsetrect.bottom();
3370
3371         // snap left?
3372         if (dleft > 0 && dleft < resistance_size)
3373           dx = offsetrect.left();
3374         // snap right?
3375         else if (dright > 0 && dright < resistance_size)
3376           dx = offsetrect.right() - frame.rect.width() + 1;
3377
3378         // snap top?
3379         if (dtop > 0 && dtop < resistance_size)
3380           dy = offsetrect.top();
3381         // snap bottom?
3382         else if (dbottom > 0 && dbottom < resistance_size)
3383           dy = offsetrect.bottom() - frame.rect.height() + 1;
3384       } else { // BScreen::WindowSnap
3385         int dleft = abs(wleft - offsetrect.left()),
3386            dright = abs(wright - offsetrect.right()),
3387              dtop = abs(wtop - offsetrect.top()),
3388           dbottom = abs(wbottom - offsetrect.bottom());
3389
3390         // snap left?
3391         if (dleft < snap_distance && dleft <= dright)
3392           dx = offsetrect.left();
3393         // snap right?
3394         else if (dright < snap_distance)
3395           dx = offsetrect.right() - frame.rect.width() + 1;
3396
3397         // snap top?
3398         if (dtop < snap_distance && dtop <= dbottom)
3399           dy = offsetrect.top();
3400         // snap bottom?
3401         else if (dbottom < snap_distance)
3402           dy = offsetrect.bottom() - frame.rect.height() + 1;
3403       }
3404     }
3405   }
3406 }
3407
3408
3409 void BlackboxWindow::endMove(void) {
3410   assert(flags.moving);
3411   assert(blackbox->getChangingWindow() == this);
3412
3413   flags.moving = False;
3414   blackbox->setChangingWindow(0);
3415
3416   if (! screen->doOpaqueMove()) {
3417     /* when drawing the rubber band, we need to make sure we only draw inside
3418      * the frame... frame.changing_* contain the new coords for the window,
3419      * so we need to subtract 1 from changing_w/changing_h every where we
3420      * draw the rubber band (for both moving and resizing)
3421      */
3422     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3423                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3424                    frame.changing.width() - 1, frame.changing.height() - 1);
3425       XUngrabServer(blackbox->getXDisplay());
3426   
3427       configure(frame.changing.x(), frame.changing.y(),
3428                 frame.changing.width(), frame.changing.height());
3429   } else {
3430     configure(frame.rect.x(), frame.rect.y(),
3431               frame.rect.width(), frame.rect.height());
3432   }
3433   screen->hideGeometry();
3434
3435   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3436
3437   // if there are any left over motions from the move, drop them now
3438   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3439   XEvent e;
3440   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3441                                 MotionNotify, &e));
3442 }
3443
3444
3445 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3446   assert(! (flags.resizing || flags.moving));
3447
3448   /*
3449     Only one window can be moved/resized at a time. If another window is already
3450     being moved or resized, then stop it before whating to work with this one.
3451   */
3452   BlackboxWindow *changing = blackbox->getChangingWindow();
3453   if (changing && changing != this) {
3454     if (changing->flags.moving)
3455       changing->endMove();
3456     else // if (changing->flags.resizing)
3457       changing->endResize();
3458   }
3459
3460   resize_dir = dir;
3461
3462   Cursor cursor;
3463   Corner anchor;
3464   
3465   switch (resize_dir) {
3466   case BottomLeft:
3467     anchor = TopRight;
3468     cursor = blackbox->getLowerLeftAngleCursor();
3469     break;
3470
3471   case BottomRight:
3472     anchor = TopLeft;
3473     cursor = blackbox->getLowerRightAngleCursor();
3474     break;
3475
3476   case TopLeft:
3477     anchor = BottomRight;
3478     cursor = blackbox->getUpperLeftAngleCursor();
3479     break;
3480
3481   case TopRight:
3482     anchor = BottomLeft;
3483     cursor = blackbox->getUpperRightAngleCursor();
3484     break;
3485
3486   default:
3487     assert(false); // unhandled Corner
3488     return;        // unreachable, for the compiler
3489   }
3490   
3491   XGrabServer(blackbox->getXDisplay());
3492   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3493                PointerMotionMask | ButtonReleaseMask,
3494                GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3495
3496   flags.resizing = True;
3497   blackbox->setChangingWindow(this);
3498
3499   unsigned int gw, gh;
3500   frame.changing = frame.rect;
3501
3502   constrain(anchor,  &gw, &gh);
3503
3504   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3505                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3506                  frame.changing.width() - 1, frame.changing.height() - 1);
3507
3508   screen->showGeometry(gw, gh);
3509   
3510   frame.grab_x = x_root;
3511   frame.grab_y = y_root;
3512 }
3513
3514
3515 void BlackboxWindow::doResize(int x_root, int y_root) {
3516   assert(flags.resizing);
3517   assert(blackbox->getChangingWindow() == this);
3518
3519   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3520                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3521                  frame.changing.width() - 1, frame.changing.height() - 1);
3522
3523   unsigned int gw, gh;
3524   Corner anchor;
3525
3526   switch (resize_dir) {
3527   case BottomLeft:
3528     anchor = TopRight;
3529     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3530                            frame.rect.height() + (y_root - frame.grab_y));
3531     break;
3532   case BottomRight:
3533     anchor = TopLeft;
3534     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3535                            frame.rect.height() + (y_root - frame.grab_y));
3536     break;
3537   case TopLeft:
3538     anchor = BottomRight;
3539     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3540                            frame.rect.height() - (y_root - frame.grab_y));
3541     break;
3542   case TopRight:
3543     anchor = BottomLeft;
3544     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3545                            frame.rect.height() - (y_root - frame.grab_y));
3546     break;
3547
3548   default:
3549     assert(false); // unhandled Corner
3550     return;        // unreachable, for the compiler
3551   }
3552   
3553   constrain(anchor, &gw, &gh);
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
3559   screen->showGeometry(gw, gh);
3560 }
3561
3562
3563 void BlackboxWindow::endResize(void) {
3564   assert(flags.resizing);
3565   assert(blackbox->getChangingWindow() == this);
3566
3567   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3568                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3569                  frame.changing.width() - 1, frame.changing.height() - 1);
3570   XUngrabServer(blackbox->getXDisplay());
3571
3572   // unset maximized state after resized when fully maximized
3573   if (flags.maximized == 1)
3574     maximize(0);
3575   
3576   flags.resizing = False;
3577   blackbox->setChangingWindow(0);
3578
3579   configure(frame.changing.x(), frame.changing.y(),
3580             frame.changing.width(), frame.changing.height());
3581   screen->hideGeometry();
3582
3583   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3584   
3585   // if there are any left over motions from the resize, drop them now
3586   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3587   XEvent e;
3588   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3589                                 MotionNotify, &e));
3590 }
3591
3592
3593 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3594 #ifdef DEBUG
3595   fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3596           client.window);
3597 #endif
3598
3599   if (flags.moving) {
3600     doMove(me->x_root, me->y_root);
3601   } else if (flags.resizing) {
3602     doResize(me->x_root, me->y_root);
3603   } else {
3604     if (!flags.resizing && me->state & Button1Mask && (functions & Func_Move) &&
3605         (frame.title == me->window || frame.label == me->window ||
3606          frame.handle == me->window || frame.window == me->window)) {
3607       beginMove(me->x_root, me->y_root);
3608     } else if ((functions & Func_Resize) &&
3609                (me->state & Button1Mask && (me->window == frame.right_grip ||
3610                                             me->window == frame.left_grip)) ||
3611                (me->state & Button3Mask && me->state & ModMask &&
3612                 me->window == frame.window)) {
3613       unsigned int zones = screen->getResizeZones();
3614       Corner corner;
3615       
3616       if (me->window == frame.left_grip) {
3617         corner = BottomLeft;
3618       } else if (me->window == frame.right_grip || zones == 1) {
3619         corner = BottomRight;
3620       } else {
3621         bool top;
3622         bool left = (me->x_root - frame.rect.x() <=
3623                      static_cast<signed>(frame.rect.width() / 2));
3624         if (zones == 2)
3625           top = False;
3626         else // (zones == 4)
3627           top = (me->y_root - frame.rect.y() <=
3628                  static_cast<signed>(frame.rect.height() / 2));
3629         corner = (top ? (left ? TopLeft : TopRight) :
3630                         (left ? BottomLeft : BottomRight));
3631       }
3632
3633       beginResize(me->x_root, me->y_root, corner);
3634     }
3635   }
3636 }
3637
3638
3639 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) {
3640   if (! (screen->isSloppyFocus() && isVisible() && isNormal()))
3641     return;
3642
3643   XEvent e;
3644   bool leave = False, inferior = False;
3645
3646   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), ce->window,
3647                                 LeaveNotify, &e)) {
3648     if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) {
3649       leave = True;
3650       inferior = (e.xcrossing.detail == NotifyInferior);
3651     }
3652   }
3653
3654   if ((! leave || inferior) && ! isFocused()) {
3655     bool success = setInputFocus();
3656     if (success)    // if focus succeeded install the colormap
3657       installColormap(True); // XXX: shouldnt we honour no install?
3658   }
3659
3660   if (screen->doAutoRaise())
3661     timer->start();
3662 }
3663
3664
3665 void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) {
3666   if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal()))
3667     return;
3668
3669   installColormap(False);
3670
3671   if (timer->isTiming())
3672     timer->stop();
3673 }
3674
3675
3676 #ifdef    SHAPE
3677 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3678   if (blackbox->hasShapeExtensions() && flags.shaped) {
3679     configureShape();
3680   }
3681 }
3682 #endif // SHAPE
3683
3684
3685 bool BlackboxWindow::validateClient(void) const {
3686   XSync(blackbox->getXDisplay(), False);
3687
3688   XEvent e;
3689   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3690                              DestroyNotify, &e) ||
3691       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3692                              UnmapNotify, &e)) {
3693     XPutBackEvent(blackbox->getXDisplay(), &e);
3694
3695     return False;
3696   }
3697
3698   return True;
3699 }
3700
3701
3702 void BlackboxWindow::restore(bool remap) {
3703   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3704   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3705   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3706
3707   // do not leave a shaded window as an icon unless it was an icon
3708   if (flags.shaded && ! flags.iconic)
3709     setState(NormalState);
3710
3711   restoreGravity(client.rect);
3712
3713   XUnmapWindow(blackbox->getXDisplay(), frame.window);
3714   XUnmapWindow(blackbox->getXDisplay(), client.window);
3715
3716   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3717
3718   XEvent ev;
3719   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3720                              ReparentNotify, &ev)) {
3721     remap = True;
3722   } else {
3723     // according to the ICCCM - if the client doesn't reparent to
3724     // root, then we have to do it for them
3725     XReparentWindow(blackbox->getXDisplay(), client.window,
3726                     screen->getRootWindow(),
3727                     client.rect.x(), client.rect.y());
3728   }
3729
3730   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3731 }
3732
3733
3734 // timer for autoraise
3735 void BlackboxWindow::timeout(void) {
3736   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3737 }
3738
3739
3740 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
3741   if ((net->flags & AttribShaded) &&
3742       ((blackbox_attrib.attrib & AttribShaded) !=
3743        (net->attrib & AttribShaded)))
3744     shade();
3745
3746   if (flags.visible && // watch out for requests when we can not be seen
3747       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3748       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3749        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3750     if (flags.maximized) {
3751       maximize(0);
3752     } else {
3753       int button = 0;
3754
3755       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3756         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
3757       else if (net->flags & AttribMaxVert)
3758         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3759       else if (net->flags & AttribMaxHoriz)
3760         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3761
3762       maximize(button);
3763     }
3764   }
3765
3766   if ((net->flags & AttribOmnipresent) &&
3767       ((blackbox_attrib.attrib & AttribOmnipresent) !=
3768        (net->attrib & AttribOmnipresent)))
3769     stick();
3770
3771   if ((net->flags & AttribWorkspace) &&
3772       (blackbox_attrib.workspace != net->workspace)) {
3773     screen->reassociateWindow(this, net->workspace, True);
3774
3775     if (screen->getCurrentWorkspaceID() != net->workspace) {
3776       withdraw();
3777     } else {
3778       show();
3779       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3780     }
3781   }
3782
3783   if (net->flags & AttribDecoration) {
3784     switch (net->decoration) {
3785     case DecorNone:
3786       decorations = 0;
3787
3788       break;
3789
3790     default:
3791     case DecorNormal:
3792       decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
3793   
3794       decorations = ((functions & Func_Resize) && !isTransient() ?
3795                      decorations | Decor_Handle :
3796                      decorations &= ~Decor_Handle);
3797       decorations = (functions & Func_Maximize ?
3798                      decorations | Decor_Maximize :
3799                      decorations &= ~Decor_Maximize);
3800
3801       break;
3802
3803     case DecorTiny:
3804       decorations |= Decor_Titlebar | Decor_Iconify;
3805       decorations &= ~(Decor_Border | Decor_Handle);
3806       
3807       decorations = (functions & Func_Maximize ?
3808                      decorations | Decor_Maximize :
3809                      decorations &= ~Decor_Maximize);
3810
3811       break;
3812
3813     case DecorTool:
3814       decorations |= Decor_Titlebar;
3815       decorations &= ~(Decor_Iconify | Decor_Border);
3816
3817       decorations = ((functions & Func_Resize) && !isTransient() ?
3818                      decorations | Decor_Handle :
3819                      decorations &= ~Decor_Handle);
3820       decorations = (functions & Func_Maximize ?
3821                      decorations | Decor_Maximize :
3822                      decorations &= ~Decor_Maximize);
3823
3824       break;
3825     }
3826
3827     // we can not be shaded if we lack a titlebar
3828     if (flags.shaded && ! (decorations & Decor_Titlebar))
3829       shade();
3830
3831     if (flags.visible && frame.window) {
3832       XMapSubwindows(blackbox->getXDisplay(), frame.window);
3833       XMapWindow(blackbox->getXDisplay(), frame.window);
3834     }
3835
3836     reconfigure();
3837     setState(current_state);
3838   }
3839 }
3840
3841
3842 /*
3843  * Set the sizes of all components of the window frame
3844  * (the window decorations).
3845  * These values are based upon the current style settings and the client
3846  * window's dimensions.
3847  */
3848 void BlackboxWindow::upsize(void) {
3849   frame.bevel_w = screen->getBevelWidth();
3850
3851   if (decorations & Decor_Border) {
3852     frame.border_w = screen->getBorderWidth();
3853     if (! isTransient())
3854       frame.mwm_border_w = screen->getFrameWidth();
3855     else
3856       frame.mwm_border_w = 0;
3857   } else {
3858     frame.mwm_border_w = frame.border_w = 0;
3859   }
3860
3861   if (decorations & Decor_Titlebar) {
3862     // the height of the titlebar is based upon the height of the font being
3863     // used to display the window's title
3864     WindowStyle *style = screen->getWindowStyle();
3865     frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3866
3867     frame.label_h = frame.title_h - (frame.bevel_w * 2);
3868     frame.button_w = (frame.label_h - 2);
3869
3870     // set the top frame margin
3871     frame.margin.top = frame.border_w + frame.title_h +
3872                        frame.border_w + frame.mwm_border_w;
3873   } else {
3874     frame.title_h = 0;
3875     frame.label_h = 0;
3876     frame.button_w = 0;
3877
3878     // set the top frame margin
3879     frame.margin.top = frame.border_w + frame.mwm_border_w;
3880   }
3881
3882   // set the left/right frame margin
3883   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3884
3885   if (decorations & Decor_Handle) {
3886     frame.grip_w = frame.button_w * 2;
3887     frame.handle_h = screen->getHandleWidth();
3888
3889     // set the bottom frame margin
3890     frame.margin.bottom = frame.border_w + frame.handle_h +
3891                           frame.border_w + frame.mwm_border_w;
3892   } else {
3893     frame.handle_h = 0;
3894     frame.grip_w = 0;
3895
3896     // set the bottom frame margin
3897     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3898   }
3899
3900   /*
3901     We first get the normal dimensions and use this to define the inside_w/h
3902     then we modify the height if shading is in effect.
3903     If the shade state is not considered then frame.rect gets reset to the
3904     normal window size on a reconfigure() call resulting in improper
3905     dimensions appearing in move/resize and other events.
3906   */
3907   unsigned int
3908     height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3909     width = client.rect.width() + frame.margin.left + frame.margin.right;
3910
3911   frame.inside_w = width - (frame.border_w * 2);
3912   frame.inside_h = height - (frame.border_w * 2);
3913
3914   if (flags.shaded)
3915     height = frame.title_h + (frame.border_w * 2);
3916   frame.rect.setSize(width, height);
3917 }
3918
3919
3920 /*
3921  * Calculate the size of the client window and constrain it to the
3922  * size specified by the size hints of the client window.
3923  *
3924  * The logical width and height are placed into pw and ph, if they
3925  * are non-zero.  Logical size refers to the users perception of
3926  * the window size (for example an xterm resizes in cells, not in pixels).
3927  * pw and ph are then used to display the geometry during window moves, resize,
3928  * etc.
3929  *
3930  * The physical geometry is placed into frame.changing_{x,y,width,height}.
3931  * Physical geometry refers to the geometry of the window in pixels.
3932  */
3933 void BlackboxWindow::constrain(Corner anchor,
3934                                unsigned int *pw, unsigned int *ph) {
3935   // frame.changing represents the requested frame size, we need to
3936   // strip the frame margin off and constrain the client size
3937   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3938                            frame.changing.top() + frame.margin.top,
3939                            frame.changing.right() - frame.margin.right,
3940                            frame.changing.bottom() - frame.margin.bottom);
3941
3942   unsigned int dw = frame.changing.width(), dh = frame.changing.height(),
3943     base_width = (client.base_width) ? client.base_width : client.min_width,
3944     base_height = (client.base_height) ? client.base_height :
3945                                          client.min_height;
3946
3947   // constrain
3948   if (dw < client.min_width) dw = client.min_width;
3949   if (dh < client.min_height) dh = client.min_height;
3950   if (dw > client.max_width) dw = client.max_width;
3951   if (dh > client.max_height) dh = client.max_height;
3952
3953   assert(dw >= base_width && dh >= base_height);
3954
3955   if (client.width_inc > 1) {
3956     dw -= base_width;
3957     dw /= client.width_inc;
3958   }
3959   if (client.height_inc > 1) {
3960     dh -= base_height;
3961     dh /= client.height_inc;
3962   }
3963
3964   if (pw)
3965     *pw = dw;
3966
3967   if (ph)
3968     *ph = dh;
3969
3970   if (client.width_inc > 1) {
3971     dw *= client.width_inc;
3972     dw += base_width;
3973   }
3974   if (client.height_inc > 1) {
3975     dh *= client.height_inc;
3976     dh += base_height;
3977   }
3978
3979   frame.changing.setSize(dw, dh);
3980
3981   // add the frame margin back onto frame.changing
3982   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3983                            frame.changing.top() - frame.margin.top,
3984                            frame.changing.right() + frame.margin.right,
3985                            frame.changing.bottom() + frame.margin.bottom);
3986
3987   // move frame.changing to the specified anchor
3988   int dx = 0,
3989       dy = 0;
3990   switch (anchor) {
3991   case TopLeft:
3992     break;
3993
3994   case TopRight:
3995     dx = frame.rect.right() - frame.changing.right();
3996     break;
3997
3998   case BottomLeft:
3999     dy = frame.rect.bottom() - frame.changing.bottom();
4000     break;
4001
4002   case BottomRight:
4003     dx = frame.rect.right() - frame.changing.right();
4004     dy = frame.rect.bottom() - frame.changing.bottom();
4005     break;
4006
4007   default:
4008     assert(false);  // unhandled corner
4009   }
4010   frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
4011 }
4012
4013
4014 void WindowStyle::doJustify(const std::string &text, int &start_pos,
4015                             unsigned int max_length,
4016                             unsigned int modifier) const {
4017   size_t text_len = text.size();
4018   unsigned int length;
4019
4020   do {
4021     length = font->measureString(string(text, 0, text_len)) + modifier;
4022   } while (length > max_length && text_len-- > 0);
4023
4024   switch (justify) {
4025   case RightJustify:
4026     start_pos += max_length - length;
4027     break;
4028
4029   case CenterJustify:
4030     start_pos += (max_length - length) / 2;
4031     break;
4032
4033   case LeftJustify:
4034   default:
4035     break;
4036   }
4037 }
4038
4039
4040 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
4041   : blackbox(b), group(_group) {
4042   XWindowAttributes wattrib;
4043   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
4044     // group window doesn't seem to exist anymore
4045     delete this;
4046     return;
4047   }
4048
4049   XSelectInput(blackbox->getXDisplay(), group,
4050                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
4051
4052   blackbox->saveGroupSearch(group, this);
4053 }
4054
4055
4056 BWindowGroup::~BWindowGroup(void) {
4057   blackbox->removeGroupSearch(group);
4058 }
4059
4060
4061 BlackboxWindow *
4062 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
4063   BlackboxWindow *ret = blackbox->getFocusedWindow();
4064
4065   // does the focus window match (or any transient_fors)?
4066   for (; ret; ret = ret->getTransientFor()) {
4067     if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4068         (! ret->isTransient() || allow_transients))
4069       break;
4070   }
4071
4072   if (ret) return ret;
4073
4074   // the focus window didn't match, look in the group's window list
4075   BlackboxWindowList::const_iterator it, end = windowList.end();
4076   for (it = windowList.begin(); it != end; ++it) {
4077     ret = *it;
4078     if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4079         (! ret->isTransient() || allow_transients))
4080       break;
4081   }
4082
4083   return ret;
4084 }