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