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