]> icculus.org git repositories - mikachu/openbox.git/blob - src/Window.cc
make all parents/transients that are related have the same sticky state.
[mikachu/openbox.git] / src / Window.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Window.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh at debian.org>
4 // Copyright (c) 1997 - 2000, 2002 Brad Hughes <bhughes at trolltech.com>
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <X11/Xatom.h>
30 #include <X11/keysym.h>
31
32 #ifdef HAVE_STRING_H
33 #  include <string.h>
34 #endif // HAVE_STRING_H
35
36 #ifdef    DEBUG
37 #  ifdef    HAVE_STDIO_H
38 #    include <stdio.h>
39 #  endif // HAVE_STDIO_H
40 #endif // DEBUG
41 }
42
43 #include <cstdlib>
44
45 #include "i18n.hh"
46 #include "blackbox.hh"
47 #include "GCCache.hh"
48 #include "Iconmenu.hh"
49 #include "Image.hh"
50 #include "Screen.hh"
51 #include "Toolbar.hh"
52 #include "Util.hh"
53 #include "Window.hh"
54 #include "Windowmenu.hh"
55 #include "Workspace.hh"
56 #include "Slit.hh"
57
58 using std::string;
59
60 /*
61  * Initializes the class with default values/the window's set initial values.
62  */
63 BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
64   // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
65   // sizeof(BlackboxWindow));
66
67 #ifdef    DEBUG
68   fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
69 #endif // DEBUG
70
71   // set timer to zero... it is initialized properly later, so we check
72   // if timer is zero in the destructor, and assume that the window is not
73   // fully constructed if timer is zero...
74   timer = 0;
75   blackbox = b;
76   client.window = w;
77   screen = s;
78   xatom = blackbox->getXAtom();
79
80   if (! validateClient()) {
81     delete this;
82     return;
83   }
84
85   // set the eventmask early in the game so that we make sure we get
86   // all the events we are interested in
87   XSetWindowAttributes attrib_set;
88   attrib_set.event_mask = PropertyChangeMask | FocusChangeMask |
89                           StructureNotifyMask;
90   attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
91                                      ButtonMotionMask;
92   XChangeWindowAttributes(blackbox->getXDisplay(), client.window,
93                           CWEventMask|CWDontPropagate, &attrib_set);
94
95   // fetch client size and placement
96   XWindowAttributes wattrib;
97   if ((! XGetWindowAttributes(blackbox->getXDisplay(),
98                               client.window, &wattrib)) ||
99       (! wattrib.screen) || wattrib.override_redirect) {
100 #ifdef    DEBUG
101     fprintf(stderr,
102             "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
103 #endif // DEBUG
104
105     delete this;
106     return;
107   }
108
109   flags.moving = flags.resizing = flags.shaded = flags.visible =
110     flags.iconic = flags.focused = flags.stuck = flags.modal =
111     flags.send_focus_message = flags.shaped = False;
112   flags.maximized = 0;
113
114   blackbox_attrib.workspace = window_number = BSENTINEL;
115
116   blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack
117     = blackbox_attrib.decoration = 0l;
118   blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
119   blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
120
121   frame.border_w = 1;
122   frame.window = frame.plate = frame.title = frame.handle = None;
123   frame.close_button = frame.iconify_button = frame.maximize_button = None;
124   frame.right_grip = frame.left_grip = None;
125
126   frame.ulabel_pixel = frame.flabel_pixel = frame.utitle_pixel =
127   frame.ftitle_pixel = frame.uhandle_pixel = frame.fhandle_pixel =
128     frame.ubutton_pixel = frame.fbutton_pixel = frame.pbutton_pixel =
129     frame.uborder_pixel = frame.fborder_pixel = frame.ugrip_pixel =
130     frame.fgrip_pixel = 0;
131   frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
132   frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
133   frame.pbutton = frame.ugrip = frame.fgrip = decorations;
134
135   decorations = Decor_Titlebar | Decor_Border | Decor_Handle |
136                 Decor_Iconify | Decor_Maximize;
137   functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
138
139   client.wm_hint_flags = client.normal_hint_flags = 0;
140   client.transient_for = 0;
141
142   // get the initial size and location of client window (relative to the
143   // _root window_). This position is the reference point used with the
144   // window's gravity to find the window's initial position.
145   client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
146   client.old_bw = wattrib.border_width;
147
148   windowmenu = 0;
149   lastButtonPressTime = 0;
150
151   timer = new BTimer(blackbox, this);
152   timer->setTimeout(blackbox->getAutoRaiseDelay());
153
154   if (! getBlackboxHints())
155     getMWMHints();
156
157   // get size, aspect, minimum/maximum size and other hints set by the
158   // client
159   getWMProtocols();
160   getWMHints();
161   getWMNormalHints();
162
163   if (client.initial_state == WithdrawnState) {
164     screen->getSlit()->addClient(client.window);
165     delete this;
166     return;
167   }
168
169   frame.window = createToplevelWindow();
170   frame.plate = createChildWindow(frame.window);
171   associateClientWindow();
172
173   blackbox->saveWindowSearch(frame.window, this);
174   blackbox->saveWindowSearch(frame.plate, this);
175   blackbox->saveWindowSearch(client.window, this);
176
177   // determine if this is a transient window
178   getTransientInfo();
179
180   // adjust the window decorations based on transience and window sizes
181   if (isTransient()) {
182     decorations &= ~(Decor_Maximize | Decor_Handle);
183     functions &= ~Func_Maximize;
184   }
185
186   if ((client.normal_hint_flags & PMinSize) &&
187       (client.normal_hint_flags & PMaxSize) &&
188       client.max_width <= client.min_width &&
189       client.max_height <= client.min_height) {
190     decorations &= ~(Decor_Maximize | Decor_Handle);
191     functions &= ~(Func_Resize | Func_Maximize);
192   }
193   upsize();
194
195   bool place_window = True;
196   if (blackbox->isStartup() || isTransient() ||
197       client.normal_hint_flags & (PPosition|USPosition)) {
198     setGravityOffsets();
199
200
201     if (blackbox->isStartup() ||
202         client.rect.intersects(screen->availableArea()))
203       place_window = False;
204   }
205
206   if (decorations & Decor_Titlebar)
207     createTitlebar();
208
209   if (decorations & Decor_Handle)
210     createHandle();
211
212 #ifdef    SHAPE
213   if (blackbox->hasShapeExtensions() && flags.shaped) {
214     configureShape();
215   }
216 #endif // SHAPE
217
218   if ((! screen->isSloppyFocus()) || screen->doClickRaise()) {
219     // grab button 1 for changing focus/raising
220     blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
221                          GrabModeSync, GrabModeSync, frame.plate, None);
222   }
223
224   blackbox->grabButton(Button1, Mod1Mask, frame.window, True,
225                        ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
226                        GrabModeAsync, frame.window, blackbox->getMoveCursor());
227   blackbox->grabButton(Button2, Mod1Mask, frame.window, True,
228                        ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
229                        frame.window, None);
230   blackbox->grabButton(Button3, Mod1Mask, frame.window, True,
231                        ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
232                        GrabModeAsync, frame.window,
233                        blackbox->getLowerRightAngleCursor());
234
235   positionWindows();
236   decorate();
237
238   if (decorations & Decor_Titlebar)
239     XMapSubwindows(blackbox->getXDisplay(), frame.title);
240   XMapSubwindows(blackbox->getXDisplay(), frame.window);
241
242   windowmenu = new Windowmenu(this);
243
244   if (blackbox_attrib.workspace >= screen->getWorkspaceCount())
245     screen->getCurrentWorkspace()->addWindow(this, place_window);
246   else
247     screen->getWorkspace(blackbox_attrib.workspace)->
248       addWindow(this, place_window);
249
250   if (! place_window) {
251     // don't need to call configure if we are letting the workspace
252     // place the window
253     configure(frame.rect.x(), frame.rect.y(),
254               frame.rect.width(), frame.rect.height());
255   }
256
257   // get sticky state from our parent window if we've got one
258   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
259       client.transient_for->isStuck() != flags.stuck)
260     stick();
261
262   if (flags.shaded) {
263     flags.shaded = False;
264     shade();
265   }
266
267   if (flags.maximized && (functions & Func_Maximize)) {
268     remaximize();
269   }
270
271   setFocusFlag(False);
272 }
273
274
275 BlackboxWindow::~BlackboxWindow(void) {
276
277 #ifdef    DEBUG
278   fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
279           client.window);
280 #endif // DEBUG
281
282   if (! timer) // window not managed...
283     return;
284
285   if (flags.moving || flags.resizing) {
286     screen->hideGeometry();
287     XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
288   }
289
290   delete timer;
291
292   delete windowmenu;
293
294   if (client.window_group) {
295     BWindowGroup *group = blackbox->searchGroup(client.window_group);
296     if (group) group->removeWindow(this);
297   }
298
299   // remove ourselves from our transient_for
300   if (isTransient()) {
301     if (client.transient_for != (BlackboxWindow *) ~0ul) {
302       client.transient_for->client.transientList.remove(this);
303     }
304     client.transient_for = (BlackboxWindow*) 0;
305   }
306
307   if (client.transientList.size() > 0) {
308     // reset transient_for for all transients
309     BlackboxWindowList::iterator it, end = client.transientList.end();
310     for (it = client.transientList.begin(); it != end; ++it) {
311       (*it)->client.transient_for = (BlackboxWindow*) 0;
312     }
313   }
314
315   if (frame.title)
316     destroyTitlebar();
317
318   if (frame.handle)
319     destroyHandle();
320
321   if (frame.plate) {
322     blackbox->removeWindowSearch(frame.plate);
323     XDestroyWindow(blackbox->getXDisplay(), frame.plate);
324   }
325
326   if (frame.window) {
327     blackbox->removeWindowSearch(frame.window);
328     XDestroyWindow(blackbox->getXDisplay(), frame.window);
329   }
330
331   blackbox->removeWindowSearch(client.window);
332 }
333
334
335 /*
336  * Creates a new top level window, with a given location, size, and border
337  * width.
338  * Returns: the newly created window
339  */
340 Window BlackboxWindow::createToplevelWindow(void) {
341   XSetWindowAttributes attrib_create;
342   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
343                               CWOverrideRedirect | CWEventMask;
344
345   attrib_create.background_pixmap = None;
346   attrib_create.colormap = screen->getColormap();
347   attrib_create.override_redirect = True;
348   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
349                              ButtonMotionMask | EnterWindowMask;
350
351   return XCreateWindow(blackbox->getXDisplay(), screen->getRootWindow(),
352                        -1, -1, 1, 1, frame.border_w, screen->getDepth(),
353                        InputOutput, screen->getVisual(), create_mask,
354                        &attrib_create);
355 }
356
357
358 /*
359  * Creates a child window, and optionally associates a given cursor with
360  * the new window.
361  */
362 Window BlackboxWindow::createChildWindow(Window parent, Cursor cursor) {
363   XSetWindowAttributes attrib_create;
364   unsigned long create_mask = CWBackPixmap | CWBorderPixel |
365                               CWEventMask;
366
367   attrib_create.background_pixmap = None;
368   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
369                              ButtonMotionMask | ExposureMask;
370
371   if (cursor) {
372     create_mask |= CWCursor;
373     attrib_create.cursor = cursor;
374   }
375
376   return XCreateWindow(blackbox->getXDisplay(), parent, 0, 0, 1, 1, 0,
377                        screen->getDepth(), InputOutput, screen->getVisual(),
378                        create_mask, &attrib_create);
379 }
380
381
382 void BlackboxWindow::associateClientWindow(void) {
383   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, 0);
384   getWMName();
385   getWMIconName();
386
387   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeInsert);
388
389   XSelectInput(blackbox->getXDisplay(), frame.plate, SubstructureRedirectMask);
390
391   XGrabServer(blackbox->getXDisplay());
392   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
393   XReparentWindow(blackbox->getXDisplay(), client.window, frame.plate, 0, 0);
394   XSelectInput(blackbox->getXDisplay(), client.window,
395                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
396   XUngrabServer(blackbox->getXDisplay());
397
398   XRaiseWindow(blackbox->getXDisplay(), frame.plate);
399   XMapSubwindows(blackbox->getXDisplay(), frame.plate);
400
401
402 #ifdef    SHAPE
403   if (blackbox->hasShapeExtensions()) {
404     XShapeSelectInput(blackbox->getXDisplay(), client.window,
405                       ShapeNotifyMask);
406
407     Bool shaped = False;
408     int foo;
409     unsigned int ufoo;
410
411     XShapeQueryExtents(blackbox->getXDisplay(), client.window, &shaped,
412                        &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo,
413                        &ufoo, &ufoo);
414     flags.shaped = shaped;
415   }
416 #endif // SHAPE
417 }
418
419
420 void BlackboxWindow::decorate(void) {
421   BTexture* texture;
422
423   texture = &(screen->getWindowStyle()->b_focus);
424   frame.fbutton = texture->render(frame.button_w, frame.button_w,
425                                   frame.fbutton);
426   if (! frame.fbutton)
427     frame.fbutton_pixel = texture->color().pixel();
428
429   texture = &(screen->getWindowStyle()->b_unfocus);
430   frame.ubutton = texture->render(frame.button_w, frame.button_w,
431                                   frame.ubutton);
432   if (! frame.ubutton)
433     frame.ubutton_pixel = texture->color().pixel();
434
435   texture = &(screen->getWindowStyle()->b_pressed);
436   frame.pbutton = texture->render(frame.button_w, frame.button_w,
437                                   frame.pbutton);
438   if (! frame.pbutton)
439     frame.pbutton_pixel = texture->color().pixel();
440
441   if (decorations & Decor_Titlebar) {
442     texture = &(screen->getWindowStyle()->t_focus);
443     frame.ftitle = texture->render(frame.inside_w, frame.title_h,
444                                    frame.ftitle);
445     if (! frame.ftitle)
446       frame.ftitle_pixel = texture->color().pixel();
447
448     texture = &(screen->getWindowStyle()->t_unfocus);
449     frame.utitle = texture->render(frame.inside_w, frame.title_h,
450                                    frame.utitle);
451     if (! frame.utitle)
452       frame.utitle_pixel = texture->color().pixel();
453
454     XSetWindowBorder(blackbox->getXDisplay(), frame.title,
455                      screen->getBorderColor()->pixel());
456
457     decorateLabel();
458   }
459
460   if (decorations & Decor_Border) {
461     frame.fborder_pixel = screen->getWindowStyle()->f_focus.pixel();
462     frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.pixel();
463     blackbox_attrib.flags |= AttribDecoration;
464     blackbox_attrib.decoration = DecorNormal;
465   } else {
466     blackbox_attrib.flags |= AttribDecoration;
467     blackbox_attrib.decoration = DecorNone;
468   }
469
470   if (decorations & Decor_Handle) {
471     texture = &(screen->getWindowStyle()->h_focus);
472     frame.fhandle = texture->render(frame.inside_w, frame.handle_h,
473                                     frame.fhandle);
474     if (! frame.fhandle)
475       frame.fhandle_pixel = texture->color().pixel();
476
477     texture = &(screen->getWindowStyle()->h_unfocus);
478     frame.uhandle = texture->render(frame.inside_w, frame.handle_h,
479                                     frame.uhandle);
480     if (! frame.uhandle)
481       frame.uhandle_pixel = texture->color().pixel();
482
483     texture = &(screen->getWindowStyle()->g_focus);
484     frame.fgrip = texture->render(frame.grip_w, frame.handle_h, frame.fgrip);
485     if (! frame.fgrip)
486       frame.fgrip_pixel = texture->color().pixel();
487
488     texture = &(screen->getWindowStyle()->g_unfocus);
489     frame.ugrip = texture->render(frame.grip_w, frame.handle_h, frame.ugrip);
490     if (! frame.ugrip)
491       frame.ugrip_pixel = texture->color().pixel();
492
493     XSetWindowBorder(blackbox->getXDisplay(), frame.handle,
494                      screen->getBorderColor()->pixel());
495     XSetWindowBorder(blackbox->getXDisplay(), frame.left_grip,
496                      screen->getBorderColor()->pixel());
497     XSetWindowBorder(blackbox->getXDisplay(), frame.right_grip,
498                      screen->getBorderColor()->pixel());
499   }
500
501   XSetWindowBorder(blackbox->getXDisplay(), frame.window,
502                    screen->getBorderColor()->pixel());
503 }
504
505
506 void BlackboxWindow::decorateLabel(void) {
507   BTexture *texture;
508
509   texture = &(screen->getWindowStyle()->l_focus);
510   frame.flabel = texture->render(frame.label_w, frame.label_h, frame.flabel);
511   if (! frame.flabel)
512     frame.flabel_pixel = texture->color().pixel();
513
514   texture = &(screen->getWindowStyle()->l_unfocus);
515   frame.ulabel = texture->render(frame.label_w, frame.label_h, frame.ulabel);
516   if (! frame.ulabel)
517     frame.ulabel_pixel = texture->color().pixel();
518 }
519
520
521 void BlackboxWindow::createHandle(void) {
522   frame.handle = createChildWindow(frame.window);
523   blackbox->saveWindowSearch(frame.handle, this);
524
525   frame.left_grip =
526     createChildWindow(frame.handle, blackbox->getLowerLeftAngleCursor());
527   blackbox->saveWindowSearch(frame.left_grip, this);
528
529   frame.right_grip =
530     createChildWindow(frame.handle, blackbox->getLowerRightAngleCursor());
531   blackbox->saveWindowSearch(frame.right_grip, this);
532 }
533
534
535 void BlackboxWindow::destroyHandle(void) {
536   if (frame.fhandle)
537     screen->getImageControl()->removeImage(frame.fhandle);
538
539   if (frame.uhandle)
540     screen->getImageControl()->removeImage(frame.uhandle);
541
542   if (frame.fgrip)
543     screen->getImageControl()->removeImage(frame.fgrip);
544
545   if (frame.ugrip)
546     screen->getImageControl()->removeImage(frame.ugrip);
547
548   blackbox->removeWindowSearch(frame.left_grip);
549   blackbox->removeWindowSearch(frame.right_grip);
550
551   XDestroyWindow(blackbox->getXDisplay(), frame.left_grip);
552   XDestroyWindow(blackbox->getXDisplay(), frame.right_grip);
553   frame.left_grip = frame.right_grip = None;
554
555   blackbox->removeWindowSearch(frame.handle);
556   XDestroyWindow(blackbox->getXDisplay(), frame.handle);
557   frame.handle = None;
558 }
559
560
561 void BlackboxWindow::createTitlebar(void) {
562   frame.title = createChildWindow(frame.window);
563   frame.label = createChildWindow(frame.title);
564   blackbox->saveWindowSearch(frame.title, this);
565   blackbox->saveWindowSearch(frame.label, this);
566
567   if (decorations & Decor_Iconify) createIconifyButton();
568   if (decorations & Decor_Maximize) createMaximizeButton();
569   if (decorations & Decor_Close) createCloseButton();
570 }
571
572
573 void BlackboxWindow::destroyTitlebar(void) {
574   if (frame.close_button)
575     destroyCloseButton();
576
577   if (frame.iconify_button)
578     destroyIconifyButton();
579
580   if (frame.maximize_button)
581     destroyMaximizeButton();
582
583   if (frame.ftitle)
584     screen->getImageControl()->removeImage(frame.ftitle);
585
586   if (frame.utitle)
587     screen->getImageControl()->removeImage(frame.utitle);
588
589   if (frame.flabel)
590     screen->getImageControl()->removeImage(frame.flabel);
591
592   if( frame.ulabel)
593     screen->getImageControl()->removeImage(frame.ulabel);
594
595   if (frame.fbutton)
596     screen->getImageControl()->removeImage(frame.fbutton);
597
598   if (frame.ubutton)
599     screen->getImageControl()->removeImage(frame.ubutton);
600
601   if (frame.pbutton)
602     screen->getImageControl()->removeImage(frame.pbutton);
603
604   blackbox->removeWindowSearch(frame.title);
605   blackbox->removeWindowSearch(frame.label);
606
607   XDestroyWindow(blackbox->getXDisplay(), frame.label);
608   XDestroyWindow(blackbox->getXDisplay(), frame.title);
609   frame.title = frame.label = None;
610 }
611
612
613 void BlackboxWindow::createCloseButton(void) {
614   if (frame.title != None) {
615     frame.close_button = createChildWindow(frame.title);
616     blackbox->saveWindowSearch(frame.close_button, this);
617   }
618 }
619
620
621 void BlackboxWindow::destroyCloseButton(void) {
622   blackbox->removeWindowSearch(frame.close_button);
623   XDestroyWindow(blackbox->getXDisplay(), frame.close_button);
624   frame.close_button = None;
625 }
626
627
628 void BlackboxWindow::createIconifyButton(void) {
629   if (frame.title != None) {
630     frame.iconify_button = createChildWindow(frame.title);
631     blackbox->saveWindowSearch(frame.iconify_button, this);
632   }
633 }
634
635
636 void BlackboxWindow::destroyIconifyButton(void) {
637   blackbox->removeWindowSearch(frame.iconify_button);
638   XDestroyWindow(blackbox->getXDisplay(), frame.iconify_button);
639   frame.iconify_button = None;
640 }
641
642
643 void BlackboxWindow::createMaximizeButton(void) {
644   if (frame.title != None) {
645     frame.maximize_button = createChildWindow(frame.title);
646     blackbox->saveWindowSearch(frame.maximize_button, this);
647   }
648 }
649
650
651 void BlackboxWindow::destroyMaximizeButton(void) {
652   blackbox->removeWindowSearch(frame.maximize_button);
653   XDestroyWindow(blackbox->getXDisplay(), frame.maximize_button);
654   frame.maximize_button = None;
655 }
656
657
658 void BlackboxWindow::positionButtons(bool redecorate_label) {
659   string layout = blackbox->getTitlebarLayout();
660   string parsed;
661
662   bool hasclose, hasiconify, hasmaximize, haslabel;
663   hasclose = hasiconify = hasmaximize = haslabel = false;
664
665   string::const_iterator it, end;
666   for (it = layout.begin(), end = layout.end(); it != end; ++it) {
667     switch(*it) {
668     case 'C':
669       if (! hasclose && (decorations & Decor_Close)) {
670         hasclose = true;
671         parsed += *it;
672       }
673       break;
674     case 'I':
675       if (! hasiconify && (decorations & Decor_Iconify)) {
676         hasiconify = true;
677         parsed += *it;
678       }
679       break;
680     case 'M':
681       if (! hasmaximize && (decorations & Decor_Maximize)) {
682         hasmaximize = true;
683         parsed += *it;
684       }
685       break;
686     case 'L':
687       if (! haslabel) {
688         haslabel = true;
689         parsed += *it;
690       }
691     }
692   }
693   if (! hasclose && frame.close_button)
694     destroyCloseButton();
695   if (! hasiconify && frame.iconify_button)
696     destroyIconifyButton();
697   if (! hasmaximize && frame.maximize_button)
698     destroyMaximizeButton();
699   if (! haslabel)
700     parsed += 'L';      // require that the label be in the layout
701
702   const unsigned int bsep = frame.bevel_w + 1;  // separation between elements
703   const unsigned int by = frame.bevel_w + 1;
704   const unsigned int ty = frame.bevel_w;
705
706   frame.label_w = frame.inside_w - bsep * 2 -
707     (frame.button_w + bsep) * (parsed.size() - 1);
708
709   unsigned int x = bsep;
710   for (it = parsed.begin(), end = parsed.end(); it != end; ++it) {
711     switch(*it) {
712     case 'C':
713       if (!frame.close_button) createCloseButton();
714       XMoveResizeWindow(blackbox->getXDisplay(), frame.close_button, x, by,
715                         frame.button_w, frame.button_w);
716       x += frame.button_w + bsep;
717       break;
718     case 'I':
719       if (!frame.iconify_button) createIconifyButton();
720       XMoveResizeWindow(blackbox->getXDisplay(), frame.iconify_button, x, by,
721                         frame.button_w, frame.button_w);
722       x += frame.button_w + bsep;
723       break;
724     case 'M':
725       if (!frame.maximize_button) createMaximizeButton();
726       XMoveResizeWindow(blackbox->getXDisplay(), frame.maximize_button, x, by,
727                         frame.button_w, frame.button_w);
728       x += frame.button_w + bsep;
729       break;
730     case 'L':
731       XMoveResizeWindow(blackbox->getXDisplay(), frame.label, x, ty,
732                         frame.label_w, frame.label_h);
733       x += frame.label_w + bsep;
734       break;
735     }
736   }
737
738   if (redecorate_label) decorateLabel();
739   redrawLabel();
740   redrawAllButtons();
741 }
742
743
744 void BlackboxWindow::reconfigure(void) {
745   upsize();
746
747   client.rect.setPos(frame.rect.left() + frame.margin.left,
748                      frame.rect.top() + frame.margin.top);
749
750   positionWindows();
751   decorate();
752
753   XClearWindow(blackbox->getXDisplay(), frame.window);
754   setFocusFlag(flags.focused);
755
756   configure(frame.rect.x(), frame.rect.y(),
757             frame.rect.width(), frame.rect.height());
758
759   if (windowmenu) {
760     windowmenu->move(windowmenu->getX(), frame.rect.y() + frame.title_h);
761     windowmenu->reconfigure();
762   }
763 }
764
765
766 void BlackboxWindow::updateFocusModel(void) {
767   if ((! screen->isSloppyFocus()) || screen->doClickRaise()) {
768     // grab button 1 for changing focus/raising
769     blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
770                          GrabModeSync, GrabModeSync, None, None);
771   } else {
772     blackbox->ungrabButton(Button1, 0, frame.plate);
773   }
774 }
775
776
777 void BlackboxWindow::positionWindows(void) {
778   XMoveResizeWindow(blackbox->getXDisplay(), frame.window,
779                     frame.rect.x(), frame.rect.y(), frame.inside_w,
780                     (flags.shaded) ? frame.title_h : frame.inside_h);
781   XSetWindowBorderWidth(blackbox->getXDisplay(), frame.window, frame.border_w);
782   XSetWindowBorderWidth(blackbox->getXDisplay(), frame.plate,
783                         frame.mwm_border_w);
784   XMoveResizeWindow(blackbox->getXDisplay(), frame.plate,
785                     frame.margin.left - frame.mwm_border_w - frame.border_w,
786                     frame.margin.top - frame.mwm_border_w - frame.border_w,
787                     client.rect.width(), client.rect.height());
788   XMoveResizeWindow(blackbox->getXDisplay(), client.window,
789                     0, 0, client.rect.width(), client.rect.height());
790
791   if (decorations & Decor_Titlebar) {
792     if (frame.title == None) createTitlebar();
793
794     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.title,
795                           frame.border_w);
796     XMoveResizeWindow(blackbox->getXDisplay(), frame.title, -frame.border_w,
797                       -frame.border_w, frame.inside_w, frame.title_h);
798
799     positionButtons();
800     XMapSubwindows(blackbox->getXDisplay(), frame.title);
801     XMapWindow(blackbox->getXDisplay(), frame.title);
802   } else if (frame.title) {
803     destroyTitlebar();
804   }
805   if (decorations & Decor_Handle) {
806     if (frame.handle == None) createHandle();
807     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.handle,
808                           frame.border_w);
809     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.left_grip,
810                           frame.border_w);
811     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.right_grip,
812                           frame.border_w);
813
814     // use client.rect here so the value is correct even if shaded
815     XMoveResizeWindow(blackbox->getXDisplay(), frame.handle,
816                       -frame.border_w,
817                       client.rect.height() + frame.margin.top +
818                       frame.mwm_border_w - frame.border_w,
819                       frame.inside_w, frame.handle_h);
820     XMoveResizeWindow(blackbox->getXDisplay(), frame.left_grip,
821                       -frame.border_w, -frame.border_w,
822                       frame.grip_w, frame.handle_h);
823     XMoveResizeWindow(blackbox->getXDisplay(), frame.right_grip,
824                       frame.inside_w - frame.grip_w - frame.border_w,
825                       -frame.border_w, frame.grip_w, frame.handle_h);
826
827     XMapSubwindows(blackbox->getXDisplay(), frame.handle);
828     XMapWindow(blackbox->getXDisplay(), frame.handle);
829   } else if (frame.handle) {
830     destroyHandle();
831   }
832 }
833
834
835 void BlackboxWindow::getWMName(void) {
836   XTextProperty text_prop;
837
838   if (XGetWMName(blackbox->getXDisplay(), client.window, &text_prop)) {
839     client.title = textPropertyToString(blackbox->getXDisplay(), text_prop);
840     if (client.title.empty())
841       client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
842     XFree((char *) text_prop.value);
843   } else {
844     client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
845   }
846 }
847
848
849 void BlackboxWindow::getWMIconName(void) {
850   XTextProperty text_prop;
851
852   if (XGetWMIconName(blackbox->getXDisplay(), client.window, &text_prop)) {
853     client.icon_title =
854       textPropertyToString(blackbox->getXDisplay(), text_prop);
855     if (client.icon_title.empty())
856       client.icon_title = client.title;
857     XFree((char *) text_prop.value);
858   } else {
859     client.icon_title = client.title;
860   }
861 }
862
863
864 /*
865  * Retrieve which WM Protocols are supported by the client window.
866  * If the WM_DELETE_WINDOW protocol is supported, add the close button to the
867  * window's decorations and allow the close behavior.
868  * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates
869  * this.
870  */
871 void BlackboxWindow::getWMProtocols(void) {
872   Atom *proto;
873   int num_return = 0;
874
875   if (XGetWMProtocols(blackbox->getXDisplay(), client.window,
876                       &proto, &num_return)) {
877     for (int i = 0; i < num_return; ++i) {
878       if (proto[i] == blackbox->getWMDeleteAtom()) {
879         decorations |= Decor_Close;
880         functions |= Func_Close;
881       } else if (proto[i] == blackbox->getWMTakeFocusAtom())
882         flags.send_focus_message = True;
883       else if (proto[i] == blackbox->getBlackboxStructureMessagesAtom())
884         screen->addNetizen(new Netizen(screen, client.window));
885     }
886
887     XFree(proto);
888   }
889 }
890
891
892 /*
893  * Gets the value of the WM_HINTS property.
894  * If the property is not set, then use a set of default values.
895  */
896 void BlackboxWindow::getWMHints(void) {
897   focus_mode = F_Passive;
898   client.initial_state = NormalState;
899
900   // remove from current window group
901   if (client.window_group) {
902     BWindowGroup *group = blackbox->searchGroup(client.window_group);
903     if (group) group->removeWindow(this);
904   }
905   client.window_group = None;
906
907   XWMHints *wmhint = XGetWMHints(blackbox->getXDisplay(), client.window);
908   if (! wmhint) {
909     return;
910   }
911
912   if (wmhint->flags & InputHint) {
913     if (wmhint->input == True) {
914       if (flags.send_focus_message)
915         focus_mode = F_LocallyActive;
916     } else {
917       if (flags.send_focus_message)
918         focus_mode = F_GloballyActive;
919       else
920         focus_mode = F_NoInput;
921     }
922   }
923
924   if (wmhint->flags & StateHint)
925     client.initial_state = wmhint->initial_state;
926
927   if (wmhint->flags & WindowGroupHint) {
928     client.window_group = wmhint->window_group;
929
930     // add window to the appropriate group
931     BWindowGroup *group = blackbox->searchGroup(client.window_group);
932     if (! group) // no group found, create it!
933       group = new BWindowGroup(blackbox, client.window_group);
934     group->addWindow(this);
935   }
936
937   client.wm_hint_flags = wmhint->flags;
938   XFree(wmhint);
939 }
940
941
942 /*
943  * Gets the value of the WM_NORMAL_HINTS property.
944  * If the property is not set, then use a set of default values.
945  */
946 void BlackboxWindow::getWMNormalHints(void) {
947   long icccm_mask;
948   XSizeHints sizehint;
949
950   client.min_width = client.min_height =
951     client.width_inc = client.height_inc = 1;
952   client.base_width = client.base_height = 0;
953
954   /*
955     use the full screen, not the strut modified size. otherwise when the
956     availableArea changes max_width/height will be incorrect and lead to odd
957     rendering bugs.
958   */
959   const Rect& screen_area = screen->getRect();
960   client.max_width = screen_area.width();
961
962   client.max_height = screen_area.height();
963   client.min_aspect_x = client.min_aspect_y =
964     client.max_aspect_x = client.max_aspect_y = 1;
965   client.win_gravity = NorthWestGravity;
966
967   if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window,
968                           &sizehint, &icccm_mask))
969     return;
970
971   client.normal_hint_flags = sizehint.flags;
972
973   if (sizehint.flags & PMinSize) {
974     client.min_width = sizehint.min_width;
975     client.min_height = sizehint.min_height;
976   }
977
978   if (sizehint.flags & PMaxSize) {
979     client.max_width = sizehint.max_width;
980     client.max_height = sizehint.max_height;
981   }
982
983   if (sizehint.flags & PResizeInc) {
984     client.width_inc = sizehint.width_inc;
985     client.height_inc = sizehint.height_inc;
986   }
987
988   if (sizehint.flags & PAspect) {
989     client.min_aspect_x = sizehint.min_aspect.x;
990     client.min_aspect_y = sizehint.min_aspect.y;
991     client.max_aspect_x = sizehint.max_aspect.x;
992     client.max_aspect_y = sizehint.max_aspect.y;
993   }
994
995   if (sizehint.flags & PBaseSize) {
996     client.base_width = sizehint.base_width;
997     client.base_height = sizehint.base_height;
998   }
999
1000   if (sizehint.flags & PWinGravity)
1001     client.win_gravity = sizehint.win_gravity;
1002 }
1003
1004
1005 /*
1006  * Gets the MWM hints for the class' contained window.
1007  * This is used while initializing the window to its first state, and not
1008  * thereafter.
1009  * Returns: true if the MWM hints are successfully retreived and applied;
1010  * false if they are not.
1011  */
1012 void BlackboxWindow::getMWMHints(void) {
1013   int format;
1014   Atom atom_return;
1015   unsigned long num, len;
1016   MwmHints *mwm_hint = 0;
1017
1018   int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window,
1019                                blackbox->getMotifWMHintsAtom(), 0,
1020                                PropMwmHintsElements, False,
1021                                blackbox->getMotifWMHintsAtom(), &atom_return,
1022                                &format, &num, &len,
1023                                (unsigned char **) &mwm_hint);
1024
1025   if (ret != Success || ! mwm_hint || num != PropMwmHintsElements)
1026     return;
1027
1028   if (mwm_hint->flags & MwmHintsDecorations) {
1029     if (mwm_hint->decorations & MwmDecorAll) {
1030       decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
1031                     Decor_Iconify | Decor_Maximize | Decor_Close;
1032     } else {
1033       decorations = 0;
1034
1035       if (mwm_hint->decorations & MwmDecorBorder)
1036         decorations |= Decor_Border;
1037       if (mwm_hint->decorations & MwmDecorHandle)
1038         decorations |= Decor_Handle;
1039       if (mwm_hint->decorations & MwmDecorTitle)
1040         decorations |= Decor_Titlebar;
1041       if (mwm_hint->decorations & MwmDecorIconify)
1042         decorations |= Decor_Iconify;
1043       if (mwm_hint->decorations & MwmDecorMaximize)
1044         decorations |= Decor_Maximize;
1045     }
1046   }
1047
1048   if (mwm_hint->flags & MwmHintsFunctions) {
1049     if (mwm_hint->functions & MwmFuncAll) {
1050       functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
1051                   Func_Close;
1052     } else {
1053       functions = 0;
1054
1055       if (mwm_hint->functions & MwmFuncResize)
1056         functions |= Func_Resize;
1057       if (mwm_hint->functions & MwmFuncMove)
1058         functions |= Func_Move;
1059       if (mwm_hint->functions & MwmFuncIconify)
1060         functions |= Func_Iconify;
1061       if (mwm_hint->functions & MwmFuncMaximize)
1062         functions |= Func_Maximize;
1063       if (mwm_hint->functions & MwmFuncClose)
1064         functions |= Func_Close;
1065     }
1066   }
1067   XFree(mwm_hint);
1068 }
1069
1070
1071 /*
1072  * Gets the blackbox hints from the class' contained window.
1073  * This is used while initializing the window to its first state, and not
1074  * thereafter.
1075  * Returns: true if the hints are successfully retreived and applied; false if
1076  * they are not.
1077  */
1078 bool BlackboxWindow::getBlackboxHints(void) {
1079   int format;
1080   Atom atom_return;
1081   unsigned long num, len;
1082   BlackboxHints *blackbox_hint = 0;
1083
1084   int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window,
1085                                blackbox->getBlackboxHintsAtom(), 0,
1086                                PropBlackboxHintsElements, False,
1087                                blackbox->getBlackboxHintsAtom(), &atom_return,
1088                                &format, &num, &len,
1089                                (unsigned char **) &blackbox_hint);
1090   if (ret != Success || ! blackbox_hint || num != PropBlackboxHintsElements)
1091     return False;
1092
1093   if (blackbox_hint->flags & AttribShaded)
1094     flags.shaded = (blackbox_hint->attrib & AttribShaded);
1095
1096   if ((blackbox_hint->flags & AttribMaxHoriz) &&
1097       (blackbox_hint->flags & AttribMaxVert))
1098     flags.maximized = (blackbox_hint->attrib &
1099                        (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1100   else if (blackbox_hint->flags & AttribMaxVert)
1101     flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1102   else if (blackbox_hint->flags & AttribMaxHoriz)
1103     flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1104
1105   if (blackbox_hint->flags & AttribOmnipresent)
1106     flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
1107
1108   if (blackbox_hint->flags & AttribWorkspace)
1109     blackbox_attrib.workspace = blackbox_hint->workspace;
1110
1111   // if (blackbox_hint->flags & AttribStack)
1112   //   don't yet have always on top/bottom for blackbox yet... working
1113   //   on that
1114
1115   if (blackbox_hint->flags & AttribDecoration) {
1116     switch (blackbox_hint->decoration) {
1117     case DecorNone:
1118       // clear all decorations except close
1119       decorations &= Decor_Close;
1120       // clear all functions except close
1121       functions &= Func_Close;
1122
1123       break;
1124
1125     case DecorTiny:
1126       decorations |= Decor_Titlebar | Decor_Iconify;
1127       decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
1128       functions |= Func_Move | Func_Iconify;
1129       functions &= ~(Func_Resize | Func_Maximize);
1130
1131       break;
1132
1133     case DecorTool:
1134       decorations |= Decor_Titlebar;
1135       decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
1136       functions |= Func_Move;
1137       functions &= ~(Func_Resize | Func_Maximize | Func_Iconify);
1138
1139       break;
1140
1141     case DecorNormal:
1142     default:
1143       decorations |= Decor_Titlebar | Decor_Border | Decor_Handle |
1144                      Decor_Iconify | Decor_Maximize;
1145       functions |= Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
1146
1147       break;
1148     }
1149
1150     reconfigure();
1151   }
1152   XFree(blackbox_hint);
1153   return True;
1154 }
1155
1156
1157 void BlackboxWindow::getTransientInfo(void) {
1158   if (client.transient_for &&
1159       client.transient_for != (BlackboxWindow *) ~0ul) {
1160     // the transient for hint was removed, so we need to tell our
1161     // previous transient_for that we are going away
1162     client.transient_for->client.transientList.remove(this);
1163   }
1164
1165   // we have no transient_for until we find a new one
1166   client.transient_for = 0;
1167
1168   Window trans_for;
1169   if (! XGetTransientForHint(blackbox->getXDisplay(), client.window,
1170                              &trans_for)) {
1171     // transient_for hint not set
1172     return;
1173   }
1174
1175   if (trans_for == client.window) {
1176     // wierd client... treat this window as a normal window
1177     return;
1178   }
1179
1180   if (trans_for == None || trans_for == screen->getRootWindow()) {
1181     // this is an undocumented interpretation of the ICCCM. a transient
1182     // associated with None/Root/itself is assumed to be a modal root
1183     // transient.  we don't support the concept of a global transient,
1184     // so we just associate this transient with nothing, and perhaps
1185     // we will add support later for global modality.
1186     client.transient_for = (BlackboxWindow *) ~0ul;
1187     flags.modal = True;
1188     return;
1189   }
1190
1191   client.transient_for = blackbox->searchWindow(trans_for);
1192   if (! client.transient_for &&
1193       client.window_group && trans_for == client.window_group) {
1194     // no direct transient_for, perhaps this is a group transient?
1195     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1196     if (group) client.transient_for = group->find(screen);
1197   }
1198
1199   if (! client.transient_for || client.transient_for == this) {
1200     // no transient_for found, or we have a wierd client that wants to be
1201     // a transient for itself, so we treat this window as a normal window
1202     client.transient_for = (BlackboxWindow*) 0;
1203     return;
1204   }
1205
1206   // register ourselves with our new transient_for
1207   client.transient_for->client.transientList.push_back(this);
1208   flags.stuck = client.transient_for->flags.stuck;
1209 }
1210
1211
1212 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1213   if (client.transient_for &&
1214       client.transient_for != (BlackboxWindow*) ~0ul)
1215     return client.transient_for;
1216   return 0;
1217 }
1218
1219
1220 void BlackboxWindow::configure(int dx, int dy,
1221                                unsigned int dw, unsigned int dh) {
1222   bool send_event = (frame.rect.x() != dx || frame.rect.y() != dy);
1223
1224   if ((dw != frame.rect.width()) || (dh != frame.rect.height())) {
1225     frame.rect.setRect(dx, dy, dw, dh);
1226     frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1227     frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1228
1229     if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1230       frame.rect.setPos(0, 0);
1231
1232     client.rect.setCoords(frame.rect.left() + frame.margin.left,
1233                           frame.rect.top() + frame.margin.top,
1234                           frame.rect.right() - frame.margin.right,
1235                           frame.rect.bottom() - frame.margin.bottom);
1236
1237 #ifdef    SHAPE
1238     if (blackbox->hasShapeExtensions() && flags.shaped) {
1239       configureShape();
1240     }
1241 #endif // SHAPE
1242
1243     positionWindows();
1244     decorate();
1245     setFocusFlag(flags.focused);
1246     redrawAllButtons();
1247   } else {
1248     frame.rect.setPos(dx, dy);
1249
1250     XMoveWindow(blackbox->getXDisplay(), frame.window,
1251                 frame.rect.x(), frame.rect.y());
1252
1253     if (! flags.moving) send_event = True;
1254   }
1255
1256   if (send_event && ! flags.moving) {
1257     client.rect.setPos(frame.rect.left() + frame.margin.left,
1258                        frame.rect.top() + frame.margin.top);
1259
1260     XEvent event;
1261     event.type = ConfigureNotify;
1262
1263     event.xconfigure.display = blackbox->getXDisplay();
1264     event.xconfigure.event = client.window;
1265     event.xconfigure.window = client.window;
1266     event.xconfigure.x = client.rect.x();
1267     event.xconfigure.y = client.rect.y();
1268     event.xconfigure.width = client.rect.width();
1269     event.xconfigure.height = client.rect.height();
1270     event.xconfigure.border_width = client.old_bw;
1271     event.xconfigure.above = frame.window;
1272     event.xconfigure.override_redirect = False;
1273
1274     XSendEvent(blackbox->getXDisplay(), client.window, True,
1275                NoEventMask, &event);
1276
1277     screen->updateNetizenConfigNotify(&event);
1278   }
1279 }
1280
1281
1282 #ifdef SHAPE
1283 void BlackboxWindow::configureShape(void) {
1284   XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1285                      frame.margin.left - frame.border_w,
1286                      frame.margin.top - frame.border_w,
1287                      client.window, ShapeBounding, ShapeSet);
1288
1289   int num = 0;
1290   XRectangle xrect[2];
1291
1292   if (decorations & Decor_Titlebar) {
1293     xrect[0].x = xrect[0].y = -frame.border_w;
1294     xrect[0].width = frame.rect.width();
1295     xrect[0].height = frame.title_h + (frame.border_w * 2);
1296     ++num;
1297   }
1298
1299   if (decorations & Decor_Handle) {
1300     xrect[1].x = -frame.border_w;
1301     xrect[1].y = frame.rect.height() - frame.margin.bottom +
1302                  frame.mwm_border_w - frame.border_w;
1303     xrect[1].width = frame.rect.width();
1304     xrect[1].height = frame.handle_h + (frame.border_w * 2);
1305     ++num;
1306   }
1307
1308   XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1309                           ShapeBounding, 0, 0, xrect, num,
1310                           ShapeUnion, Unsorted);
1311 }
1312 #endif // SHAPE
1313
1314
1315 bool BlackboxWindow::setInputFocus(void) {
1316   if (flags.focused) return True;
1317
1318   if (! client.rect.intersects(screen->getRect())) {
1319     // client is outside the screen, move it to the center
1320     configure((screen->getWidth() - frame.rect.width()) / 2,
1321               (screen->getHeight() - frame.rect.height()) / 2,
1322               frame.rect.width(), frame.rect.height());
1323   }
1324
1325   if (client.transientList.size() > 0) {
1326     // transfer focus to any modal transients
1327     BlackboxWindowList::iterator it, end = client.transientList.end();
1328     for (it = client.transientList.begin(); it != end; ++it) {
1329       if ((*it)->flags.modal) return (*it)->setInputFocus();
1330     }
1331   }
1332
1333   bool ret = True;
1334   if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1335     XSetInputFocus(blackbox->getXDisplay(), client.window,
1336                    RevertToPointerRoot, CurrentTime);
1337
1338     blackbox->setFocusedWindow(this);
1339   } else {
1340     /* we could set the focus to none, since the window doesn't accept focus,
1341      * but we shouldn't set focus to nothing since this would surely make
1342      * someone angry
1343      */
1344     ret = False;
1345   }
1346
1347   if (flags.send_focus_message) {
1348     XEvent ce;
1349     ce.xclient.type = ClientMessage;
1350     ce.xclient.message_type = blackbox->getWMProtocolsAtom();
1351     ce.xclient.display = blackbox->getXDisplay();
1352     ce.xclient.window = client.window;
1353     ce.xclient.format = 32;
1354     ce.xclient.data.l[0] = blackbox->getWMTakeFocusAtom();
1355     ce.xclient.data.l[1] = blackbox->getLastTime();
1356     ce.xclient.data.l[2] = 0l;
1357     ce.xclient.data.l[3] = 0l;
1358     ce.xclient.data.l[4] = 0l;
1359     XSendEvent(blackbox->getXDisplay(), client.window, False,
1360                NoEventMask, &ce);
1361   }
1362
1363   return ret;
1364 }
1365
1366
1367 void BlackboxWindow::iconify(void) {
1368   if (flags.iconic) return;
1369
1370   if (windowmenu) windowmenu->hide();
1371
1372   setState(IconicState);
1373
1374   /*
1375    * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1376    * we need to clear the event mask on client.window for a split second.
1377    * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1378    * split second, leaving us with a ghost window... so, we need to do this
1379    * while the X server is grabbed
1380    */
1381   XGrabServer(blackbox->getXDisplay());
1382   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
1383   XUnmapWindow(blackbox->getXDisplay(), client.window);
1384   XSelectInput(blackbox->getXDisplay(), client.window,
1385                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
1386   XUngrabServer(blackbox->getXDisplay());
1387
1388   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1389   flags.visible = False;
1390   flags.iconic = True;
1391
1392   screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1393
1394   if (isTransient()) {
1395     if (client.transient_for != (BlackboxWindow *) ~0ul &&
1396         ! client.transient_for->flags.iconic) {
1397       // iconify our transient_for
1398       client.transient_for->iconify();
1399     }
1400   }
1401
1402   screen->addIcon(this);
1403
1404   if (client.transientList.size() > 0) {
1405     // iconify all transients
1406     BlackboxWindowList::iterator it, end = client.transientList.end();
1407     for (it = client.transientList.begin(); it != end; ++it) {
1408       if (! (*it)->flags.iconic) (*it)->iconify();
1409     }
1410   }
1411 }
1412
1413
1414 void BlackboxWindow::show(void) {
1415   setState(NormalState);
1416
1417   XMapWindow(blackbox->getXDisplay(), client.window);
1418   XMapSubwindows(blackbox->getXDisplay(), frame.window);
1419   XMapWindow(blackbox->getXDisplay(), frame.window);
1420
1421   flags.visible = True;
1422   flags.iconic = False;
1423 }
1424
1425
1426 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1427   if (flags.iconic || reassoc)
1428     screen->reassociateWindow(this, BSENTINEL, False);
1429   else if (blackbox_attrib.workspace != screen->getCurrentWorkspace()->getID())
1430     return;
1431
1432   show();
1433
1434   // reassociate and deiconify all transients
1435   if (reassoc && client.transientList.size() > 0) {
1436     BlackboxWindowList::iterator it, end = client.transientList.end();
1437     for (it = client.transientList.begin(); it != end; ++it) {
1438       (*it)->deiconify(True, False);
1439     }
1440   }
1441
1442   if (raise)
1443     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1444 }
1445
1446
1447 void BlackboxWindow::close(void) {
1448   XEvent ce;
1449   ce.xclient.type = ClientMessage;
1450   ce.xclient.message_type = blackbox->getWMProtocolsAtom();
1451   ce.xclient.display = blackbox->getXDisplay();
1452   ce.xclient.window = client.window;
1453   ce.xclient.format = 32;
1454   ce.xclient.data.l[0] = blackbox->getWMDeleteAtom();
1455   ce.xclient.data.l[1] = CurrentTime;
1456   ce.xclient.data.l[2] = 0l;
1457   ce.xclient.data.l[3] = 0l;
1458   ce.xclient.data.l[4] = 0l;
1459   XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1460 }
1461
1462
1463 void BlackboxWindow::withdraw(void) {
1464   setState(current_state);
1465
1466   flags.visible = False;
1467   flags.iconic = False;
1468
1469   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1470
1471   XGrabServer(blackbox->getXDisplay());
1472   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
1473   XUnmapWindow(blackbox->getXDisplay(), client.window);
1474   XSelectInput(blackbox->getXDisplay(), client.window,
1475                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
1476     XUngrabServer(blackbox->getXDisplay());
1477
1478   if (windowmenu) windowmenu->hide();
1479 }
1480
1481
1482 void BlackboxWindow::maximize(unsigned int button) {
1483   // handle case where menu is open then the max button is used instead
1484   if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1485
1486   if (flags.maximized) {
1487     flags.maximized = 0;
1488
1489     blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1490     blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1491
1492     /*
1493       when a resize is begun, maximize(0) is called to clear any maximization
1494       flags currently set.  Otherwise it still thinks it is maximized.
1495       so we do not need to call configure() because resizing will handle it
1496     */
1497     if (! flags.resizing)
1498       configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1499                 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1500
1501     blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1502     blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1503
1504     redrawAllButtons();
1505     setState(current_state);
1506     return;
1507   }
1508
1509   blackbox_attrib.premax_x = frame.rect.x();
1510   blackbox_attrib.premax_y = frame.rect.y();
1511   blackbox_attrib.premax_w = frame.rect.width();
1512   // use client.rect so that clients can be restored even if shaded
1513   blackbox_attrib.premax_h =
1514     client.rect.height() + frame.margin.top + frame.margin.bottom;
1515
1516   const Rect &screen_area = screen->availableArea();
1517   frame.changing = screen_area;
1518   constrain(TopLeft);
1519
1520   switch(button) {
1521   case 1:
1522     blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1523     blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1524     break;
1525
1526   case 2:
1527     blackbox_attrib.flags |= AttribMaxVert;
1528     blackbox_attrib.attrib |= AttribMaxVert;
1529
1530     frame.changing.setX(frame.rect.x());
1531     frame.changing.setWidth(frame.rect.width());
1532     break;
1533
1534   case 3:
1535     blackbox_attrib.flags |= AttribMaxHoriz;
1536     blackbox_attrib.attrib |= AttribMaxHoriz;
1537
1538     frame.changing.setY(frame.rect.y());
1539     frame.changing.setHeight(frame.rect.height());
1540     break;
1541   }
1542
1543   if (flags.shaded) {
1544     blackbox_attrib.flags ^= AttribShaded;
1545     blackbox_attrib.attrib ^= AttribShaded;
1546     flags.shaded = False;
1547   }
1548
1549   flags.maximized = button;
1550
1551   configure(frame.changing.x(), frame.changing.y(),
1552             frame.changing.width(), frame.changing.height());
1553   if (flags.focused)
1554     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1555   redrawAllButtons();
1556   setState(current_state);
1557 }
1558
1559
1560 // re-maximizes the window to take into account availableArea changes
1561 void BlackboxWindow::remaximize(void) {
1562   // save the original dimensions because maximize will wipe them out
1563   int premax_x = blackbox_attrib.premax_x,
1564     premax_y = blackbox_attrib.premax_y,
1565     premax_w = blackbox_attrib.premax_w,
1566     premax_h = blackbox_attrib.premax_h;
1567
1568   unsigned int button = flags.maximized;
1569   flags.maximized = 0; // trick maximize() into working
1570   maximize(button);
1571
1572   // restore saved values
1573   blackbox_attrib.premax_x = premax_x;
1574   blackbox_attrib.premax_y = premax_y;
1575   blackbox_attrib.premax_w = premax_w;
1576   blackbox_attrib.premax_h = premax_h;
1577 }
1578
1579
1580 void BlackboxWindow::setWorkspace(unsigned int n) {
1581   blackbox_attrib.flags |= AttribWorkspace;
1582   blackbox_attrib.workspace = n;
1583 }
1584
1585
1586 void BlackboxWindow::shade(void) {
1587   if (flags.shaded) {
1588     XResizeWindow(blackbox->getXDisplay(), frame.window,
1589                   frame.inside_w, frame.inside_h);
1590     flags.shaded = False;
1591     blackbox_attrib.flags ^= AttribShaded;
1592     blackbox_attrib.attrib ^= AttribShaded;
1593
1594     setState(NormalState);
1595
1596     // set the frame rect to the normal size
1597     frame.rect.setHeight(client.rect.height() + frame.margin.top +
1598                          frame.margin.bottom);
1599   } else {
1600     if (! (decorations & Decor_Titlebar))
1601       return;
1602
1603     XResizeWindow(blackbox->getXDisplay(), frame.window,
1604                   frame.inside_w, frame.title_h);
1605     flags.shaded = True;
1606     blackbox_attrib.flags |= AttribShaded;
1607     blackbox_attrib.attrib |= AttribShaded;
1608
1609     setState(IconicState);
1610
1611     // set the frame rect to the shaded size
1612     frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
1613   }
1614 }
1615
1616
1617 /*
1618  * (Un)Sticks a window and its relatives.
1619  */
1620 void BlackboxWindow::stick(void) {
1621   if (flags.stuck) {
1622     blackbox_attrib.flags ^= AttribOmnipresent;
1623     blackbox_attrib.attrib ^= AttribOmnipresent;
1624
1625     flags.stuck = False;
1626
1627     if (! flags.iconic)
1628       screen->reassociateWindow(this, BSENTINEL, True);
1629
1630     setState(current_state);
1631   } else {
1632     flags.stuck = True;
1633
1634     blackbox_attrib.flags |= AttribOmnipresent;
1635     blackbox_attrib.attrib |= AttribOmnipresent;
1636
1637     setState(current_state);
1638   }
1639   // go up the chain
1640   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
1641       client.transient_for->isStuck() != flags.stuck)
1642     client.transient_for->stick();
1643   // go down the chain
1644   BlackboxWindowList::iterator it;
1645   const BlackboxWindowList::iterator end = client.transientList.end();
1646   for (it = client.transientList.begin(); it != end; ++it)
1647     if ((*it)->isStuck() != flags.stuck)
1648       (*it)->stick();
1649 }
1650
1651
1652 void BlackboxWindow::setFocusFlag(bool focus) {
1653   flags.focused = focus;
1654
1655   if (decorations & Decor_Titlebar) {
1656     if (flags.focused) {
1657       if (frame.ftitle)
1658         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1659                                    frame.title, frame.ftitle);
1660       else
1661         XSetWindowBackground(blackbox->getXDisplay(),
1662                              frame.title, frame.ftitle_pixel);
1663     } else {
1664       if (frame.utitle)
1665         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1666                                    frame.title, frame.utitle);
1667       else
1668         XSetWindowBackground(blackbox->getXDisplay(),
1669                              frame.title, frame.utitle_pixel);
1670     }
1671     XClearWindow(blackbox->getXDisplay(), frame.title);
1672
1673     redrawLabel();
1674     redrawAllButtons();
1675   }
1676
1677   if (decorations & Decor_Handle) {
1678     if (flags.focused) {
1679       if (frame.fhandle)
1680         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1681                                    frame.handle, frame.fhandle);
1682       else
1683         XSetWindowBackground(blackbox->getXDisplay(),
1684                              frame.handle, frame.fhandle_pixel);
1685
1686       if (frame.fgrip) {
1687         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1688                                    frame.left_grip, frame.fgrip);
1689         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1690                                    frame.right_grip, frame.fgrip);
1691       } else {
1692         XSetWindowBackground(blackbox->getXDisplay(),
1693                              frame.left_grip, frame.fgrip_pixel);
1694         XSetWindowBackground(blackbox->getXDisplay(),
1695                              frame.right_grip, frame.fgrip_pixel);
1696       }
1697     } else {
1698       if (frame.uhandle)
1699         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1700                                    frame.handle, frame.uhandle);
1701       else
1702         XSetWindowBackground(blackbox->getXDisplay(),
1703                              frame.handle, frame.uhandle_pixel);
1704
1705       if (frame.ugrip) {
1706         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1707                                    frame.left_grip, frame.ugrip);
1708         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1709                                    frame.right_grip, frame.ugrip);
1710       } else {
1711         XSetWindowBackground(blackbox->getXDisplay(),
1712                              frame.left_grip, frame.ugrip_pixel);
1713         XSetWindowBackground(blackbox->getXDisplay(),
1714                              frame.right_grip, frame.ugrip_pixel);
1715       }
1716     }
1717     XClearWindow(blackbox->getXDisplay(), frame.handle);
1718     XClearWindow(blackbox->getXDisplay(), frame.left_grip);
1719     XClearWindow(blackbox->getXDisplay(), frame.right_grip);
1720   }
1721
1722   if (decorations & Decor_Border) {
1723     if (flags.focused)
1724       XSetWindowBorder(blackbox->getXDisplay(),
1725                        frame.plate, frame.fborder_pixel);
1726     else
1727       XSetWindowBorder(blackbox->getXDisplay(),
1728                        frame.plate, frame.uborder_pixel);
1729   }
1730
1731   if (screen->isSloppyFocus() && screen->doAutoRaise()) {
1732     if (isFocused()) timer->start();
1733     else timer->stop();
1734   }
1735
1736   if (isFocused())
1737     blackbox->setFocusedWindow(this);
1738 }
1739
1740
1741 void BlackboxWindow::installColormap(bool install) {
1742   int i = 0, ncmap = 0;
1743   Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
1744                                             client.window, &ncmap);
1745   XWindowAttributes wattrib;
1746   if (cmaps) {
1747     if (XGetWindowAttributes(blackbox->getXDisplay(),
1748                              client.window, &wattrib)) {
1749       if (install) {
1750         // install the window's colormap
1751         for (i = 0; i < ncmap; i++) {
1752           if (*(cmaps + i) == wattrib.colormap)
1753             // this window is using an installed color map... do not install
1754             install = False;
1755         }
1756         // otherwise, install the window's colormap
1757         if (install)
1758           XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
1759       } else {
1760         // uninstall the window's colormap
1761         for (i = 0; i < ncmap; i++) {
1762           if (*(cmaps + i) == wattrib.colormap)
1763             // we found the colormap to uninstall
1764             XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
1765         }
1766       }
1767     }
1768
1769     XFree(cmaps);
1770   }
1771 }
1772
1773
1774 void BlackboxWindow::setState(unsigned long new_state) {
1775   current_state = new_state;
1776
1777   unsigned long state[2];
1778   state[0] = current_state;
1779   state[1] = None;
1780   xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
1781  
1782   xatom->setValue(client.window, XAtom::blackbox_attributes,
1783                   XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
1784                   PropBlackboxAttributesElements);
1785 }
1786
1787
1788 bool BlackboxWindow::getState(void) {
1789   current_state = 0;
1790
1791   Atom atom_return;
1792   bool ret = False;
1793   unsigned long *state, nitems;
1794
1795   if (! xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state, nitems,
1796                         &state))
1797     return False;
1798
1799   current_state = static_cast<unsigned long>(state[0]);
1800   delete state;
1801
1802   return True;
1803 }
1804
1805
1806 void BlackboxWindow::restoreAttributes(void) {
1807   if (! getState()) current_state = NormalState;
1808
1809   Atom atom_return;
1810   int foo;
1811   unsigned long ulfoo, nitems;
1812
1813   BlackboxAttributes *net;
1814   int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window,
1815                                blackbox->getBlackboxAttributesAtom(), 0l,
1816                                PropBlackboxAttributesElements, False,
1817                                blackbox->getBlackboxAttributesAtom(),
1818                                &atom_return, &foo, &nitems, &ulfoo,
1819                                (unsigned char **) &net);
1820   if (ret != Success || ! net || nitems != PropBlackboxAttributesElements)
1821     return;
1822
1823   if (net->flags & AttribShaded &&
1824       net->attrib & AttribShaded) {
1825     int save_state =
1826       ((current_state == IconicState) ? NormalState : current_state);
1827
1828     flags.shaded = False;
1829     shade();
1830
1831     current_state = save_state;
1832   }
1833
1834   if ((net->workspace != screen->getCurrentWorkspaceID()) &&
1835       (net->workspace < screen->getWorkspaceCount())) {
1836     screen->reassociateWindow(this, net->workspace, True);
1837
1838     if (current_state == NormalState) current_state = WithdrawnState;
1839   } else if (current_state == WithdrawnState) {
1840     current_state = NormalState;
1841   }
1842
1843   if (net->flags & AttribOmnipresent &&
1844       net->attrib & AttribOmnipresent) {
1845     flags.stuck = False;
1846     stick();
1847
1848     current_state = NormalState;
1849   }
1850
1851   if ((net->flags & AttribMaxHoriz) ||
1852       (net->flags & AttribMaxVert)) {
1853     int x = net->premax_x, y = net->premax_y;
1854     unsigned int w = net->premax_w, h = net->premax_h;
1855     flags.maximized = 0;
1856
1857     unsigned int m = 0;
1858     if ((net->flags & AttribMaxHoriz) &&
1859         (net->flags & AttribMaxVert))
1860       m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1861     else if (net->flags & AttribMaxVert)
1862       m = (net->attrib & AttribMaxVert) ? 2 : 0;
1863     else if (net->flags & AttribMaxHoriz)
1864       m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
1865
1866     if (m) maximize(m);
1867
1868     blackbox_attrib.premax_x = x;
1869     blackbox_attrib.premax_y = y;
1870     blackbox_attrib.premax_w = w;
1871     blackbox_attrib.premax_h = h;
1872   }
1873
1874   setState(current_state);
1875
1876   XFree((void *) net);
1877 }
1878
1879
1880 /*
1881  * Positions the frame according the the client window position and window
1882  * gravity.
1883  */
1884 void BlackboxWindow::setGravityOffsets(void) {
1885   // x coordinates for each gravity type
1886   const int x_west = client.rect.x();
1887   const int x_east = client.rect.right() - frame.inside_w + 1;
1888   const int x_center = client.rect.left() +
1889     ((client.rect.width() - frame.rect.width()) / 2);
1890   // y coordinates for each gravity type
1891   const int y_north = client.rect.y();
1892   const int y_south = client.rect.bottom() - frame.inside_h + 1;
1893   const int y_center = client.rect.top() +
1894     ((client.rect.height() - frame.rect.height()) / 2);
1895
1896   switch (client.win_gravity) {
1897   default:
1898   case NorthWestGravity: frame.rect.setPos(x_west,   y_north);  break;
1899   case NorthGravity:     frame.rect.setPos(x_center, y_north);  break;
1900   case NorthEastGravity: frame.rect.setPos(x_east,   y_north);  break;
1901   case SouthWestGravity: frame.rect.setPos(x_west,   y_south);  break;
1902   case SouthGravity:     frame.rect.setPos(x_center, y_south);  break;
1903   case SouthEastGravity: frame.rect.setPos(x_east,   y_south);  break;
1904   case WestGravity:      frame.rect.setPos(x_west,   y_center); break;
1905   case CenterGravity:    frame.rect.setPos(x_center, y_center); break;
1906   case EastGravity:      frame.rect.setPos(x_east,   y_center); break;
1907
1908   case ForgetGravity:
1909   case StaticGravity:
1910     frame.rect.setPos(client.rect.x() - frame.margin.left,
1911                       client.rect.y() - frame.margin.top);
1912     break;
1913   }
1914 }
1915
1916
1917 /*
1918  * The reverse of the setGravityOffsets function. Uses the frame window's
1919  * position to find the window's reference point.
1920  */
1921 void BlackboxWindow::restoreGravity(void) {
1922   // x coordinates for each gravity type
1923   const int x_west = frame.rect.x();
1924   const int x_east = frame.rect.x() + frame.inside_w - client.rect.width();
1925   const int x_center = frame.rect.x() -
1926     ((client.rect.width() - frame.rect.width()) / 2);
1927   // y coordinates for each gravity type
1928   const int y_north = frame.rect.y();
1929   const int y_south = frame.rect.y() + frame.inside_h - client.rect.height();
1930   const int y_center = frame.rect.y() -
1931     ((client.rect.height() - frame.rect.height()) / 2);
1932
1933   switch(client.win_gravity) {
1934   default:
1935   case NorthWestGravity: client.rect.setPos(x_west,   y_north);  break;
1936   case NorthGravity:     client.rect.setPos(x_center, y_north);  break;
1937   case NorthEastGravity: client.rect.setPos(x_east,   y_north);  break;
1938   case SouthWestGravity: client.rect.setPos(x_west,   y_south);  break;
1939   case SouthGravity:     client.rect.setPos(x_center, y_south);  break;
1940   case SouthEastGravity: client.rect.setPos(x_east,   y_south);  break;
1941   case WestGravity:      client.rect.setPos(x_west,   y_center); break;
1942   case CenterGravity:    client.rect.setPos(x_center, y_center); break;
1943   case EastGravity:      client.rect.setPos(x_east,   y_center); break;
1944
1945   case ForgetGravity:
1946   case StaticGravity:
1947     client.rect.setPos(frame.rect.left() + frame.margin.left,
1948                        frame.rect.top() + frame.margin.top);
1949     break;
1950   }
1951 }
1952
1953
1954 void BlackboxWindow::redrawLabel(void) {
1955   if (flags.focused) {
1956     if (frame.flabel)
1957       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1958                                  frame.label, frame.flabel);
1959     else
1960       XSetWindowBackground(blackbox->getXDisplay(),
1961                            frame.label, frame.flabel_pixel);
1962   } else {
1963     if (frame.ulabel)
1964       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1965                                  frame.label, frame.ulabel);
1966     else
1967       XSetWindowBackground(blackbox->getXDisplay(),
1968                            frame.label, frame.ulabel_pixel);
1969   }
1970   XClearWindow(blackbox->getXDisplay(), frame.label);
1971
1972   WindowStyle *style = screen->getWindowStyle();
1973
1974   int pos = frame.bevel_w * 2,
1975     dlen = style->doJustify(client.title.c_str(), pos, frame.label_w,
1976                             frame.bevel_w * 4, i18n.multibyte());
1977
1978   BPen pen((flags.focused) ? style->l_text_focus : style->l_text_unfocus,
1979            style->font);
1980   if (i18n.multibyte())
1981     XmbDrawString(blackbox->getXDisplay(), frame.label, style->fontset,
1982                   pen.gc(), pos,
1983                   (1 - style->fontset_extents->max_ink_extent.y),
1984                   client.title.c_str(), dlen);
1985   else
1986     XDrawString(blackbox->getXDisplay(), frame.label, pen.gc(), pos,
1987                 (style->font->ascent + 1), client.title.c_str(), dlen);
1988 }
1989
1990
1991 void BlackboxWindow::redrawAllButtons(void) {
1992   if (frame.iconify_button) redrawIconifyButton(False);
1993   if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
1994   if (frame.close_button) redrawCloseButton(False);
1995 }
1996
1997
1998 void BlackboxWindow::redrawIconifyButton(bool pressed) {
1999   if (! pressed) {
2000     if (flags.focused) {
2001       if (frame.fbutton)
2002         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2003                                    frame.iconify_button, frame.fbutton);
2004       else
2005         XSetWindowBackground(blackbox->getXDisplay(),
2006                              frame.iconify_button, frame.fbutton_pixel);
2007     } else {
2008       if (frame.ubutton)
2009         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2010                                    frame.iconify_button, frame.ubutton);
2011       else
2012         XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2013                              frame.ubutton_pixel);
2014     }
2015   } else {
2016     if (frame.pbutton)
2017       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2018                                  frame.iconify_button, frame.pbutton);
2019     else
2020       XSetWindowBackground(blackbox->getXDisplay(),
2021                            frame.iconify_button, frame.pbutton_pixel);
2022   }
2023   XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2024
2025   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2026            screen->getWindowStyle()->b_pic_unfocus);
2027   XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2028                  2, (frame.button_w - 5), (frame.button_w - 5), 2);
2029 }
2030
2031
2032 void BlackboxWindow::redrawMaximizeButton(bool pressed) {
2033   if (! pressed) {
2034     if (flags.focused) {
2035       if (frame.fbutton)
2036         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2037                                    frame.maximize_button, frame.fbutton);
2038       else
2039         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2040                              frame.fbutton_pixel);
2041     } else {
2042       if (frame.ubutton)
2043         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2044                                    frame.maximize_button, frame.ubutton);
2045       else
2046         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2047                              frame.ubutton_pixel);
2048     }
2049   } else {
2050     if (frame.pbutton)
2051       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2052                                  frame.maximize_button, frame.pbutton);
2053     else
2054       XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2055                            frame.pbutton_pixel);
2056   }
2057   XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2058
2059   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2060            screen->getWindowStyle()->b_pic_unfocus);
2061   XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2062                  2, 2, (frame.button_w - 5), (frame.button_w - 5));
2063   XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2064             2, 3, (frame.button_w - 3), 3);
2065 }
2066
2067
2068 void BlackboxWindow::redrawCloseButton(bool pressed) {
2069   if (! pressed) {
2070     if (flags.focused) {
2071       if (frame.fbutton)
2072         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2073                                    frame.fbutton);
2074       else
2075         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2076                              frame.fbutton_pixel);
2077     } else {
2078       if (frame.ubutton)
2079         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2080                                    frame.ubutton);
2081       else
2082         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2083                              frame.ubutton_pixel);
2084     }
2085   } else {
2086     if (frame.pbutton)
2087       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2088                                  frame.close_button, frame.pbutton);
2089     else
2090       XSetWindowBackground(blackbox->getXDisplay(),
2091                            frame.close_button, frame.pbutton_pixel);
2092   }
2093   XClearWindow(blackbox->getXDisplay(), frame.close_button);
2094
2095   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2096            screen->getWindowStyle()->b_pic_unfocus);
2097   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2098             2, 2, (frame.button_w - 3), (frame.button_w - 3));
2099   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2100             2, (frame.button_w - 3), (frame.button_w - 3), 2);
2101 }
2102
2103
2104 void BlackboxWindow::mapRequestEvent(XMapRequestEvent *re) {
2105   if (re->window != client.window)
2106     return;
2107
2108 #ifdef    DEBUG
2109   fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2110           client.window);
2111 #endif // DEBUG
2112
2113   bool get_state_ret = getState();
2114   if (! (get_state_ret && blackbox->isStartup())) {
2115     if ((client.wm_hint_flags & StateHint) &&
2116         (! (current_state == NormalState || current_state == IconicState)))
2117       current_state = client.initial_state;
2118     else
2119       current_state = NormalState;
2120   } else if (flags.iconic) {
2121     current_state = NormalState;
2122   }
2123
2124   switch (current_state) {
2125   case IconicState:
2126     iconify();
2127     break;
2128
2129   case WithdrawnState:
2130     withdraw();
2131     break;
2132
2133   case NormalState:
2134   case InactiveState:
2135   case ZoomState:
2136   default:
2137     show();
2138     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2139     if (! blackbox->isStartup() && (isTransient() || screen->doFocusNew())) {
2140       XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped..
2141       setInputFocus();
2142     }
2143     break;
2144   }
2145 }
2146
2147
2148 void BlackboxWindow::unmapNotifyEvent(XUnmapEvent *ue) {
2149   if (ue->window != client.window)
2150     return;
2151
2152 #ifdef    DEBUG
2153   fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2154           client.window);
2155 #endif // DEBUG
2156
2157   screen->unmanageWindow(this, False);
2158 }
2159
2160
2161 void BlackboxWindow::destroyNotifyEvent(XDestroyWindowEvent *de) {
2162   if (de->window != client.window)
2163     return;
2164
2165 #ifdef    DEBUG
2166   fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2167           client.window);
2168 #endif // DEBUG
2169
2170   screen->unmanageWindow(this, False);
2171 }
2172
2173
2174 void BlackboxWindow::reparentNotifyEvent(XReparentEvent *re) {
2175   if (re->window != client.window || re->parent == frame.plate)
2176     return;
2177
2178 #ifdef    DEBUG
2179   fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2180           "0x%lx.\n", client.window, re->parent);
2181 #endif // DEBUG
2182
2183   XEvent ev;
2184   ev.xreparent = *re;
2185   XPutBackEvent(blackbox->getXDisplay(), &ev);
2186   screen->unmanageWindow(this, True);
2187 }
2188
2189
2190 void BlackboxWindow::propertyNotifyEvent(Atom atom) {
2191   switch(atom) {
2192   case XA_WM_CLASS:
2193   case XA_WM_CLIENT_MACHINE:
2194   case XA_WM_COMMAND:
2195     break;
2196
2197   case XA_WM_TRANSIENT_FOR: {
2198     // determine if this is a transient window
2199     getTransientInfo();
2200
2201     // adjust the window decorations based on transience
2202     if (isTransient()) {
2203       decorations &= ~(Decor_Maximize | Decor_Handle);
2204       functions &= ~Func_Maximize;
2205     }
2206
2207     reconfigure();
2208   }
2209     break;
2210
2211   case XA_WM_HINTS:
2212     getWMHints();
2213     break;
2214
2215   case XA_WM_ICON_NAME:
2216     getWMIconName();
2217     if (flags.iconic) screen->propagateWindowName(this);
2218     break;
2219
2220   case XA_WM_NAME:
2221     getWMName();
2222
2223     if (decorations & Decor_Titlebar)
2224       redrawLabel();
2225
2226     screen->propagateWindowName(this);
2227     break;
2228
2229   case XA_WM_NORMAL_HINTS: {
2230     getWMNormalHints();
2231
2232     if ((client.normal_hint_flags & PMinSize) &&
2233         (client.normal_hint_flags & PMaxSize)) {
2234       if (client.max_width <= client.min_width &&
2235           client.max_height <= client.min_height) {
2236         decorations &= ~(Decor_Maximize | Decor_Handle);
2237         functions &= ~(Func_Resize | Func_Maximize);
2238       } else {
2239         decorations |= Decor_Maximize | Decor_Handle;
2240         functions |= Func_Resize | Func_Maximize;
2241       }
2242     }
2243
2244     Rect old_rect = frame.rect;
2245
2246     upsize();
2247
2248     if (old_rect != frame.rect)
2249       reconfigure();
2250
2251     break;
2252   }
2253
2254   default:
2255     if (atom == blackbox->getWMProtocolsAtom()) {
2256       getWMProtocols();
2257
2258       if ((decorations & Decor_Close) && (! frame.close_button)) {
2259         createCloseButton();
2260         if (decorations & Decor_Titlebar) {
2261           positionButtons(True);
2262           XMapSubwindows(blackbox->getXDisplay(), frame.title);
2263         }
2264         if (windowmenu) windowmenu->reconfigure();
2265       }
2266     }
2267
2268     break;
2269   }
2270 }
2271
2272
2273 void BlackboxWindow::exposeEvent(XExposeEvent *ee) {
2274   if (frame.label == ee->window && (decorations & Decor_Titlebar))
2275     redrawLabel();
2276   else if (frame.close_button == ee->window)
2277     redrawCloseButton(False);
2278   else if (frame.maximize_button == ee->window)
2279     redrawMaximizeButton(flags.maximized);
2280   else if (frame.iconify_button == ee->window)
2281     redrawIconifyButton(False);
2282 }
2283
2284
2285 void BlackboxWindow::configureRequestEvent(XConfigureRequestEvent *cr) {
2286   if (cr->window != client.window || flags.iconic)
2287     return;
2288
2289   int cx = frame.rect.x(), cy = frame.rect.y();
2290   unsigned int cw = frame.rect.width(), ch = frame.rect.height();
2291
2292   if (cr->value_mask & CWBorderWidth)
2293     client.old_bw = cr->border_width;
2294
2295   if (cr->value_mask & CWX)
2296     cx = cr->x - frame.margin.left;
2297
2298   if (cr->value_mask & CWY)
2299     cy = cr->y - frame.margin.top;
2300
2301   if (cr->value_mask & CWWidth)
2302     cw = cr->width + frame.margin.left + frame.margin.right;
2303
2304   if (cr->value_mask & CWHeight)
2305     ch = cr->height + frame.margin.top + frame.margin.bottom;
2306
2307   if (frame.rect != Rect(cx, cy, cw, ch))
2308     configure(cx, cy, cw, ch);
2309
2310   if (cr->value_mask & CWStackMode) {
2311     switch (cr->detail) {
2312     case Below:
2313     case BottomIf:
2314       screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2315       break;
2316
2317     case Above:
2318     case TopIf:
2319     default:
2320       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2321       break;
2322     }
2323   }
2324 }
2325
2326
2327 void BlackboxWindow::buttonPressEvent(XButtonEvent *be) {
2328   if (frame.maximize_button == be->window) {
2329     redrawMaximizeButton(True);
2330   } else if (be->button == 1 || (be->button == 3 && be->state == Mod1Mask)) {
2331     if (! flags.focused)
2332       setInputFocus();
2333
2334     if (frame.iconify_button == be->window) {
2335       redrawIconifyButton(True);
2336     } else if (frame.close_button == be->window) {
2337       redrawCloseButton(True);
2338     } else if (frame.plate == be->window) {
2339       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2340
2341       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2342
2343       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2344     } else {
2345       if (frame.title == be->window || frame.label == be->window) {
2346         if (((be->time - lastButtonPressTime) <=
2347              blackbox->getDoubleClickInterval()) ||
2348             (be->state & ControlMask)) {
2349           lastButtonPressTime = 0;
2350           shade();
2351         } else {
2352           lastButtonPressTime = be->time;
2353         }
2354       }
2355
2356       frame.grab_x = be->x_root - frame.rect.x() - frame.border_w;
2357       frame.grab_y = be->y_root - frame.rect.y() - frame.border_w;
2358
2359       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2360
2361       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2362     }
2363   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2364              (be->window != frame.close_button)) {
2365     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2366   } else if (windowmenu && be->button == 3 &&
2367              (frame.title == be->window || frame.label == be->window ||
2368               frame.handle == be->window || frame.window == be->window)) {
2369     int mx = 0, my = 0;
2370
2371     if (frame.title == be->window || frame.label == be->window) {
2372       mx = be->x_root - (windowmenu->getWidth() / 2);
2373       my = frame.rect.y() + frame.title_h + frame.border_w;
2374     } else if (frame.handle == be->window) {
2375       mx = be->x_root - (windowmenu->getWidth() / 2);
2376       my = frame.rect.bottom() - frame.handle_h - (frame.border_w * 3) -
2377            windowmenu->getHeight();
2378     } else {
2379       mx = be->x_root - (windowmenu->getWidth() / 2);
2380
2381       if (be->y <= static_cast<signed>(frame.bevel_w))
2382         my = frame.rect.y() + frame.title_h;
2383       else
2384         my = be->y_root - (windowmenu->getHeight() / 2);
2385     }
2386
2387     // snap the window menu into a corner if necessary - we check the
2388     // position of the menu with the coordinates of the client to
2389     // make the comparisions easier.
2390     // XXX: this needs some work!
2391     if (mx > client.rect.right() -
2392         static_cast<signed>(windowmenu->getWidth()))
2393       mx = frame.rect.right() - windowmenu->getWidth() - frame.border_w + 1;
2394     if (mx < client.rect.left())
2395       mx = frame.rect.x();
2396
2397     if (my > client.rect.bottom() -
2398         static_cast<signed>(windowmenu->getHeight()))
2399       my = frame.rect.bottom() - windowmenu->getHeight() - frame.border_w + 1;
2400     if (my < client.rect.top())
2401       my = frame.rect.y() + ((decorations & Decor_Titlebar) ?
2402                              frame.title_h : 0);
2403
2404     if (windowmenu) {
2405       if (! windowmenu->isVisible()) {
2406         windowmenu->move(mx, my);
2407         windowmenu->show();
2408         XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2409         XRaiseWindow(blackbox->getXDisplay(),
2410                      windowmenu->getSendToMenu()->getWindowID());
2411       } else {
2412         windowmenu->hide();
2413       }
2414     }
2415   // mouse wheel up
2416   } else if (be->button == 4) {
2417     if ((be->window == frame.label ||
2418          be->window == frame.title) &&
2419         ! flags.shaded)
2420       shade();
2421   // mouse wheel down
2422   } else if (be->button == 5) {
2423     if ((be->window == frame.label ||
2424          be->window == frame.title) &&
2425         flags.shaded)
2426       shade();
2427   }
2428 }
2429
2430
2431 void BlackboxWindow::buttonReleaseEvent(XButtonEvent *re) {
2432   if (re->window == frame.maximize_button) {
2433     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2434         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2435       maximize(re->button);
2436     } else {
2437       redrawMaximizeButton(flags.maximized);
2438     }
2439   } else if (re->window == frame.iconify_button) {
2440     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2441         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2442       iconify();
2443     } else {
2444       redrawIconifyButton(False);
2445     }
2446   } else if (re->window == frame.close_button) {
2447     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2448         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
2449       close();
2450     redrawCloseButton(False);
2451   } else if (flags.moving) {
2452     flags.moving = False;
2453
2454     if (! screen->doOpaqueMove()) {
2455       /* when drawing the rubber band, we need to make sure we only draw inside
2456        * the frame... frame.changing_* contain the new coords for the window,
2457        * so we need to subtract 1 from changing_w/changing_h every where we
2458        * draw the rubber band (for both moving and resizing)
2459        */
2460       XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2461                      screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2462                      frame.changing.width() - 1, frame.changing.height() - 1);
2463       XUngrabServer(blackbox->getXDisplay());
2464
2465       configure(frame.changing.x(), frame.changing.y(),
2466                 frame.changing.width(), frame.changing.height());
2467     } else {
2468       configure(frame.rect.x(), frame.rect.y(),
2469                 frame.rect.width(), frame.rect.height());
2470     }
2471     screen->hideGeometry();
2472     XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2473   } else if (flags.resizing) {
2474     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2475                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2476                    frame.changing.width() - 1, frame.changing.height() - 1);
2477     XUngrabServer(blackbox->getXDisplay());
2478
2479     screen->hideGeometry();
2480
2481     constrain((re->window == frame.left_grip) ? TopRight : TopLeft);
2482
2483     // unset maximized state when resized after fully maximized
2484     if (flags.maximized == 1)
2485       maximize(0);
2486     flags.resizing = False;
2487     configure(frame.changing.x(), frame.changing.y(),
2488               frame.changing.width(), frame.changing.height());
2489
2490     XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2491   } else if (re->window == frame.window) {
2492     if (re->button == 2 && re->state == Mod1Mask)
2493       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2494   }
2495 }
2496
2497
2498 void BlackboxWindow::motionNotifyEvent(XMotionEvent *me) {
2499   if (! flags.resizing && (me->state & Button1Mask) &&
2500       (functions & Func_Move) &&
2501       (frame.title == me->window || frame.label == me->window ||
2502        frame.handle == me->window || frame.window == me->window)) {
2503     if (! flags.moving) {
2504       XGrabPointer(blackbox->getXDisplay(), me->window, False,
2505                    Button1MotionMask | ButtonReleaseMask,
2506                    GrabModeAsync, GrabModeAsync,
2507                    None, blackbox->getMoveCursor(), CurrentTime);
2508
2509       if (windowmenu && windowmenu->isVisible())
2510         windowmenu->hide();
2511
2512       flags.moving = True;
2513
2514       if (! screen->doOpaqueMove()) {
2515         XGrabServer(blackbox->getXDisplay());
2516
2517         frame.changing = frame.rect;
2518         screen->showPosition(frame.changing.x(), frame.changing.y());
2519
2520         XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2521                        screen->getOpGC(),
2522                        frame.changing.x(),
2523                        frame.changing.y(),
2524                        frame.changing.width() - 1,
2525                        frame.changing.height() - 1);
2526       }
2527     } else {
2528       int dx = me->x_root - frame.grab_x, dy = me->y_root - frame.grab_y;
2529       dx -= frame.border_w;
2530       dy -= frame.border_w;
2531
2532       const int snap_distance = screen->getEdgeSnapThreshold();
2533
2534       if (snap_distance) {
2535         // window corners
2536         const int wleft = dx,
2537                  wright = dx + frame.rect.width() - 1,
2538                    wtop = dy,
2539                 wbottom = dy + frame.rect.height() - 1;
2540
2541         // Maybe this should be saved in the class, and set in the setWorkspace
2542         // function!!
2543         Workspace *w = screen->getWorkspace(getWorkspaceNumber());
2544         assert(w);
2545
2546         // try snap to another window
2547         for (unsigned int i = 0, c = w->getCount(); i < c; ++i) {
2548           BlackboxWindow *snapwin = w->getWindow(i);
2549           if (snapwin == this)
2550             continue;   // don't snap to self
2551
2552           const Rect &winrect = snapwin->frameRect();
2553           int dleft = std::abs(wright - winrect.left()),
2554              dright = std::abs(wleft - winrect.right()),
2555                dtop = std::abs(wbottom - winrect.top()),
2556             dbottom = std::abs(wtop - winrect.bottom());
2557
2558           // snap left?
2559           if (dleft < snap_distance && dleft <= dright) {
2560             dx = winrect.left() - frame.rect.width();
2561             continue;
2562           }
2563           // snap right?
2564           else if (dright < snap_distance) {
2565             dx = winrect.right() + 1;
2566             continue;
2567           }
2568
2569           // snap top?
2570           if (dtop < snap_distance && dtop <= dbottom) {
2571             dy = winrect.top() - frame.rect.height();
2572             continue;
2573           }
2574           // snap bottom?
2575           else if (dbottom < snap_distance) {
2576             dy = winrect.bottom() + 1;
2577             continue;
2578           }
2579         }
2580                 
2581         // try snap to the screen's available area
2582         Rect srect = screen->availableArea();
2583
2584         int dleft = std::abs(wleft - srect.left()),
2585            dright = std::abs(wright - srect.right()),
2586              dtop = std::abs(wtop - srect.top()),
2587           dbottom = std::abs(wbottom - srect.bottom());
2588
2589         // snap left?
2590         if (dleft < snap_distance && dleft <= dright)
2591           dx = srect.left();
2592         // snap right?
2593         else if (dright < snap_distance)
2594           dx = srect.right() - frame.rect.width() + 1;
2595
2596         // snap top?
2597         if (dtop < snap_distance && dtop <= dbottom)
2598           dy = srect.top();
2599         // snap bottom?
2600         else if (dbottom < snap_distance)
2601           dy = srect.bottom() - frame.rect.height() + 1;
2602
2603         if (! screen->doFullMax()) {
2604           srect = screen->getRect(); // now get the full screen
2605
2606           dleft = std::abs(wleft - srect.left()),
2607           dright = std::abs(wright - srect.right()),
2608           dtop = std::abs(wtop - srect.top()),
2609           dbottom = std::abs(wbottom - srect.bottom());
2610
2611           // snap left?
2612           if (dleft < snap_distance && dleft <= dright)
2613             dx = srect.left();
2614           // snap right?
2615           else if (dright < snap_distance)
2616             dx = srect.right() - frame.rect.width() + 1;
2617
2618           // snap top?
2619           if (dtop < snap_distance && dtop <= dbottom)
2620             dy = srect.top();
2621           // snap bottom?
2622           else if (dbottom < snap_distance)
2623             dy = srect.bottom() - frame.rect.height() + 1;
2624         }
2625       }
2626
2627       if (screen->doOpaqueMove()) {
2628         configure(dx, dy, frame.rect.width(), frame.rect.height());
2629       } else {
2630         XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2631                        screen->getOpGC(),
2632                        frame.changing.x(),
2633                        frame.changing.y(),
2634                        frame.changing.width() - 1,
2635                        frame.changing.height() - 1);
2636
2637         frame.changing.setPos(dx, dy);
2638
2639         XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2640                        screen->getOpGC(),
2641                        frame.changing.x(),
2642                        frame.changing.y(),
2643                        frame.changing.width() - 1,
2644                        frame.changing.height() - 1);
2645       }
2646
2647       screen->showPosition(dx, dy);
2648     }
2649   } else if ((functions & Func_Resize) &&
2650              (((me->state & Button1Mask) &&
2651                (me->window == frame.right_grip ||
2652                 me->window == frame.left_grip)) ||
2653               (me->state & (Mod1Mask | Button3Mask) &&
2654                me->window == frame.window))) {
2655     bool left = (me->window == frame.left_grip);
2656
2657     if (! flags.resizing) {
2658       XGrabServer(blackbox->getXDisplay());
2659       XGrabPointer(blackbox->getXDisplay(), me->window, False,
2660                    ButtonMotionMask | ButtonReleaseMask,
2661                    GrabModeAsync, GrabModeAsync, None,
2662                    ((left) ? blackbox->getLowerLeftAngleCursor() :
2663                     blackbox->getLowerRightAngleCursor()),
2664                    CurrentTime);
2665
2666       flags.resizing = True;
2667
2668       int gw, gh;
2669       frame.grab_x = me->x;
2670       frame.grab_y = me->y;
2671       frame.changing = frame.rect;
2672
2673       constrain((left) ? TopRight : TopLeft, &gw, &gh);
2674
2675       XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2676                      screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2677                      frame.changing.width() - 1, frame.changing.height() - 1);
2678
2679       screen->showGeometry(gw, gh);
2680     } else {
2681       XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2682                      screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2683                      frame.changing.width() - 1, frame.changing.height() - 1);
2684
2685       int gw, gh;
2686
2687       Corner anchor;
2688
2689       if (left) {
2690         anchor = TopRight;
2691         frame.changing.setCoords(me->x_root - frame.grab_x, frame.rect.top(),
2692                                  frame.rect.right(), frame.rect.bottom());
2693         frame.changing.setHeight(frame.rect.height() + (me->y - frame.grab_y));
2694       } else {
2695         anchor = TopLeft;
2696         frame.changing.setSize(frame.rect.width() + (me->x - frame.grab_x),
2697                                frame.rect.height() + (me->y - frame.grab_y));
2698       }
2699
2700       constrain(anchor, &gw, &gh);
2701
2702       XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2703                      screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2704                      frame.changing.width() - 1, frame.changing.height() - 1);
2705
2706       screen->showGeometry(gw, gh);
2707     }
2708   }
2709 }
2710
2711
2712 #ifdef    SHAPE
2713 void BlackboxWindow::shapeEvent(XShapeEvent *) {
2714   if (blackbox->hasShapeExtensions() && flags.shaped) {
2715     configureShape();
2716   }
2717 }
2718 #endif // SHAPE
2719
2720
2721 bool BlackboxWindow::validateClient(void) const {
2722   XSync(blackbox->getXDisplay(), False);
2723
2724   XEvent e;
2725   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
2726                              DestroyNotify, &e) ||
2727       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
2728                              UnmapNotify, &e)) {
2729     XPutBackEvent(blackbox->getXDisplay(), &e);
2730
2731     return False;
2732   }
2733
2734   return True;
2735 }
2736
2737
2738 void BlackboxWindow::restore(bool remap) {
2739   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
2740   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
2741   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
2742
2743   restoreGravity();
2744
2745   XUnmapWindow(blackbox->getXDisplay(), frame.window);
2746   XUnmapWindow(blackbox->getXDisplay(), client.window);
2747
2748   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
2749
2750   XEvent ev;
2751   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
2752                              ReparentNotify, &ev)) {
2753     remap = True;
2754   } else {
2755     // according to the ICCCM - if the client doesn't reparent to
2756     // root, then we have to do it for them
2757     XReparentWindow(blackbox->getXDisplay(), client.window,
2758                     screen->getRootWindow(),
2759                     client.rect.x(), client.rect.y());
2760   }
2761
2762   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
2763 }
2764
2765
2766 // timer for autoraise
2767 void BlackboxWindow::timeout(void) {
2768   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2769 }
2770
2771
2772 void BlackboxWindow::changeBlackboxHints(BlackboxHints *net) {
2773   if ((net->flags & AttribShaded) &&
2774       ((blackbox_attrib.attrib & AttribShaded) !=
2775        (net->attrib & AttribShaded)))
2776     shade();
2777
2778   if (flags.visible && // watch out for requests when we can not be seen
2779       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
2780       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
2781        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
2782     if (flags.maximized) {
2783       maximize(0);
2784     } else {
2785       int button = 0;
2786
2787       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
2788         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
2789       else if (net->flags & AttribMaxVert)
2790         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
2791       else if (net->flags & AttribMaxHoriz)
2792         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
2793
2794       maximize(button);
2795     }
2796   }
2797
2798   if ((net->flags & AttribOmnipresent) &&
2799       ((blackbox_attrib.attrib & AttribOmnipresent) !=
2800        (net->attrib & AttribOmnipresent)))
2801     stick();
2802
2803   if ((net->flags & AttribWorkspace) &&
2804       (blackbox_attrib.workspace != net->workspace)) {
2805     screen->reassociateWindow(this, net->workspace, True);
2806
2807     if (screen->getCurrentWorkspaceID() != net->workspace) {
2808       withdraw();
2809     } else {
2810       show();
2811       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2812     }
2813   }
2814
2815   if (net->flags & AttribDecoration) {
2816     switch (net->decoration) {
2817     case DecorNone:
2818       // clear all decorations except close
2819       decorations &= Decor_Close;
2820
2821       break;
2822
2823     default:
2824     case DecorNormal:
2825       decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
2826   
2827       decorations = ((functions & Func_Resize) && !isTransient() ?
2828                      decorations | Decor_Handle :
2829                      decorations &= ~Decor_Handle);
2830       decorations = (functions & Func_Maximize ?
2831                      decorations | Decor_Maximize :
2832                      decorations &= ~Decor_Maximize);
2833
2834       break;
2835
2836     case DecorTiny:
2837       decorations |= Decor_Titlebar | Decor_Iconify;
2838       decorations &= ~(Decor_Border | Decor_Handle);
2839       
2840       decorations = (functions & Func_Maximize ?
2841                      decorations | Decor_Maximize :
2842                      decorations &= ~Decor_Maximize);
2843
2844       break;
2845
2846     case DecorTool:
2847       decorations |= Decor_Titlebar;
2848       decorations &= ~(Decor_Iconify | Decor_Border);
2849
2850       decorations = ((functions & Func_Resize) && !isTransient() ?
2851                      decorations | Decor_Handle :
2852                      decorations &= ~Decor_Handle);
2853       decorations = (functions & Func_Maximize ?
2854                      decorations | Decor_Maximize :
2855                      decorations &= ~Decor_Maximize);
2856
2857       break;
2858     }
2859
2860     // we can not be shaded if we lack a titlebar
2861     if (flags.shaded && ! (decorations & Decor_Titlebar))
2862       shade();
2863
2864     if (frame.window) {
2865       XMapSubwindows(blackbox->getXDisplay(), frame.window);
2866       XMapWindow(blackbox->getXDisplay(), frame.window);
2867     }
2868
2869     reconfigure();
2870     setState(current_state);
2871   }
2872 }
2873
2874
2875 /*
2876  * Set the sizes of all components of the window frame
2877  * (the window decorations).
2878  * These values are based upon the current style settings and the client
2879  * window's dimensions.
2880  */
2881 void BlackboxWindow::upsize(void) {
2882   frame.bevel_w = screen->getBevelWidth();
2883
2884   if (decorations & Decor_Border) {
2885     frame.border_w = screen->getBorderWidth();
2886     if (! isTransient())
2887       frame.mwm_border_w = screen->getFrameWidth();
2888     else
2889       frame.mwm_border_w = 0;
2890   } else {
2891     frame.mwm_border_w = frame.border_w = 0;
2892   }
2893
2894   if (decorations & Decor_Titlebar) {
2895     // the height of the titlebar is based upon the height of the font being
2896     // used to display the window's title
2897     WindowStyle *style = screen->getWindowStyle();
2898     if (i18n.multibyte())
2899       frame.title_h = (style->fontset_extents->max_ink_extent.height +
2900                        (frame.bevel_w * 2) + 2);
2901     else
2902       frame.title_h = (style->font->ascent + style->font->descent +
2903                        (frame.bevel_w * 2) + 2);
2904
2905     frame.label_h = frame.title_h - (frame.bevel_w * 2);
2906     frame.button_w = (frame.label_h - 2);
2907
2908     // set the top frame margin
2909     frame.margin.top = frame.border_w + frame.title_h +
2910                        frame.border_w + frame.mwm_border_w;
2911   } else {
2912     frame.title_h = 0;
2913     frame.label_h = 0;
2914     frame.button_w = 0;
2915
2916     // set the top frame margin
2917     frame.margin.top = frame.border_w + frame.mwm_border_w;
2918   }
2919
2920   // set the left/right frame margin
2921   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
2922
2923   if (decorations & Decor_Handle) {
2924     frame.grip_w = frame.button_w * 2;
2925     frame.handle_h = screen->getHandleWidth();
2926
2927     // set the bottom frame margin
2928     frame.margin.bottom = frame.border_w + frame.handle_h +
2929                           frame.border_w + frame.mwm_border_w;
2930   } else {
2931     frame.handle_h = 0;
2932     frame.grip_w = 0;
2933
2934     // set the bottom frame margin
2935     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
2936   }
2937
2938   /*
2939     We first get the normal dimensions and use this to define the inside_w/h
2940     then we modify the height if shading is in effect.
2941     If the shade state is not considered then frame.rect gets reset to the
2942     normal window size on a reconfigure() call resulting in improper
2943     dimensions appearing in move/resize and other events.
2944   */
2945   unsigned int
2946     height = client.rect.height() + frame.margin.top + frame.margin.bottom,
2947     width = client.rect.width() + frame.margin.left + frame.margin.right;
2948
2949   frame.inside_w = width - (frame.border_w * 2);
2950   frame.inside_h = height - (frame.border_w * 2);
2951
2952   if (flags.shaded)
2953     height = frame.title_h + (frame.border_w * 2);
2954   frame.rect.setSize(width, height);
2955 }
2956
2957
2958 /*
2959  * Calculate the size of the client window and constrain it to the
2960  * size specified by the size hints of the client window.
2961  *
2962  * The logical width and height are placed into pw and ph, if they
2963  * are non-zero.  Logical size refers to the users perception of
2964  * the window size (for example an xterm resizes in cells, not in pixels).
2965  *
2966  * The physical geometry is placed into frame.changing_{x,y,width,height}.
2967  * Physical geometry refers to the geometry of the window in pixels.
2968  */
2969 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
2970   // frame.changing represents the requested frame size, we need to
2971   // strip the frame margin off and constrain the client size
2972   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
2973                            frame.changing.top() + frame.margin.top,
2974                            frame.changing.right() - frame.margin.right,
2975                            frame.changing.bottom() - frame.margin.bottom);
2976
2977   int dw = frame.changing.width(), dh = frame.changing.height(),
2978     base_width = (client.base_width) ? client.base_width : client.min_width,
2979     base_height = (client.base_height) ? client.base_height :
2980                                          client.min_height;
2981
2982   // constrain
2983   if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
2984   if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
2985   if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
2986   if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
2987
2988   dw -= base_width;
2989   dw /= client.width_inc;
2990   dh -= base_height;
2991   dh /= client.height_inc;
2992
2993   if (pw) *pw = dw;
2994   if (ph) *ph = dh;
2995
2996   dw *= client.width_inc;
2997   dw += base_width;
2998   dh *= client.height_inc;
2999   dh += base_height;
3000
3001   frame.changing.setSize(dw, dh);
3002
3003   // add the frame margin back onto frame.changing
3004   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3005                            frame.changing.top() - frame.margin.top,
3006                            frame.changing.right() + frame.margin.right,
3007                            frame.changing.bottom() + frame.margin.bottom);
3008
3009   // move frame.changing to the specified anchor
3010   switch (anchor) {
3011   case TopLeft:
3012     // nothing to do
3013     break;
3014
3015   case TopRight:
3016     int dx = frame.rect.right() - frame.changing.right();
3017     frame.changing.setPos(frame.changing.x() + dx, frame.changing.y());
3018     break;
3019   }
3020 }
3021
3022
3023 int WindowStyle::doJustify(const char *text, int &start_pos,
3024                            unsigned int max_length, unsigned int modifier,
3025                            bool multibyte) const {
3026   size_t text_len = strlen(text);
3027   unsigned int length;
3028
3029   do {
3030     if (multibyte) {
3031       XRectangle ink, logical;
3032       XmbTextExtents(fontset, text, text_len, &ink, &logical);
3033       length = logical.width;
3034     } else {
3035       length = XTextWidth(font, text, text_len);
3036     }
3037     length += modifier;
3038   } while (length > max_length && text_len-- > 0);
3039
3040   switch (justify) {
3041   case RightJustify:
3042     start_pos += max_length - length;
3043     break;
3044
3045   case CenterJustify:
3046     start_pos += (max_length - length) / 2;
3047     break;
3048
3049   case LeftJustify:
3050   default:
3051     break;
3052   }
3053
3054   return text_len;
3055 }
3056
3057
3058 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
3059   : blackbox(b), group(_group) {
3060   XWindowAttributes wattrib;
3061   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
3062     // group window doesn't seem to exist anymore
3063     delete this;
3064     return;
3065   }
3066
3067   /*
3068     watch for destroy notify on the group window (in addition to
3069     any other events we are looking for)
3070
3071     since some managed windows can also be window group controllers,
3072     we need to make sure that we don't clobber the event mask for the
3073     managed window
3074   */
3075   XSelectInput(blackbox->getXDisplay(), group,
3076                wattrib.your_event_mask | StructureNotifyMask);
3077
3078   blackbox->saveGroupSearch(group, this);
3079 }
3080
3081
3082 BWindowGroup::~BWindowGroup(void) {
3083   blackbox->removeGroupSearch(group);
3084 }
3085
3086
3087 BlackboxWindow *
3088 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
3089   BlackboxWindow *ret = blackbox->getFocusedWindow();
3090
3091   // does the focus window match (or any transient_fors)?
3092   while (ret) {
3093     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3094       if (ret->isTransient() && allow_transients) break;
3095       else if (! ret->isTransient()) break;
3096     }
3097
3098     ret = ret->getTransientFor();
3099   }
3100
3101   if (ret) return ret;
3102
3103   // the focus window didn't match, look in the group's window list
3104   BlackboxWindowList::const_iterator it, end = windowList.end();
3105   for (it = windowList.begin(); it != end; ++it) {
3106     ret = *it;
3107     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3108       if (ret->isTransient() && allow_transients) break;
3109       else if (! ret->isTransient()) break;
3110     }
3111   }
3112
3113   return ret;
3114 }