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