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