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