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