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