]> icculus.org git repositories - dana/openbox.git/blob - src/Screen.cc
unmanage desktop windows on shutdown
[dana/openbox.git] / src / Screen.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Screen.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
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    XINERAMA
33 #  include <X11/Xlib.h>
34 #  include <X11/extensions/Xinerama.h>
35 #endif // XINERAMA
36
37 #ifdef HAVE_STDLIB_H
38 #  include <stdlib.h>
39 #endif // HAVE_STDLIB_H
40
41 #ifdef HAVE_STRING_H
42 #  include <string.h>
43 #endif // HAVE_STRING_H
44
45 #ifdef    HAVE_CTYPE_H
46 #  include <ctype.h>
47 #endif // HAVE_CTYPE_H
48
49 #ifdef    HAVE_UNISTD_H
50 #  include <sys/types.h>
51 #  include <unistd.h>
52 #endif // HAVE_UNISTD_H
53
54 #ifdef    HAVE_DIRENT_H
55 #  include <dirent.h>
56 #endif // HAVE_DIRENT_H
57
58 #ifdef    HAVE_LOCALE_H
59 #  include <locale.h>
60 #endif // HAVE_LOCALE_H
61
62 #ifdef    HAVE_SYS_STAT_H
63 #  include <sys/stat.h>
64 #endif // HAVE_SYS_STAT_H
65
66 #ifdef    HAVE_STDARG_H
67 #  include <stdarg.h>
68 #endif // HAVE_STDARG_H
69 }
70
71 #include <assert.h>
72
73 #include <algorithm>
74 #include <functional>
75 #include <string>
76 using std::string;
77
78 #include "i18n.hh"
79 #include "blackbox.hh"
80 #include "Clientmenu.hh"
81 #include "Font.hh"
82 #include "GCCache.hh"
83 #include "Iconmenu.hh"
84 #include "Image.hh"
85 #include "Screen.hh"
86 #include "Slit.hh"
87 #include "Rootmenu.hh"
88 #include "Toolbar.hh"
89 #include "Util.hh"
90 #include "Window.hh"
91 #include "Workspace.hh"
92 #include "Workspacemenu.hh"
93 #include "Util.hh"
94 #include "XAtom.hh"
95
96 #ifndef   FONT_ELEMENT_SIZE
97 #define   FONT_ELEMENT_SIZE 50
98 #endif // FONT_ELEMENT_SIZE
99
100
101 static bool running = True;
102
103 static int anotherWMRunning(Display *display, XErrorEvent *) {
104   fprintf(stderr, i18n(ScreenSet, ScreenAnotherWMRunning,
105           "BScreen::BScreen: an error occured while querying the X server.\n"
106           "  another window manager already running on display %s.\n"),
107           DisplayString(display));
108
109   running = False;
110
111   return(-1);
112 }
113
114
115 BScreen::BScreen(Blackbox *bb, unsigned int scrn) : ScreenInfo(bb, scrn) {
116   blackbox = bb;
117   screenstr = "session.screen" + itostring(scrn) + '.';
118   config = blackbox->getConfig();
119   xatom = blackbox->getXAtom();
120
121   event_mask = ColormapChangeMask | EnterWindowMask | PropertyChangeMask |
122     SubstructureRedirectMask | ButtonPressMask | ButtonReleaseMask;
123
124   XErrorHandler old = XSetErrorHandler((XErrorHandler) anotherWMRunning);
125   XSelectInput(getBaseDisplay()->getXDisplay(), getRootWindow(), event_mask);
126   XSync(getBaseDisplay()->getXDisplay(), False);
127   XSetErrorHandler((XErrorHandler) old);
128
129   managed = running;
130   if (! managed) return;
131
132   fprintf(stderr, i18n(ScreenSet, ScreenManagingScreen,
133                        "BScreen::BScreen: managing screen %d "
134                        "using visual 0x%lx, depth %d\n"),
135           getScreenNumber(), XVisualIDFromVisual(getVisual()),
136           getDepth());
137
138   rootmenu = 0;
139
140   resource.mstyle.t_font = resource.mstyle.f_font = resource.tstyle.font =
141     resource.wstyle.font = (BFont *) 0;
142
143   geom_pixmap = None;
144
145   xatom->setSupported(this);    // set-up netwm support
146 #ifdef    HAVE_GETPID
147   xatom->setValue(getRootWindow(), XAtom::blackbox_pid, XAtom::cardinal,
148                   (unsigned long) getpid());
149 #endif // HAVE_GETPID
150   unsigned long geometry[] = { getWidth(),
151                                getHeight()};
152   xatom->setValue(getRootWindow(), XAtom::net_desktop_geometry,
153                   XAtom::cardinal, geometry, 2);
154   unsigned long viewport[] = {0,0};
155   xatom->setValue(getRootWindow(), XAtom::net_desktop_viewport,
156                   XAtom::cardinal, viewport, 2);
157                   
158
159   XDefineCursor(blackbox->getXDisplay(), getRootWindow(),
160                 blackbox->getSessionCursor());
161
162   updateAvailableArea();
163
164   image_control =
165     new BImageControl(blackbox, this, True, blackbox->getColorsPerChannel(),
166                       blackbox->getCacheLife(), blackbox->getCacheMax());
167   image_control->installRootColormap();
168   root_colormap_installed = True;
169
170   load_rc();
171   LoadStyle();
172
173   XGCValues gcv;
174   gcv.foreground = WhitePixel(blackbox->getXDisplay(), getScreenNumber())
175     ^ BlackPixel(blackbox->getXDisplay(), getScreenNumber());
176   gcv.function = GXxor;
177   gcv.subwindow_mode = IncludeInferiors;
178   opGC = XCreateGC(blackbox->getXDisplay(), getRootWindow(),
179                    GCForeground | GCFunction | GCSubwindowMode, &gcv);
180
181   const char *s =  i18n(ScreenSet, ScreenPositionLength,
182                         "0: 0000 x 0: 0000");
183   geom_w = resource.wstyle.font->measureString(s) + resource.bevel_width * 2;
184   geom_h = resource.wstyle.font->height() + resource.bevel_width * 2;
185
186   XSetWindowAttributes attrib;
187   unsigned long mask = CWBorderPixel | CWColormap | CWSaveUnder;
188   attrib.border_pixel = getBorderColor()->pixel();
189   attrib.colormap = getColormap();
190   attrib.save_under = True;
191
192   geom_window = XCreateWindow(blackbox->getXDisplay(), getRootWindow(),
193                               0, 0, geom_w, geom_h, resource.border_width,
194                               getDepth(), InputOutput, getVisual(),
195                               mask, &attrib);
196   geom_visible = False;
197
198   BTexture* texture = &(resource.wstyle.l_focus);
199   geom_pixmap = texture->render(geom_w, geom_h, geom_pixmap);
200   if (geom_pixmap == ParentRelative) {
201     texture = &(resource.wstyle.t_focus);
202     geom_pixmap = texture->render(geom_w, geom_h, geom_pixmap);
203   }
204   if (! geom_pixmap)
205     XSetWindowBackground(blackbox->getXDisplay(), geom_window,
206                          texture->color().pixel());
207   else
208     XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
209                                geom_window, geom_pixmap);
210
211   workspacemenu = new Workspacemenu(this);
212   iconmenu = new Iconmenu(this);
213   configmenu = new Configmenu(this);
214
215   if (resource.workspaces > 0) {
216     for (unsigned int i = 0; i < resource.workspaces; ++i) {
217       Workspace *wkspc = new Workspace(this, workspacesList.size());
218       workspacesList.push_back(wkspc);
219       workspacemenu->insertWorkspace(wkspc);
220       workspacemenu->update();
221
222     }
223   } else {
224     Workspace *wkspc = new Workspace(this, workspacesList.size());
225     workspacesList.push_back(wkspc);
226     workspacemenu->insertWorkspace(wkspc);
227     workspacemenu->update();
228   }
229   saveWorkspaceNames();
230
231   updateNetizenWorkspaceCount();
232
233   workspacemenu->insert(i18n(IconSet, IconIcons, "Icons"), iconmenu);
234   workspacemenu->update();
235
236   current_workspace = workspacesList.front();
237   
238   xatom->setValue(getRootWindow(), XAtom::net_current_desktop,
239                   XAtom::cardinal, 0); //first workspace
240
241   workspacemenu->setItemSelected(2, True);
242
243   toolbar = new Toolbar(this);
244
245   slit = new Slit(this);
246
247   InitMenu();
248
249   raiseWindows(0, 0);     // this also initializes the empty stacking list
250   rootmenu->update();
251
252   updateClientList();     // initialize the client lists, which will be empty
253   updateAvailableArea();
254
255   changeWorkspaceID(0);
256
257   unsigned int i, j, nchild;
258   Window r, p, *children;
259   XQueryTree(blackbox->getXDisplay(), getRootWindow(), &r, &p,
260              &children, &nchild);
261
262   // preen the window list of all icon windows... for better dockapp support
263   for (i = 0; i < nchild; i++) {
264     if (children[i] == None) continue;
265
266     XWMHints *wmhints = XGetWMHints(blackbox->getXDisplay(),
267                                     children[i]);
268
269     if (wmhints) {
270       if ((wmhints->flags & IconWindowHint) &&
271           (wmhints->icon_window != children[i])) {
272         for (j = 0; j < nchild; j++) {
273           if (children[j] == wmhints->icon_window) {
274             children[j] = None;
275             break;
276           }
277         }
278       }
279
280       XFree(wmhints);
281     }
282   }
283
284   // manage shown windows
285   for (i = 0; i < nchild; ++i) {
286     if (children[i] == None || ! blackbox->validateWindow(children[i]))
287       continue;
288
289     XWindowAttributes attrib;
290     if (XGetWindowAttributes(blackbox->getXDisplay(), children[i], &attrib)) {
291       if (attrib.override_redirect) continue;
292
293       if (attrib.map_state != IsUnmapped) {
294         manageWindow(children[i]);
295       }
296     }
297   }
298
299   XFree(children);
300
301   // call this again just in case a window we found updates the Strut list
302   updateAvailableArea();
303 }
304
305
306 BScreen::~BScreen(void) {
307   if (! managed) return;
308
309   if (geom_pixmap != None)
310     image_control->removeImage(geom_pixmap);
311
312   if (geom_window != None)
313     XDestroyWindow(blackbox->getXDisplay(), geom_window);
314
315   std::for_each(workspacesList.begin(), workspacesList.end(),
316                 PointerAssassin());
317
318   std::for_each(iconList.begin(), iconList.end(), PointerAssassin());
319
320   std::for_each(netizenList.begin(), netizenList.end(), PointerAssassin());
321
322   while (! systrayWindowList.empty())
323     removeSystrayWindow(systrayWindowList[0]);
324
325   delete rootmenu;
326   delete workspacemenu;
327   delete iconmenu;
328   delete configmenu;
329   delete slit;
330   delete toolbar;
331   delete image_control;
332
333   if (resource.wstyle.font)
334     delete resource.wstyle.font;
335   if (resource.mstyle.t_font)
336     delete resource.mstyle.t_font;
337   if (resource.mstyle.f_font)
338     delete resource.mstyle.f_font;
339   if (resource.tstyle.font)
340     delete resource.tstyle.font;
341
342   XFreeGC(blackbox->getXDisplay(), opGC);
343 }
344
345
346 void BScreen::saveSloppyFocus(bool s) {
347   resource.sloppy_focus = s;
348
349   string fmodel;
350   if (resource.sloppy_focus) {
351     fmodel = "SloppyFocus";
352     if (resource.auto_raise) fmodel += " AutoRaise";
353     if (resource.click_raise) fmodel += " ClickRaise";
354   } else {
355     fmodel = "ClickToFocus";
356   }
357   config->setValue(screenstr + "focusModel", fmodel);
358 }
359
360
361 void BScreen::saveAutoRaise(bool a) {
362   resource.auto_raise = a;
363   saveSloppyFocus(resource.sloppy_focus);
364 }
365
366
367 void BScreen::saveClickRaise(bool c) {
368   resource.click_raise = c;
369   saveSloppyFocus(resource.sloppy_focus);
370 }
371
372
373 void BScreen::saveImageDither(bool d) {
374   image_control->setDither(d);
375   config->setValue(screenstr + "imageDither", doImageDither());
376 }
377
378
379 void BScreen::saveOpaqueMove(bool o) {
380   resource.opaque_move = o;
381   config->setValue(screenstr + "opaqueMove", resource.opaque_move);
382 }
383
384
385 void BScreen::saveFullMax(bool f) {
386   resource.full_max = f;
387   config->setValue(screenstr + "fullMaximization", resource.full_max);
388 }
389
390
391 void BScreen::saveFocusNew(bool f) {
392   resource.focus_new = f;
393   config->setValue(screenstr + "focusNewWindows", resource.focus_new);
394 }
395
396
397 void BScreen::saveFocusLast(bool f) {
398   resource.focus_last = f;
399   config->setValue(screenstr + "focusLastWindow", resource.focus_last);
400 }
401
402
403 void BScreen::saveAAFonts(bool f) {
404   resource.aa_fonts = f;
405   reconfigure();
406   config->setValue(screenstr + "antialiasFonts", resource.aa_fonts);
407 }
408
409
410 void BScreen::saveHideToolbar(bool h) {
411   resource.hide_toolbar = h;
412   if (resource.hide_toolbar)
413     toolbar->unmapToolbar();
414   else
415     toolbar->mapToolbar();
416   config->setValue(screenstr + "hideToolbar", resource.hide_toolbar);
417 }
418
419
420 void BScreen::saveWindowToEdgeSnap(int s) {
421   resource.snap_to_edges = s;
422
423   const char *snap;
424   switch (resource.snap_to_edges) {
425   case WindowNoSnap: snap = "NoSnap"; break;
426   case WindowResistance: snap = "Resistance"; break;
427   case WindowSnap: default: snap = "Snap"; break;
428   }
429   config->setValue(screenstr + "windowToEdgeSnap", snap);
430 }
431
432
433 void BScreen::saveWindowToWindowSnap(int s) {
434   resource.snap_to_windows = s;
435   
436   const char *snap;
437   switch (resource.snap_to_windows) {
438   case WindowNoSnap: snap = "NoSnap"; break;
439   case WindowResistance: snap = "Resistance"; break;
440   case WindowSnap: default: snap = "Snap"; break;
441   }
442   config->setValue(screenstr + "windowToWindowSnap", snap);
443 }
444
445
446 void BScreen::saveResizeZones(unsigned int z) {
447   resource.resize_zones = z;
448   config->setValue(screenstr + "resizeZones", resource.resize_zones);
449 }
450
451
452 void BScreen::saveWindowCornerSnap(bool s) {
453   resource.window_corner_snap = s;
454   config->setValue(screenstr + "windowCornerSnap",
455                    resource.window_corner_snap);
456 }
457
458
459 void BScreen::saveWorkspaces(unsigned int w) {
460   resource.workspaces = w;
461   config->setValue(screenstr + "workspaces", resource.workspaces);
462 }
463
464
465 void BScreen::savePlacementPolicy(int p) {
466   resource.placement_policy = p; 
467   const char *placement;
468   switch (resource.placement_policy) {
469   case CascadePlacement: placement = "CascadePlacement"; break;
470   case UnderMousePlacement: placement = "UnderMousePlacement"; break;
471   case ClickMousePlacement: placement = "ClickMousePlacement"; break;
472   case ColSmartPlacement: placement = "ColSmartPlacement"; break;
473   case RowSmartPlacement: default: placement = "RowSmartPlacement"; break;
474   }
475   config->setValue(screenstr + "windowPlacement", placement);
476 }
477
478
479 void BScreen::saveResistanceSize(int s) {
480   resource.resistance_size = s;
481   config->setValue(screenstr + "resistanceSize",
482                    resource.resistance_size);
483 }
484
485
486 void BScreen::saveSnapThreshold(int t) {
487   resource.snap_threshold = t;
488   config->setValue(screenstr + "edgeSnapThreshold",
489                    resource.snap_threshold);
490 }
491
492
493 void BScreen::saveSnapOffset(int t) {
494   resource.snap_offset = t;
495   config->setValue(screenstr + "edgeSnapOffset",
496                    resource.snap_offset);
497 }
498
499
500 void BScreen::saveRowPlacementDirection(int d) {
501   resource.row_direction = d;
502   config->setValue(screenstr + "rowPlacementDirection",
503                    resource.row_direction == LeftRight ?
504                    "LeftToRight" : "RightToLeft");
505 }
506
507
508 void BScreen::saveColPlacementDirection(int d) {
509   resource.col_direction = d;
510   config->setValue(screenstr + "colPlacementDirection",
511                    resource.col_direction == TopBottom ?
512                    "TopToBottom" : "BottomToTop");
513 }
514
515
516 #ifdef    HAVE_STRFTIME
517 void BScreen::saveStrftimeFormat(const std::string& format) {
518   resource.strftime_format = format;
519   config->setValue(screenstr + "strftimeFormat", resource.strftime_format);
520 }
521
522 #else // !HAVE_STRFTIME
523
524 void BScreen::saveDateFormat(int f) {
525   resource.date_format = f;
526   config->setValue(screenstr + "dateFormat",
527                    resource.date_format == B_EuropeanDate ?
528                    "European" : "American");
529 }
530
531
532 void BScreen::saveClock24Hour(bool c) {
533   resource.clock24hour = c;
534   config->setValue(screenstr + "clockFormat", resource.clock24hour ? 24 : 12);
535 }
536 #endif // HAVE_STRFTIME
537
538
539 void BScreen::saveWorkspaceNames() {
540   string names;
541  
542   for (unsigned int i = 0; i < workspacesList.size(); ++i) {
543     names += workspacesList[i]->getName();
544     if (i < workspacesList.size() - 1)
545       names += ',';
546   }
547
548   config->setValue(screenstr + "workspaceNames", names);
549 }
550
551
552 void BScreen::savePlaceIgnoreShaded(bool i) {
553   resource.ignore_shaded = i;
554   config->setValue(screenstr + "placementIgnoreShaded",
555                    resource.ignore_shaded);
556 }
557
558
559 void BScreen::savePlaceIgnoreMaximized(bool i) {
560   resource.ignore_maximized = i;
561   config->setValue(screenstr + "placementIgnoreMaximized",
562                    resource.ignore_maximized);
563 }
564
565
566 void BScreen::saveAllowScrollLock(bool a) {
567   resource.allow_scroll_lock = a;
568   config->setValue(screenstr + "disableBindingsWithScrollLock",
569                    resource.allow_scroll_lock);
570 }
571
572
573 void BScreen::saveWorkspaceWarping(bool w) {
574   resource.workspace_warping = w;
575   config->setValue(screenstr + "workspaceWarping",
576                    resource.workspace_warping);
577 }
578
579
580 void BScreen::saveRootScrollDirection(int d) {
581   resource.root_scroll = d;
582   const char *dir;
583   switch (resource.root_scroll) {
584   case NoScroll: dir = "None"; break;
585   case ReverseScroll: dir = "Reverse"; break;
586   case NormalScroll: default: dir = "Normal"; break;
587   }
588   config->setValue(screenstr + "rootScrollDirection", dir);
589 }
590
591
592 void BScreen::save_rc(void) {
593   saveSloppyFocus(resource.sloppy_focus);
594   saveAutoRaise(resource.auto_raise);
595   saveImageDither(doImageDither());
596   saveAAFonts(resource.aa_fonts);
597   saveResizeZones(resource.resize_zones);
598   saveOpaqueMove(resource.opaque_move);
599   saveFullMax(resource.full_max);
600   saveFocusNew(resource.focus_new);
601   saveFocusLast(resource.focus_last);
602   saveHideToolbar(resource.hide_toolbar);
603   saveWindowToWindowSnap(resource.snap_to_windows);
604   saveWindowToEdgeSnap(resource.snap_to_edges);
605   saveWindowCornerSnap(resource.window_corner_snap);
606   saveWorkspaces(resource.workspaces);
607   savePlacementPolicy(resource.placement_policy);
608   saveSnapThreshold(resource.snap_threshold);
609   saveSnapOffset(resource.snap_offset);
610   saveResistanceSize(resource.resistance_size);
611   saveRowPlacementDirection(resource.row_direction);
612   saveColPlacementDirection(resource.col_direction);
613 #ifdef    HAVE_STRFTIME
614   saveStrftimeFormat(resource.strftime_format); 
615 #else // !HAVE_STRFTIME
616   saveDateFormat(resource.date_format);
617   savwClock24Hour(resource.clock24hour);
618 #endif // HAVE_STRFTIME
619   savePlaceIgnoreShaded(resource.ignore_shaded);
620   savePlaceIgnoreMaximized(resource.ignore_maximized);
621   saveAllowScrollLock(resource.allow_scroll_lock);
622   saveWorkspaceWarping(resource.workspace_warping);
623   saveRootScrollDirection(resource.root_scroll);
624
625   toolbar->save_rc();
626   slit->save_rc();
627 }
628
629
630 void BScreen::load_rc(void) {
631   std::string s;
632   bool b;
633
634   if (! config->getValue(screenstr + "fullMaximization", resource.full_max))
635     resource.full_max = false;
636
637   if (! config->getValue(screenstr + "focusNewWindows", resource.focus_new))
638     resource.focus_new = false;
639
640   if (! config->getValue(screenstr + "focusLastWindow", resource.focus_last))
641     resource.focus_last = false;
642
643   if (! config->getValue(screenstr + "workspaces", resource.workspaces))
644     resource.workspaces = 1;
645
646   if (! config->getValue(screenstr + "opaqueMove", resource.opaque_move))
647     resource.opaque_move = false;
648
649   if (! config->getValue(screenstr + "antialiasFonts", resource.aa_fonts))
650     resource.aa_fonts = true;
651
652   if (! config->getValue(screenstr + "resizeZones", resource.resize_zones) ||
653       (resource.resize_zones != 1 && resource.resize_zones != 2 &&
654        resource.resize_zones != 4))
655       resource.resize_zones = 4;
656
657   if (! config->getValue(screenstr + "hideToolbar", resource.hide_toolbar))
658     resource.hide_toolbar = false;
659
660   resource.snap_to_windows = WindowResistance;
661   if (config->getValue(screenstr + "windowToWindowSnap", s)) {
662     if (s == "NoSnap")
663       resource.snap_to_windows = WindowNoSnap;
664     else if (s == "Snap")
665       resource.snap_to_windows = WindowSnap;
666   }
667
668   resource.snap_to_edges = WindowResistance;
669   if (config->getValue(screenstr + "windowToEdgeSnap", s)) {
670     if (s == "NoSnap")
671       resource.snap_to_edges = WindowNoSnap;
672     else if (s == "Snap")
673       resource.snap_to_edges = WindowSnap;
674   }
675
676   if (! config->getValue(screenstr + "windowCornerSnap",
677                          resource.window_corner_snap))
678     resource.window_corner_snap = true;
679
680   if (! config->getValue(screenstr + "imageDither", b))
681     b = true;
682   image_control->setDither(b);
683
684   if (! config->getValue(screenstr + "edgeSnapOffset",
685                         resource.snap_offset))
686     resource.snap_offset = 0;
687   if (resource.snap_offset > 50)  // sanity check, setting this huge would
688     resource.snap_offset = 50;    // seriously suck.
689   
690   if (! config->getValue(screenstr + "edgeSnapThreshold",
691                         resource.snap_threshold))
692     resource.snap_threshold = 4;
693   
694   if (! config->getValue(screenstr + "resistanceSize",
695                         resource.resistance_size))
696     resource.resistance_size = 18;
697   
698   if (config->getValue(screenstr + "rowPlacementDirection", s) &&
699       s == "RightToLeft")
700     resource.row_direction = RightLeft;
701   else
702     resource.row_direction = LeftRight;
703
704   if (config->getValue(screenstr + "colPlacementDirection", s) &&
705       s == "BottomToTop")
706     resource.col_direction = BottomTop;
707   else
708     resource.col_direction = TopBottom;
709
710   if (config->getValue(screenstr + "workspaceNames", s)) {
711     XAtom::StringVect workspaceNames;
712
713     string::const_iterator it = s.begin(), end = s.end();
714     while(1) {
715       string::const_iterator tmp = it;     // current string.begin()
716       it = std::find(tmp, end, ',');       // look for comma between tmp and end
717       workspaceNames.push_back(string(tmp, it)); // s[tmp:it]
718       if (it == end)
719         break;
720       ++it;
721     }
722
723     xatom->setValue(getRootWindow(), XAtom::net_desktop_names, XAtom::utf8,
724                     workspaceNames);
725   }
726
727   resource.sloppy_focus = true;
728   resource.auto_raise = false;
729   resource.click_raise = false;
730   if (config->getValue(screenstr + "focusModel", s)) {
731     if (s.find("ClickToFocus") != string::npos) {
732       resource.sloppy_focus = false;
733     } else {
734       // must be sloppy
735       if (s.find("AutoRaise") != string::npos)
736         resource.auto_raise = true;
737       if (s.find("ClickRaise") != string::npos)
738         resource.click_raise = true;
739     }
740   }
741
742   if (config->getValue(screenstr + "windowPlacement", s)) {
743     if (s == "CascadePlacement")
744       resource.placement_policy = CascadePlacement;
745     else if (s == "UnderMousePlacement")
746       resource.placement_policy = UnderMousePlacement;
747     else if (s == "ClickMousePlacement")
748       resource.placement_policy = ClickMousePlacement;
749     else if (s == "ColSmartPlacement")
750       resource.placement_policy = ColSmartPlacement;
751     else //if (s == "RowSmartPlacement")
752       resource.placement_policy = RowSmartPlacement;
753   } else
754     resource.placement_policy = RowSmartPlacement;
755
756 #ifdef    HAVE_STRFTIME
757   if (! config->getValue(screenstr + "strftimeFormat",
758                          resource.strftime_format))
759     resource.strftime_format = "%I:%M %p";
760 #else // !HAVE_STRFTIME
761   long l;
762
763   if (config->getValue(screenstr + "dateFormat", s) && s == "European")
764     resource.date_format = B_EuropeanDate;
765  else
766     resource.date_format = B_AmericanDate;
767
768   if (! config->getValue(screenstr + "clockFormat", l))
769     l = 12;
770   resource.clock24hour = l == 24;
771 #endif // HAVE_STRFTIME
772   
773   if (! config->getValue(screenstr + "placementIgnoreShaded",
774                          resource.ignore_shaded))
775     resource.ignore_shaded = true;
776
777   if (! config->getValue(screenstr + "placementIgnoreMaximized",
778                          resource.ignore_maximized))
779     resource.ignore_maximized = true;
780
781   if (! config->getValue(screenstr + "disableBindingsWithScrollLock",
782                        resource.allow_scroll_lock))
783     resource.allow_scroll_lock = false;
784
785   if (! config->getValue(screenstr + "workspaceWarping",
786                          resource.workspace_warping))
787     resource.workspace_warping = false;
788
789   resource.root_scroll = NormalScroll;
790   if (config->getValue(screenstr + "rootScrollDirection", s)) {
791     if (s == "None")
792       resource.root_scroll = NoScroll;
793     else if (s == "Reverse")
794       resource.root_scroll = ReverseScroll;
795   }
796 }
797
798
799 void BScreen::changeWorkspaceCount(unsigned int new_count) {
800   assert(new_count > 0);
801
802   if (new_count < workspacesList.size()) {
803     // shrink
804     for (unsigned int i = workspacesList.size(); i > new_count; --i)
805       removeLastWorkspace();
806     // removeLast already sets the current workspace to the 
807     // last available one.
808   } else if (new_count > workspacesList.size()) {
809     // grow
810     for(unsigned int i = workspacesList.size(); i < new_count; ++i)
811       addWorkspace();
812   }
813 }
814
815
816 void BScreen::reconfigure(void) {
817   // don't reconfigure while saving the initial rc file, it's a waste and it
818   // breaks somethings (workspace names)
819   if (blackbox->isStartup()) return;
820
821   load_rc();
822   toolbar->load_rc();
823   slit->load_rc();
824   LoadStyle();
825
826   // we need to do this explicitly, because just loading this value from the rc
827   // does nothing
828   changeWorkspaceCount(resource.workspaces);
829
830   XGCValues gcv;
831   gcv.foreground = WhitePixel(blackbox->getXDisplay(),
832                               getScreenNumber());
833   gcv.function = GXinvert;
834   gcv.subwindow_mode = IncludeInferiors;
835   XChangeGC(blackbox->getXDisplay(), opGC,
836             GCForeground | GCFunction | GCSubwindowMode, &gcv);
837
838   const char *s = i18n(ScreenSet, ScreenPositionLength,
839                        "0: 0000 x 0: 0000");
840
841   geom_w = resource.wstyle.font->measureString(s) + resource.bevel_width * 2;
842   geom_h = resource.wstyle.font->height() + resource.bevel_width * 2;
843
844   BTexture* texture = &(resource.wstyle.l_focus);
845   geom_pixmap = texture->render(geom_w, geom_h, geom_pixmap);
846   if (geom_pixmap == ParentRelative) {
847     texture = &(resource.wstyle.t_focus);
848     geom_pixmap = texture->render(geom_w, geom_h, geom_pixmap);
849   }
850   if (! geom_pixmap)
851     XSetWindowBackground(blackbox->getXDisplay(), geom_window,
852                          texture->color().pixel());
853   else
854     XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
855                                geom_window, geom_pixmap);
856
857   XSetWindowBorderWidth(blackbox->getXDisplay(), geom_window,
858                         resource.border_width);
859   XSetWindowBorder(blackbox->getXDisplay(), geom_window,
860                    resource.border_color.pixel());
861
862   workspacemenu->reconfigure();
863   iconmenu->reconfigure();
864
865   typedef std::vector<int> SubList;
866   SubList remember_subs;
867
868   // save the current open menus
869   Basemenu *menu = rootmenu;
870   int submenu;
871   while ((submenu = menu->getCurrentSubmenu()) >= 0) {
872     remember_subs.push_back(submenu);
873     menu = menu->find(submenu)->submenu();
874     assert(menu);
875   }
876   
877   InitMenu();
878   raiseWindows(0, 0);
879   rootmenu->reconfigure();
880
881   // reopen the saved menus
882   menu = rootmenu;
883   const SubList::iterator subs_end = remember_subs.end();
884   for (SubList::iterator it = remember_subs.begin(); it != subs_end; ++it) {
885     menu->drawSubmenu(*it);
886     menu = menu->find(*it)->submenu();
887     if (! menu)
888       break;
889   }
890
891   configmenu->reconfigure();
892
893   toolbar->reconfigure();
894
895   slit->reconfigure();
896
897   std::for_each(workspacesList.begin(), workspacesList.end(),
898                 std::mem_fun(&Workspace::reconfigure));
899
900   BlackboxWindowList::iterator iit = iconList.begin();
901   for (; iit != iconList.end(); ++iit) {
902     BlackboxWindow *bw = *iit;
903     if (bw->validateClient())
904       bw->reconfigure();
905   }
906
907   image_control->timeout();
908 }
909
910
911 void BScreen::rereadMenu(void) {
912   InitMenu();
913   raiseWindows(0, 0);
914
915   rootmenu->reconfigure();
916 }
917
918
919 void BScreen::LoadStyle(void) {
920   Configuration style(False);
921
922   const char *sfile = blackbox->getStyleFilename();
923   if (sfile != NULL) {
924     style.setFile(sfile);
925     if (! style.load()) {
926       style.setFile(DEFAULTSTYLE);
927       if (! style.load())
928         style.create();  // hardcoded default values will be used.
929     }
930   }
931
932   // merge in the rc file
933   style.merge(config->file(), True);
934
935   string s;
936
937   // load fonts/fontsets
938   if (resource.wstyle.font)
939     delete resource.wstyle.font;
940   if (resource.tstyle.font)
941     delete resource.tstyle.font;
942   if (resource.mstyle.f_font)
943     delete resource.mstyle.f_font;
944   if (resource.mstyle.t_font)
945     delete resource.mstyle.t_font;
946   resource.wstyle.font = resource.tstyle.font = resource.mstyle.f_font =
947     resource.mstyle.t_font = (BFont *) 0;
948
949   resource.wstyle.font = readDatabaseFont("window.", style);
950   resource.tstyle.font = readDatabaseFont("toolbar.", style);
951   resource.mstyle.t_font = readDatabaseFont("menu.title.", style);
952   resource.mstyle.f_font = readDatabaseFont("menu.frame.", style);
953
954   // load window config
955   resource.wstyle.t_focus =
956     readDatabaseTexture("window.title.focus", "white", style);
957   resource.wstyle.t_unfocus =
958     readDatabaseTexture("window.title.unfocus", "black", style);
959   resource.wstyle.l_focus =
960     readDatabaseTexture("window.label.focus", "white", style);
961   resource.wstyle.l_unfocus =
962     readDatabaseTexture("window.label.unfocus", "black", style);
963   resource.wstyle.h_focus =
964     readDatabaseTexture("window.handle.focus", "white", style);
965   resource.wstyle.h_unfocus =
966     readDatabaseTexture("window.handle.unfocus", "black", style);
967   resource.wstyle.g_focus =
968     readDatabaseTexture("window.grip.focus", "white", style);
969   resource.wstyle.g_unfocus =
970     readDatabaseTexture("window.grip.unfocus", "black", style);
971   resource.wstyle.b_focus =
972     readDatabaseTexture("window.button.focus", "white", style);
973   resource.wstyle.b_unfocus =
974     readDatabaseTexture("window.button.unfocus", "black", style);
975   resource.wstyle.b_pressed =
976     readDatabaseTexture("window.button.pressed", "black", style);
977
978   // we create the window.frame texture by hand because it exists only to
979   // make the code cleaner and is not actually used for display
980   BColor color = readDatabaseColor("window.frame.focusColor", "white", style);
981   resource.wstyle.f_focus = BTexture("solid flat", getBaseDisplay(),
982                                      getScreenNumber(), image_control);
983   resource.wstyle.f_focus.setColor(color);
984
985   color = readDatabaseColor("window.frame.unfocusColor", "white", style);
986   resource.wstyle.f_unfocus = BTexture("solid flat", getBaseDisplay(),
987                                        getScreenNumber(), image_control);
988   resource.wstyle.f_unfocus.setColor(color);
989
990   resource.wstyle.l_text_focus =
991     readDatabaseColor("window.label.focus.textColor", "black", style);
992   resource.wstyle.l_text_unfocus =
993     readDatabaseColor("window.label.unfocus.textColor", "white", style);
994   resource.wstyle.b_pic_focus =
995     readDatabaseColor("window.button.focus.picColor", "black", style);
996   resource.wstyle.b_pic_unfocus =
997     readDatabaseColor("window.button.unfocus.picColor", "white", style);
998
999   resource.wstyle.justify = LeftJustify;
1000   if (style.getValue("window.justify", s)) {
1001     if (s == "right" || s == "Right")
1002       resource.wstyle.justify = RightJustify;
1003     else if (s == "center" || s == "Center")
1004       resource.wstyle.justify = CenterJustify;
1005   }
1006
1007   // sanity checks
1008   if (resource.wstyle.t_focus.texture() == BTexture::Parent_Relative)
1009     resource.wstyle.t_focus = resource.wstyle.f_focus;
1010   if (resource.wstyle.t_unfocus.texture() == BTexture::Parent_Relative)
1011     resource.wstyle.t_unfocus = resource.wstyle.f_unfocus;
1012   if (resource.wstyle.h_focus.texture() == BTexture::Parent_Relative)
1013     resource.wstyle.h_focus = resource.wstyle.f_focus;
1014   if (resource.wstyle.h_unfocus.texture() == BTexture::Parent_Relative)
1015     resource.wstyle.h_unfocus = resource.wstyle.f_unfocus;
1016
1017 // load toolbar config
1018   resource.tstyle.toolbar =
1019     readDatabaseTexture("toolbar", "black", style);
1020   resource.tstyle.label =
1021     readDatabaseTexture("toolbar.label", "black", style);
1022   resource.tstyle.window =
1023     readDatabaseTexture("toolbar.windowLabel", "black", style);
1024   resource.tstyle.button =
1025     readDatabaseTexture("toolbar.button", "white", style);
1026   resource.tstyle.pressed =
1027     readDatabaseTexture("toolbar.button.pressed", "black", style);
1028   resource.tstyle.clock =
1029     readDatabaseTexture("toolbar.clock", "black", style);
1030   resource.tstyle.l_text =
1031     readDatabaseColor("toolbar.label.textColor", "white", style);
1032   resource.tstyle.w_text =
1033     readDatabaseColor("toolbar.windowLabel.textColor", "white", style);
1034   resource.tstyle.c_text =
1035     readDatabaseColor("toolbar.clock.textColor", "white", style);
1036   resource.tstyle.b_pic =
1037     readDatabaseColor("toolbar.button.picColor", "black", style);
1038
1039   resource.tstyle.justify = LeftJustify;
1040   if (style.getValue("toolbar.justify", s)) {
1041     if (s == "right" || s == "Right")
1042       resource.tstyle.justify = RightJustify;
1043     else if (s == "center" || s == "Center")
1044       resource.tstyle.justify = CenterJustify;
1045   }
1046
1047   // sanity checks
1048   if (resource.tstyle.toolbar.texture() == BTexture::Parent_Relative) {
1049     resource.tstyle.toolbar = BTexture("solid flat", getBaseDisplay(),
1050                                        getScreenNumber(), image_control);
1051     resource.tstyle.toolbar.setColor(BColor("black", getBaseDisplay(),
1052                                             getScreenNumber()));
1053   }
1054
1055   // load menu config
1056   resource.mstyle.title =
1057     readDatabaseTexture("menu.title", "white", style);
1058   resource.mstyle.frame =
1059     readDatabaseTexture("menu.frame", "black", style);
1060   resource.mstyle.hilite =
1061     readDatabaseTexture("menu.hilite", "white", style);
1062   resource.mstyle.t_text =
1063     readDatabaseColor("menu.title.textColor", "black", style);
1064   resource.mstyle.f_text =
1065     readDatabaseColor("menu.frame.textColor", "white", style);
1066   resource.mstyle.d_text =
1067     readDatabaseColor("menu.frame.disableColor", "black", style);
1068   resource.mstyle.h_text =
1069     readDatabaseColor("menu.hilite.textColor", "black", style);
1070
1071   resource.mstyle.t_justify = LeftJustify;
1072   if (style.getValue("menu.title.justify", s)) {
1073     if (s == "right" || s == "Right")
1074       resource.mstyle.t_justify = RightJustify;
1075     else if (s == "center" || s == "Center")
1076       resource.mstyle.t_justify = CenterJustify;
1077   }
1078
1079   resource.mstyle.f_justify = LeftJustify;
1080   if (style.getValue("menu.frame.justify", s)) {
1081     if (s == "right" || s == "Right")
1082       resource.mstyle.f_justify = RightJustify;
1083     else if (s == "center" || s == "Center")
1084       resource.mstyle.f_justify = CenterJustify;
1085   }
1086
1087   resource.mstyle.bullet = Basemenu::Triangle;
1088   if (style.getValue("menu.bullet", s)) {
1089     if (s == "empty" || s == "Empty")
1090       resource.mstyle.bullet = Basemenu::Empty;
1091     else if (s == "square" || s == "Square")
1092       resource.mstyle.bullet = Basemenu::Square;
1093     else if (s == "diamond" || s == "Diamond")
1094       resource.mstyle.bullet = Basemenu::Diamond;
1095   }
1096
1097   resource.mstyle.bullet_pos = Basemenu::Left;
1098   if (style.getValue("menu.bullet.position", s)) {
1099     if (s == "right" || s == "Right")
1100       resource.mstyle.bullet_pos = Basemenu::Right;
1101   }
1102
1103   // sanity checks
1104   if (resource.mstyle.frame.texture() == BTexture::Parent_Relative) {
1105     resource.mstyle.frame = BTexture("solid flat", getBaseDisplay(),
1106                                      getScreenNumber(), image_control);
1107     resource.mstyle.frame.setColor(BColor("black", getBaseDisplay(),
1108                                           getScreenNumber()));
1109   }
1110
1111   resource.border_color =
1112     readDatabaseColor("borderColor", "black", style);
1113
1114   // load bevel, border and handle widths
1115   if (! style.getValue("handleWidth", resource.handle_width) ||
1116       resource.handle_width > (getWidth() / 2) || resource.handle_width == 0)
1117     resource.handle_width = 6;
1118
1119   if (! style.getValue("borderWidth", resource.border_width))
1120     resource.border_width = 1;
1121
1122   if (! style.getValue("bevelWidth", resource.bevel_width) ||
1123       resource.bevel_width > (getWidth() / 2) || resource.bevel_width == 0)
1124     resource.bevel_width = 3;
1125
1126   if (! style.getValue("frameWidth", resource.frame_width) ||
1127       resource.frame_width > (getWidth() / 2))
1128     resource.frame_width = resource.bevel_width;
1129
1130   if (style.getValue("rootCommand", s))
1131     bexec(s, displayString());
1132 }
1133
1134
1135 void BScreen::addIcon(BlackboxWindow *w) {
1136   if (! w) return;
1137
1138   w->setWorkspace(BSENTINEL);
1139   w->setWindowNumber(iconList.size());
1140
1141   iconList.push_back(w);
1142
1143   const char* title = w->getIconTitle();
1144   iconmenu->insert(title);
1145   iconmenu->update();
1146 }
1147
1148
1149 void BScreen::removeIcon(BlackboxWindow *w) {
1150   if (! w) return;
1151
1152   iconList.remove(w);
1153
1154   iconmenu->remove(w->getWindowNumber());
1155   iconmenu->update();
1156
1157   BlackboxWindowList::iterator it = iconList.begin(),
1158     end = iconList.end();
1159   for (int i = 0; it != end; ++it)
1160     (*it)->setWindowNumber(i++);
1161 }
1162
1163
1164 BlackboxWindow *BScreen::getIcon(unsigned int index) {
1165   if (index < iconList.size()) {
1166     BlackboxWindowList::iterator it = iconList.begin();
1167     while (index-- > 0) // increment to index
1168       ++it;
1169     return *it;
1170   }
1171
1172   return (BlackboxWindow *) 0;
1173 }
1174
1175
1176 unsigned int BScreen::addWorkspace(void) {
1177   Workspace *wkspc = new Workspace(this, workspacesList.size());
1178   workspacesList.push_back(wkspc);
1179   saveWorkspaces(getWorkspaceCount());
1180   saveWorkspaceNames();
1181
1182   workspacemenu->insertWorkspace(wkspc);
1183   workspacemenu->update();
1184
1185   toolbar->reconfigure();
1186
1187   updateNetizenWorkspaceCount();
1188
1189   return workspacesList.size();
1190 }
1191
1192
1193 unsigned int BScreen::removeLastWorkspace(void) {
1194   if (workspacesList.size() == 1)
1195     return 1;
1196
1197   Workspace *wkspc = workspacesList.back();
1198
1199   if (current_workspace->getID() == wkspc->getID())
1200     changeWorkspaceID(current_workspace->getID() - 1);
1201
1202   wkspc->removeAll();
1203
1204   workspacemenu->removeWorkspace(wkspc);
1205   workspacemenu->update();
1206
1207   workspacesList.pop_back();
1208   delete wkspc;
1209
1210   saveWorkspaces(getWorkspaceCount());
1211   saveWorkspaceNames();
1212
1213   toolbar->reconfigure();
1214
1215   updateNetizenWorkspaceCount();
1216
1217   return workspacesList.size();
1218 }
1219
1220
1221 void BScreen::changeWorkspaceID(unsigned int id) {
1222   if (! current_workspace || id == current_workspace->getID()) return;
1223
1224   BlackboxWindow *focused = blackbox->getFocusedWindow();
1225   if (focused && focused->getScreen() == this) {
1226     assert(focused->isStuck() ||
1227            focused->getWorkspaceNumber() == current_workspace->getID());
1228
1229     current_workspace->setLastFocusedWindow(focused);
1230   } else {
1231     // if no window had focus, no need to store a last focus
1232     current_workspace->setLastFocusedWindow((BlackboxWindow *) 0);
1233   }
1234
1235   // when we switch workspaces, unfocus whatever was focused if it is going
1236   // to be unmapped
1237   if (focused && ! focused->isStuck())
1238     blackbox->setFocusedWindow((BlackboxWindow *) 0);
1239
1240   current_workspace->hideAll();
1241   workspacemenu->setItemSelected(current_workspace->getID() + 2, False);
1242
1243   current_workspace = getWorkspace(id);
1244
1245   xatom->setValue(getRootWindow(), XAtom::net_current_desktop,
1246                   XAtom::cardinal, id);
1247
1248   workspacemenu->setItemSelected(current_workspace->getID() + 2, True);
1249   toolbar->redrawWorkspaceLabel(True);
1250
1251   current_workspace->showAll();
1252
1253   int x, y, rx, ry;
1254   Window c, r;
1255   unsigned int m;
1256   BlackboxWindow *win = (BlackboxWindow *) 0;
1257   bool f = False;
1258
1259   XSync(blackbox->getXDisplay(), False);
1260
1261   // If sloppy focus and we can find the client window under the pointer,
1262   // try to focus it.  
1263   if (resource.sloppy_focus &&
1264       XQueryPointer(blackbox->getXDisplay(), getRootWindow(), &r, &c,
1265                     &rx, &ry, &x, &y, &m) &&
1266       c != None) {
1267     if ( (win = blackbox->searchWindow(c)) )
1268       f = win->setInputFocus();
1269   }
1270
1271   // If that fails, and we're doing focus_last, try to focus the last window.
1272   if (! f && resource.focus_last &&
1273       (win = current_workspace->getLastFocusedWindow()))
1274     f = win->setInputFocus();
1275
1276   /*
1277     if we found a focus target, then we set the focused window explicitly
1278     because it is possible to switch off this workspace before the x server
1279     generates the FocusIn event for the window. if that happens, openbox would
1280     lose track of what window was the 'LastFocused' window on the workspace.
1281
1282     if we did not find a focus target, then set the current focused window to
1283     nothing.
1284   */
1285   if (f)
1286     blackbox->setFocusedWindow(win);
1287   else
1288     blackbox->setFocusedWindow((BlackboxWindow *) 0);
1289
1290   updateNetizenCurrentWorkspace();
1291 }
1292
1293
1294 /*
1295  * Set the _NET_CLIENT_LIST root window property.
1296  */
1297 void BScreen::updateClientList(void) {
1298   if (windowList.size() > 0) {
1299     Window *windows = new Window[windowList.size()];
1300     Window *win_it = windows;
1301     BlackboxWindowList::iterator it = windowList.begin();
1302     const BlackboxWindowList::iterator end = windowList.end();
1303     for (; it != end; ++it, ++win_it)
1304       *win_it = (*it)->getClientWindow();
1305     xatom->setValue(getRootWindow(), XAtom::net_client_list, XAtom::window,
1306                     windows, windowList.size());
1307     delete [] windows;
1308   } else
1309     xatom->setValue(getRootWindow(), XAtom::net_client_list, XAtom::window,
1310                     0, 0);
1311
1312   updateStackingList();
1313 }
1314
1315
1316 /*
1317  * Set the _NET_CLIENT_LIST_STACKING root window property.
1318  */
1319 void BScreen::updateStackingList(void) {
1320
1321   BlackboxWindowList stack_order;
1322
1323   /*
1324    * Get the stacking order from all of the workspaces.
1325    * We start with the current workspace so that the sticky windows will be
1326    * in the right order on the current workspace.
1327    * XXX: Do we need to have sticky windows in the list once for each workspace?
1328    */
1329   getCurrentWorkspace()->appendStackOrder(stack_order);
1330   for (unsigned int i = 0; i < getWorkspaceCount(); ++i)
1331     if (i != getCurrentWorkspaceID())
1332       getWorkspace(i)->appendStackOrder(stack_order);
1333
1334   if (stack_order.size() > 0) {
1335     // set the client list atoms
1336     Window *windows = new Window[stack_order.size()];
1337     Window *win_it = windows;
1338     BlackboxWindowList::iterator it = stack_order.begin(),
1339                                  end = stack_order.end();
1340     for (; it != end; ++it, ++win_it)
1341       *win_it = (*it)->getClientWindow();
1342     xatom->setValue(getRootWindow(), XAtom::net_client_list_stacking,
1343                     XAtom::window, windows, stack_order.size());
1344     delete [] windows;
1345   } else
1346     xatom->setValue(getRootWindow(), XAtom::net_client_list_stacking,
1347                     XAtom::window, 0, 0);
1348 }
1349
1350
1351 void BScreen::addSystrayWindow(Window window) {
1352   XGrabServer(blackbox->getXDisplay());
1353   
1354   XSelectInput(blackbox->getXDisplay(), window, SubstructureRedirectMask);
1355   systrayWindowList.push_back(window);
1356   xatom->setValue(getRootWindow(), XAtom::kde_net_system_tray_windows,
1357                   XAtom::window,
1358                   &systrayWindowList[0], systrayWindowList.size());
1359   blackbox->saveSystrayWindowSearch(window, this);
1360
1361   XUngrabServer(blackbox->getXDisplay());
1362 }
1363
1364
1365 void BScreen::removeSystrayWindow(Window window) {
1366   XGrabServer(blackbox->getXDisplay());
1367   
1368   WindowList::iterator it = systrayWindowList.begin();
1369   const WindowList::iterator end = systrayWindowList.end();
1370   for (; it != end; ++it)
1371     if (*it == window) {
1372       systrayWindowList.erase(it);
1373       xatom->setValue(getRootWindow(), XAtom::kde_net_system_tray_windows,
1374                       XAtom::window,
1375                       &systrayWindowList[0], systrayWindowList.size());
1376       blackbox->removeSystrayWindowSearch(window);
1377       XSelectInput(blackbox->getXDisplay(), window, NoEventMask);
1378       break;
1379     }
1380
1381   assert(it != end);    // not a systray window
1382
1383   XUngrabServer(blackbox->getXDisplay());
1384 }
1385
1386
1387 void BScreen::manageWindow(Window w) {
1388   // is the window a KDE systray window?
1389   Window systray;
1390   if (xatom->getValue(w, XAtom::kde_net_wm_system_tray_window_for,
1391                       XAtom::window, systray) && systray != None) {
1392     addSystrayWindow(w);
1393     return;
1394   }
1395
1396   // is the window a docking app
1397   XWMHints *wmhint = XGetWMHints(blackbox->getXDisplay(), w);
1398   if (wmhint && (wmhint->flags & StateHint) &&
1399       wmhint->initial_state == WithdrawnState) {
1400     slit->addClient(w);
1401     return;
1402   }
1403
1404   new BlackboxWindow(blackbox, w, this);
1405
1406   BlackboxWindow *win = blackbox->searchWindow(w);
1407   if (! win)
1408     return;
1409
1410   if (win->isDesktop()) {
1411     desktopWindowList.push_back(win->getFrameWindow());
1412   } else { // if (win->isNormal()) {
1413     // don't list desktop windows as managed windows
1414     windowList.push_back(win);
1415     updateClientList();
1416   
1417     if (win->isTopmost())
1418       specialWindowList.push_back(win->getFrameWindow());
1419   }
1420   
1421   XMapRequestEvent mre;
1422   mre.window = w;
1423   if (blackbox->isStartup() && win->isNormal()) win->restoreAttributes();
1424   win->mapRequestEvent(&mre);
1425 }
1426
1427
1428 void BScreen::unmanageWindow(BlackboxWindow *w, bool remap) {
1429   // is the window a KDE systray window?
1430   Window systray;
1431   if (xatom->getValue(w->getClientWindow(),
1432                       XAtom::kde_net_wm_system_tray_window_for,
1433                       XAtom::window, systray) && systray != None) {
1434     removeSystrayWindow(w->getClientWindow());
1435     return;
1436   }
1437
1438   w->restore(remap);
1439
1440   // Remove the modality so that its parent won't try to re-focus the window
1441   if (w->isModal()) w->setModal(False);
1442   
1443   if (w->getWorkspaceNumber() != BSENTINEL &&
1444       w->getWindowNumber() != BSENTINEL) {
1445     getWorkspace(w->getWorkspaceNumber())->removeWindow(w);
1446     if (w->isStuck()) {
1447       for (unsigned int i = 0; i < getNumberOfWorkspaces(); ++i)
1448         if (i != w->getWorkspaceNumber())
1449           getWorkspace(i)->removeWindow(w, True);
1450     }
1451   } else if (w->isIconic())
1452     removeIcon(w);
1453
1454   if (w->isDesktop()) {
1455     WindowList::iterator it = desktopWindowList.begin();
1456     const WindowList::iterator end = desktopWindowList.end();
1457     for (; it != end; ++it)
1458       if (*it == w->getFrameWindow()) {
1459         desktopWindowList.erase(it);
1460         break;
1461       }
1462     assert(it != end);  // the window wasnt a desktop window?
1463   } else { // if (w->isNormal()) {
1464     // we don't list desktop windows as managed windows
1465     windowList.remove(w);
1466     updateClientList();
1467
1468     if (w->isTopmost()) {
1469       WindowList::iterator it = specialWindowList.begin();
1470       const WindowList::iterator end = specialWindowList.end();
1471       for (; it != end; ++it)
1472         if (*it == w->getFrameWindow()) {
1473           specialWindowList.erase(it);
1474           break;
1475         }
1476       assert(it != end);  // the window wasnt a special window?
1477     }
1478   }
1479
1480   if (blackbox->getFocusedWindow() == w)
1481     blackbox->setFocusedWindow((BlackboxWindow *) 0);
1482
1483   removeNetizen(w->getClientWindow());
1484
1485   /*
1486     some managed windows can also be window group controllers.  when
1487     unmanaging such windows, we should also delete the window group.
1488   */
1489   BWindowGroup *group = blackbox->searchGroup(w->getClientWindow());
1490   delete group;
1491
1492   delete w;
1493 }
1494
1495
1496 void BScreen::addNetizen(Netizen *n) {
1497   netizenList.push_back(n);
1498
1499   n->sendWorkspaceCount();
1500   n->sendCurrentWorkspace();
1501
1502   WorkspaceList::iterator it = workspacesList.begin();
1503   const WorkspaceList::iterator end = workspacesList.end();
1504   for (; it != end; ++it)
1505     (*it)->sendWindowList(*n);
1506
1507   Window f = ((blackbox->getFocusedWindow()) ?
1508               blackbox->getFocusedWindow()->getClientWindow() : None);
1509   n->sendWindowFocus(f);
1510 }
1511
1512
1513 void BScreen::removeNetizen(Window w) {
1514   NetizenList::iterator it = netizenList.begin();
1515   for (; it != netizenList.end(); ++it) {
1516     if ((*it)->getWindowID() == w) {
1517       delete *it;
1518       netizenList.erase(it);
1519       break;
1520     }
1521   }
1522 }
1523
1524
1525 void BScreen::updateWorkArea(void) {
1526   if (workspacesList.size() > 0) {
1527     unsigned long *dims = new unsigned long[4 * workspacesList.size()];
1528     for (unsigned int i = 0, m = workspacesList.size(); i < m; ++i) {
1529       // XXX: this could be different for each workspace
1530       const Rect &area = availableArea();
1531       dims[(i * 4) + 0] = area.x();
1532       dims[(i * 4) + 1] = area.y();
1533       dims[(i * 4) + 2] = area.width();
1534       dims[(i * 4) + 3] = area.height();
1535     }
1536     xatom->setValue(getRootWindow(), XAtom::net_workarea, XAtom::cardinal,
1537                     dims, 4 * workspacesList.size());
1538     delete [] dims;
1539   } else
1540     xatom->setValue(getRootWindow(), XAtom::net_workarea, XAtom::cardinal,
1541                     0, 0);
1542 }
1543
1544
1545 void BScreen::updateNetizenCurrentWorkspace(void) {
1546   std::for_each(netizenList.begin(), netizenList.end(),
1547                 std::mem_fun(&Netizen::sendCurrentWorkspace));
1548 }
1549
1550
1551 void BScreen::updateNetizenWorkspaceCount(void) {
1552   xatom->setValue(getRootWindow(), XAtom::net_number_of_desktops,
1553                   XAtom::cardinal, workspacesList.size());
1554
1555   updateWorkArea();
1556   
1557   std::for_each(netizenList.begin(), netizenList.end(),
1558                 std::mem_fun(&Netizen::sendWorkspaceCount));
1559 }
1560
1561
1562 void BScreen::updateNetizenWindowFocus(void) {
1563   Window f = ((blackbox->getFocusedWindow()) ?
1564               blackbox->getFocusedWindow()->getClientWindow() : None);
1565
1566   xatom->setValue(getRootWindow(), XAtom::net_active_window,
1567                   XAtom::window, f);
1568
1569   NetizenList::iterator it = netizenList.begin();
1570   for (; it != netizenList.end(); ++it)
1571     (*it)->sendWindowFocus(f);
1572 }
1573
1574
1575 void BScreen::updateNetizenWindowAdd(Window w, unsigned long p) {
1576   NetizenList::iterator it = netizenList.begin();
1577   for (; it != netizenList.end(); ++it) {
1578     (*it)->sendWindowAdd(w, p);
1579   }
1580 }
1581
1582
1583 void BScreen::updateNetizenWindowDel(Window w) {
1584   NetizenList::iterator it = netizenList.begin();
1585   for (; it != netizenList.end(); ++it)
1586     (*it)->sendWindowDel(w);
1587 }
1588
1589
1590 void BScreen::updateNetizenWindowRaise(Window w) {
1591   NetizenList::iterator it = netizenList.begin();
1592   for (; it != netizenList.end(); ++it)
1593     (*it)->sendWindowRaise(w);
1594 }
1595
1596
1597 void BScreen::updateNetizenWindowLower(Window w) {
1598   NetizenList::iterator it = netizenList.begin();
1599   for (; it != netizenList.end(); ++it)
1600     (*it)->sendWindowLower(w);
1601 }
1602
1603
1604 void BScreen::updateNetizenConfigNotify(XEvent *e) {
1605   NetizenList::iterator it = netizenList.begin();
1606   for (; it != netizenList.end(); ++it)
1607     (*it)->sendConfigNotify(e);
1608 }
1609
1610
1611 void BScreen::raiseWindows(Window *workspace_stack, unsigned int num) {
1612   // the 13 represents the number of blackbox windows such as menus
1613   int bbwins = 15;
1614 #ifdef    XINERAMA
1615   ++bbwins;
1616 #endif // XINERAMA
1617
1618   Window *session_stack = new
1619     Window[(num + workspacesList.size() + rootmenuList.size() +
1620             specialWindowList.size() + bbwins)];
1621   unsigned int i = 0, k = num;
1622
1623   XRaiseWindow(blackbox->getXDisplay(), iconmenu->getWindowID());
1624   *(session_stack + i++) = iconmenu->getWindowID();
1625
1626   WorkspaceList::iterator wit = workspacesList.begin();
1627   const WorkspaceList::iterator w_end = workspacesList.end();
1628   for (; wit != w_end; ++wit)
1629     *(session_stack + i++) = (*wit)->getMenu()->getWindowID();
1630
1631   *(session_stack + i++) = workspacemenu->getWindowID();
1632
1633   *(session_stack + i++) = configmenu->getFocusmenu()->getWindowID();
1634   *(session_stack + i++) = configmenu->getPlacementmenu()->getWindowID();
1635   *(session_stack + i++) = configmenu->getWindowSnapmenu()->getWindowID();
1636   *(session_stack + i++) = configmenu->getEdgeSnapmenu()->getWindowID();
1637 #ifdef    XINERAMA
1638   *(session_stack + i++) = configmenu->getXineramamenu()->getWindowID();
1639 #endif // XINERAMA
1640   *(session_stack + i++) = configmenu->getWindowID();
1641
1642   *(session_stack + i++) = slit->getMenu()->getDirectionmenu()->getWindowID();
1643   *(session_stack + i++) = slit->getMenu()->getPlacementmenu()->getWindowID();
1644   *(session_stack + i++) = slit->getMenu()->getWindowID();
1645
1646   *(session_stack + i++) =
1647     toolbar->getMenu()->getPlacementmenu()->getWindowID();
1648   *(session_stack + i++) = toolbar->getMenu()->getWindowID();
1649
1650   RootmenuList::iterator rit = rootmenuList.begin();
1651   for (; rit != rootmenuList.end(); ++rit)
1652     *(session_stack + i++) = (*rit)->getWindowID();
1653   *(session_stack + i++) = rootmenu->getWindowID();
1654
1655   if (toolbar->isOnTop())
1656     *(session_stack + i++) = toolbar->getWindowID();
1657
1658   if (slit->isOnTop())
1659     *(session_stack + i++) = slit->getWindowID();
1660
1661   WindowList::iterator sit, send = specialWindowList.end();
1662   for (sit = specialWindowList.begin(); sit != send; ++sit)
1663     *(session_stack + i++) = *sit;
1664
1665   while (k--)
1666     *(session_stack + i++) = *(workspace_stack + k);
1667
1668   XRestackWindows(blackbox->getXDisplay(), session_stack, i);
1669
1670   delete [] session_stack;
1671
1672   updateStackingList();
1673 }
1674
1675
1676 void BScreen::lowerWindows(Window *workspace_stack, unsigned int num) {
1677   assert(num > 0);  // this would cause trouble in the XRaiseWindow call
1678
1679   Window *session_stack = new Window[(num + desktopWindowList.size())];
1680   unsigned int i = 0, k = num;
1681
1682   XLowerWindow(blackbox->getXDisplay(), workspace_stack[0]);
1683
1684   while (k--)
1685     *(session_stack + i++) = *(workspace_stack + k);
1686
1687   WindowList::iterator dit = desktopWindowList.begin();
1688   const WindowList::iterator d_end = desktopWindowList.end();
1689   for (; dit != d_end; ++dit)
1690     *(session_stack + i++) = *dit;
1691
1692   XRestackWindows(blackbox->getXDisplay(), session_stack, i);
1693
1694   delete [] session_stack;
1695
1696   updateStackingList();
1697 }
1698
1699
1700 void BScreen::reassociateWindow(BlackboxWindow *w, unsigned int wkspc_id,
1701                                 bool ignore_sticky) {
1702   if (! w) return;
1703
1704   if (wkspc_id == BSENTINEL)
1705     wkspc_id = current_workspace->getID();
1706
1707   if (w->getWorkspaceNumber() == wkspc_id)
1708     return;
1709
1710   if (w->isIconic()) {
1711     removeIcon(w);
1712     getWorkspace(wkspc_id)->addWindow(w);
1713     if (w->isStuck())
1714       for (unsigned int i = 0; i < getNumberOfWorkspaces(); ++i)
1715         if (i != w->getWorkspaceNumber())
1716           getWorkspace(i)->addWindow(w, True);
1717   } else if (ignore_sticky || ! w->isStuck()) {
1718     if (w->isStuck())
1719       w->stick();
1720     getWorkspace(w->getWorkspaceNumber())->removeWindow(w);
1721     getWorkspace(wkspc_id)->addWindow(w);
1722   }
1723   updateStackingList();
1724 }
1725
1726
1727 void BScreen::propagateWindowName(const BlackboxWindow *bw) {
1728   if (bw->isIconic()) {
1729     iconmenu->changeItemLabel(bw->getWindowNumber(), bw->getIconTitle());
1730     iconmenu->update();
1731   }
1732   else {
1733     Clientmenu *clientmenu = getWorkspace(bw->getWorkspaceNumber())->getMenu();
1734     clientmenu->changeItemLabel(bw->getWindowNumber(), bw->getTitle());
1735     clientmenu->update();
1736
1737     if (blackbox->getFocusedWindow() == bw)
1738       toolbar->redrawWindowLabel(True);
1739   }
1740 }
1741
1742
1743 void BScreen::nextFocus(void) {
1744   BlackboxWindow *focused = blackbox->getFocusedWindow(),
1745     *next = focused;
1746
1747   if (focused) {
1748     // if window is not on this screen, ignore it
1749     if (focused->getScreen()->getScreenNumber() != getScreenNumber())
1750       focused = (BlackboxWindow*) 0;
1751   }
1752
1753   if (focused && current_workspace->getCount() > 1) {
1754     // next is the next window to recieve focus, current is a place holder
1755     BlackboxWindow *current;
1756     do {
1757       current = next;
1758       next = current_workspace->getNextWindowInList(current);
1759     } while(! next->setInputFocus() && next != focused);
1760
1761     if (next != focused)
1762       current_workspace->raiseWindow(next);
1763   } else if (current_workspace->getCount() >= 1) {
1764     next = current_workspace->getTopWindowOnStack();
1765
1766     current_workspace->raiseWindow(next);
1767     next->setInputFocus();
1768   }
1769 }
1770
1771
1772 void BScreen::prevFocus(void) {
1773   BlackboxWindow *focused = blackbox->getFocusedWindow(),
1774     *next = focused;
1775
1776   if (focused) {
1777     // if window is not on this screen, ignore it
1778     if (focused->getScreen()->getScreenNumber() != getScreenNumber())
1779       focused = (BlackboxWindow*) 0;
1780   }
1781
1782   if (focused && current_workspace->getCount() > 1) {
1783     // next is the next window to recieve focus, current is a place holder
1784     BlackboxWindow *current;
1785     do {
1786       current = next;
1787       next = current_workspace->getPrevWindowInList(current);
1788     } while(! next->setInputFocus() && next != focused);
1789
1790     if (next != focused)
1791       current_workspace->raiseWindow(next);
1792   } else if (current_workspace->getCount() >= 1) {
1793     next = current_workspace->getTopWindowOnStack();
1794
1795     current_workspace->raiseWindow(next);
1796     next->setInputFocus();
1797   }
1798 }
1799
1800
1801 void BScreen::raiseFocus(void) {
1802   BlackboxWindow *focused = blackbox->getFocusedWindow();
1803   if (! focused)
1804     return;
1805
1806   // if on this Screen, raise it
1807   if (focused->getScreen()->getScreenNumber() == getScreenNumber()) {
1808     Workspace *workspace = getWorkspace(focused->getWorkspaceNumber());
1809     workspace->raiseWindow(focused);
1810   }
1811 }
1812
1813
1814 void BScreen::InitMenu(void) {
1815   if (rootmenu) {
1816     rootmenuList.clear();
1817
1818     while (rootmenu->getCount())
1819       rootmenu->remove(0);
1820   } else {
1821     rootmenu = new Rootmenu(this);
1822   }
1823   bool defaultMenu = True;
1824
1825   FILE *menu_file = (FILE *) 0;
1826   const char *menu_filename = blackbox->getMenuFilename();
1827
1828   if (menu_filename) 
1829     if (! (menu_file = fopen(menu_filename, "r")))
1830       perror(menu_filename);
1831   if (! menu_file) {     // opening the menu file failed, try the default menu
1832     menu_filename = DEFAULTMENU;
1833     if (! (menu_file = fopen(menu_filename, "r")))
1834       perror(menu_filename);
1835   } 
1836
1837   if (menu_file) {
1838     if (feof(menu_file)) {
1839       fprintf(stderr, i18n(ScreenSet, ScreenEmptyMenuFile,
1840                            "%s: Empty menu file"),
1841               menu_filename);
1842     } else {
1843       char line[1024], label[1024];
1844       memset(line, 0, 1024);
1845       memset(label, 0, 1024);
1846
1847       while (fgets(line, 1024, menu_file) && ! feof(menu_file)) {
1848         if (line[0] == '#')
1849           continue;
1850
1851         int i, key = 0, index = -1, len = strlen(line);
1852
1853         for (i = 0; i < len; i++) {
1854           if (line[i] == '[') index = 0;
1855           else if (line[i] == ']') break;
1856           else if (line[i] != ' ')
1857             if (index++ >= 0)
1858               key += tolower(line[i]);
1859         }
1860
1861         if (key == 517) { // [begin]
1862           index = -1;
1863           for (i = index; i < len; i++) {
1864             if (line[i] == '(') index = 0;
1865             else if (line[i] == ')') break;
1866             else if (index++ >= 0) {
1867               if (line[i] == '\\' && i < len - 1) i++;
1868               label[index - 1] = line[i];
1869             }
1870           }
1871
1872           if (index == -1) index = 0;
1873           label[index] = '\0';
1874
1875           rootmenu->setLabel(label);
1876           defaultMenu = parseMenuFile(menu_file, rootmenu);
1877           if (! defaultMenu)
1878             blackbox->addMenuTimestamp(menu_filename);
1879           break;
1880         }
1881       }
1882     }
1883     fclose(menu_file);
1884   }
1885
1886   if (defaultMenu) {
1887     rootmenu->setInternalMenu();
1888     rootmenu->insert(i18n(ScreenSet, Screenxterm, "xterm"),
1889                      BScreen::Execute,
1890                      i18n(ScreenSet, Screenxterm, "xterm"));
1891     rootmenu->insert(i18n(ScreenSet, ScreenRestart, "Restart"),
1892                      BScreen::Restart);
1893     rootmenu->insert(i18n(ScreenSet, ScreenExit, "Exit"),
1894                      BScreen::Exit);
1895     rootmenu->setLabel(i18n(BasemenuSet, BasemenuBlackboxMenu,
1896                             "Openbox Menu"));
1897   }
1898 }
1899
1900
1901 static
1902 size_t string_within(char begin, char end,
1903                      const char *input, size_t start_at, size_t length,
1904                      char *output) {
1905   bool parse = False;
1906   size_t index = 0;
1907   size_t i = start_at;
1908   for (; i < length; ++i) {
1909     if (input[i] == begin) {
1910       parse = True;
1911     } else if (input[i] == end) {
1912       break;
1913     } else if (parse) {
1914       if (input[i] == '\\' && i < length - 1) i++;
1915       output[index++] = input[i];
1916     } 
1917   }
1918
1919   if (parse)
1920     output[index] = '\0';
1921   else
1922     output[0] = '\0';
1923
1924   return i;
1925 }
1926
1927
1928 bool BScreen::parseMenuFile(FILE *file, Rootmenu *menu) {
1929   char line[1024], keyword[1024], label[1024], command[1024];
1930   bool done = False;
1931
1932   while (! (done || feof(file))) {
1933     memset(line, 0, 1024);
1934     memset(label, 0, 1024);
1935     memset(command, 0, 1024);
1936
1937     if (! fgets(line, 1024, file))
1938       continue;
1939
1940     if (line[0] == '#') // comment, skip it
1941       continue;
1942
1943     size_t line_length = strlen(line);
1944     unsigned int key = 0;
1945
1946     // get the keyword enclosed in []'s
1947     size_t pos = string_within('[', ']', line, 0, line_length, keyword);
1948
1949     if (keyword[0] == '\0') {  // no keyword, no menu entry
1950       continue;
1951     } else {
1952       size_t len = strlen(keyword);
1953       for (size_t i = 0; i < len; ++i) {
1954         if (keyword[i] != ' ')
1955           key += tolower(keyword[i]);
1956       }
1957     }
1958
1959     // get the label enclosed in ()'s
1960     pos = string_within('(', ')', line, pos, line_length, label);
1961
1962     // get the command enclosed in {}'s
1963     pos = string_within('{', '}', line, pos, line_length, command);
1964
1965     switch (key) {
1966     case 311: // end
1967       done = True;
1968
1969       break;
1970
1971     case 333: // nop
1972       if (! *label)
1973         label[0] = '\0';
1974       menu->insert(label);
1975
1976       break;
1977
1978     case 421: // exec
1979       if (! (*label && *command)) {
1980         fprintf(stderr, i18n(ScreenSet, ScreenEXECError,
1981                              "BScreen::parseMenuFile: [exec] error, "
1982                              "no menu label and/or command defined\n"));
1983         continue;
1984       }
1985
1986       menu->insert(label, BScreen::Execute, command);
1987
1988       break;
1989
1990     case 442: // exit
1991       if (! *label) {
1992         fprintf(stderr, i18n(ScreenSet, ScreenEXITError,
1993                              "BScreen::parseMenuFile: [exit] error, "
1994                              "no menu label defined\n"));
1995         continue;
1996       }
1997
1998       menu->insert(label, BScreen::Exit);
1999
2000       break;
2001
2002     case 561: { // style
2003       if (! (*label && *command)) {
2004         fprintf(stderr,
2005                 i18n(ScreenSet, ScreenSTYLEError,
2006                      "BScreen::parseMenuFile: [style] error, "
2007                      "no menu label and/or filename defined\n"));
2008         continue;
2009       }
2010
2011       string style = expandTilde(command);
2012
2013       menu->insert(label, BScreen::SetStyle, style.c_str());
2014     }
2015       break;
2016
2017     case 630: // config
2018       if (! *label) {
2019         fprintf(stderr, i18n(ScreenSet, ScreenCONFIGError,
2020                              "BScreen::parseMenufile: [config] error, "
2021                              "no label defined"));
2022         continue;
2023       }
2024
2025       menu->insert(label, configmenu);
2026
2027       break;
2028
2029     case 740: { // include
2030       if (! *label) {
2031         fprintf(stderr, i18n(ScreenSet, ScreenINCLUDEError,
2032                              "BScreen::parseMenuFile: [include] error, "
2033                              "no filename defined\n"));
2034         continue;
2035       }
2036
2037       string newfile = expandTilde(label);
2038       FILE *submenufile = fopen(newfile.c_str(), "r");
2039
2040       if (! submenufile) {
2041         perror(newfile.c_str());
2042         continue;
2043       }
2044
2045       struct stat buf;
2046       if (fstat(fileno(submenufile), &buf) ||
2047           ! S_ISREG(buf.st_mode)) {
2048         fprintf(stderr,
2049                 i18n(ScreenSet, ScreenINCLUDEErrorReg,
2050                      "BScreen::parseMenuFile: [include] error: "
2051                      "'%s' is not a regular file\n"), newfile.c_str());
2052         break;
2053       }
2054
2055       if (! feof(submenufile)) {
2056         if (! parseMenuFile(submenufile, menu))
2057           blackbox->addMenuTimestamp(newfile);
2058
2059         fclose(submenufile);
2060       }
2061     }
2062
2063       break;
2064
2065     case 767: { // submenu
2066       if (! *label) {
2067         fprintf(stderr, i18n(ScreenSet, ScreenSUBMENUError,
2068                              "BScreen::parseMenuFile: [submenu] error, "
2069                              "no menu label defined\n"));
2070         continue;
2071       }
2072
2073       Rootmenu *submenu = new Rootmenu(this);
2074
2075       if (*command)
2076         submenu->setLabel(command);
2077       else
2078         submenu->setLabel(label);
2079
2080       parseMenuFile(file, submenu);
2081       submenu->update();
2082       menu->insert(label, submenu);
2083       rootmenuList.push_back(submenu);
2084     }
2085
2086       break;
2087
2088     case 773: { // restart
2089       if (! *label) {
2090         fprintf(stderr, i18n(ScreenSet, ScreenRESTARTError,
2091                              "BScreen::parseMenuFile: [restart] error, "
2092                              "no menu label defined\n"));
2093         continue;
2094       }
2095
2096       if (*command)
2097         menu->insert(label, BScreen::RestartOther, command);
2098       else
2099         menu->insert(label, BScreen::Restart);
2100     }
2101
2102       break;
2103
2104     case 845: { // reconfig
2105       if (! *label) {
2106         fprintf(stderr,
2107                 i18n(ScreenSet, ScreenRECONFIGError,
2108                      "BScreen::parseMenuFile: [reconfig] error, "
2109                      "no menu label defined\n"));
2110         continue;
2111       }
2112
2113       menu->insert(label, BScreen::Reconfigure);
2114     }
2115
2116       break;
2117
2118     case 995:    // stylesdir
2119     case 1113: { // stylesmenu
2120       bool newmenu = ((key == 1113) ? True : False);
2121
2122       if (! *label || (! *command && newmenu)) {
2123         fprintf(stderr,
2124                 i18n(ScreenSet, ScreenSTYLESDIRError,
2125                      "BScreen::parseMenuFile: [stylesdir/stylesmenu]"
2126                      " error, no directory defined\n"));
2127         continue;
2128       }
2129
2130       char *directory = ((newmenu) ? command : label);
2131
2132       string stylesdir = expandTilde(directory);
2133
2134       struct stat statbuf;
2135
2136       if (stat(stylesdir.c_str(), &statbuf) == -1) {
2137         fprintf(stderr,
2138                 i18n(ScreenSet, ScreenSTYLESDIRErrorNoExist,
2139                      "BScreen::parseMenuFile: [stylesdir/stylesmenu]"
2140                      " error, %s does not exist\n"), stylesdir.c_str());
2141         continue;
2142       }
2143       if (! S_ISDIR(statbuf.st_mode)) {
2144         fprintf(stderr,
2145                 i18n(ScreenSet, ScreenSTYLESDIRErrorNotDir,
2146                      "BScreen::parseMenuFile:"
2147                      " [stylesdir/stylesmenu] error, %s is not a"
2148                      " directory\n"), stylesdir.c_str());
2149         continue;
2150       }
2151
2152       Rootmenu *stylesmenu;
2153
2154       if (newmenu)
2155         stylesmenu = new Rootmenu(this);
2156       else
2157         stylesmenu = menu;
2158
2159       DIR *d = opendir(stylesdir.c_str());
2160       struct dirent *p;
2161       std::vector<string> ls;
2162
2163       while((p = readdir(d)))
2164         ls.push_back(p->d_name);
2165
2166       closedir(d);
2167
2168       std::sort(ls.begin(), ls.end());
2169
2170       std::vector<string>::iterator it = ls.begin(),
2171         end = ls.end();
2172       for (; it != end; ++it) {
2173         const string& fname = *it;
2174
2175         if (fname[fname.size()-1] == '~')
2176           continue;
2177
2178         string style = stylesdir;
2179         style += '/';
2180         style += fname;
2181
2182         if (! stat(style.c_str(), &statbuf) && S_ISREG(statbuf.st_mode))
2183           stylesmenu->insert(fname, BScreen::SetStyle, style);
2184       }
2185
2186       stylesmenu->update();
2187
2188       if (newmenu) {
2189         stylesmenu->setLabel(label);
2190         menu->insert(label, stylesmenu);
2191         rootmenuList.push_back(stylesmenu);
2192       }
2193
2194       blackbox->addMenuTimestamp(stylesdir);
2195     }
2196       break;
2197
2198     case 1090: { // workspaces
2199       if (! *label) {
2200         fprintf(stderr,
2201                 i18n(ScreenSet, ScreenWORKSPACESError,
2202                      "BScreen:parseMenuFile: [workspaces] error, "
2203                      "no menu label defined\n"));
2204         continue;
2205       }
2206
2207       menu->insert(label, workspacemenu);
2208     }
2209       break;
2210     }
2211   }
2212
2213   return ((menu->getCount() == 0) ? True : False);
2214 }
2215
2216
2217 void BScreen::shutdown(void) {
2218   XSelectInput(blackbox->getXDisplay(), getRootWindow(), NoEventMask);
2219   XSync(blackbox->getXDisplay(), False);
2220
2221   while(! windowList.empty())
2222     unmanageWindow(windowList.front(), True);
2223
2224   while(! desktopWindowList.empty()) {
2225     BlackboxWindow *win = blackbox->searchWindow(desktopWindowList.front());
2226     assert(win);
2227     unmanageWindow(win, True);
2228   }
2229
2230   slit->shutdown();
2231 }
2232
2233
2234 void BScreen::showPosition(int x, int y) {
2235   if (! geom_visible) {
2236     XMoveResizeWindow(blackbox->getXDisplay(), geom_window,
2237                       (getWidth() - geom_w) / 2,
2238                       (getHeight() - geom_h) / 2, geom_w, geom_h);
2239     XMapWindow(blackbox->getXDisplay(), geom_window);
2240     XRaiseWindow(blackbox->getXDisplay(), geom_window);
2241
2242     geom_visible = True;
2243   }
2244
2245   char label[1024];
2246
2247   sprintf(label, i18n(ScreenSet, ScreenPositionFormat,
2248                       "X: %4d x Y: %4d"), x, y);
2249
2250   XClearWindow(blackbox->getXDisplay(), geom_window);
2251
2252   resource.wstyle.font->drawString(geom_window,
2253                                    resource.bevel_width, resource.bevel_width,
2254                                    resource.wstyle.l_text_focus,
2255                                    label);
2256 }
2257
2258
2259 void BScreen::showGeometry(unsigned int gx, unsigned int gy) {
2260   if (! geom_visible) {
2261     XMoveResizeWindow(blackbox->getXDisplay(), geom_window,
2262                       (getWidth() - geom_w) / 2,
2263                       (getHeight() - geom_h) / 2, geom_w, geom_h);
2264     XMapWindow(blackbox->getXDisplay(), geom_window);
2265     XRaiseWindow(blackbox->getXDisplay(), geom_window);
2266
2267     geom_visible = True;
2268   }
2269
2270   char label[1024];
2271
2272   sprintf(label, i18n(ScreenSet, ScreenGeometryFormat,
2273                       "W: %4d x H: %4d"), gx, gy);
2274
2275   XClearWindow(blackbox->getXDisplay(), geom_window);
2276
2277   resource.wstyle.font->drawString(geom_window,
2278                                    resource.bevel_width, resource.bevel_width,
2279                                    resource.wstyle.l_text_focus,
2280                                    label);
2281 }
2282
2283
2284 void BScreen::hideGeometry(void) {
2285   if (geom_visible) {
2286     XUnmapWindow(blackbox->getXDisplay(), geom_window);
2287     geom_visible = False;
2288   }
2289 }
2290
2291
2292 void BScreen::addStrut(Strut *strut) {
2293   strutList.push_back(strut);
2294 }
2295
2296
2297 void BScreen::removeStrut(Strut *strut) {
2298   strutList.remove(strut);
2299 }
2300
2301
2302 const Rect& BScreen::availableArea(void) const {
2303   if (doFullMax())
2304     return getRect(); // return the full screen
2305   return usableArea;
2306 }
2307
2308
2309 #ifdef    XINERAMA
2310 const RectList& BScreen::allAvailableAreas(void) const {
2311   assert(isXineramaActive());
2312   assert(xineramaUsableArea.size() > 0);
2313   fprintf(stderr, "1found x %d y %d w %d h %d\n",
2314           xineramaUsableArea[0].x(), xineramaUsableArea[0].y(),
2315           xineramaUsableArea[0].width(), xineramaUsableArea[0].height());
2316   return xineramaUsableArea;
2317 }
2318 #endif // XINERAMA
2319
2320
2321 void BScreen::updateAvailableArea(void) {
2322   Rect old_area = usableArea;
2323   usableArea = getRect(); // reset to full screen
2324
2325 #ifdef    XINERAMA
2326   // reset to the full areas
2327   if (isXineramaActive())
2328     xineramaUsableArea = getXineramaAreas();
2329 #endif // XINERAMA
2330
2331   /* these values represent offsets from the screen edge
2332    * we look for the biggest offset on each edge and then apply them
2333    * all at once
2334    * do not be confused by the similarity to the names of Rect's members
2335    */
2336   unsigned int current_left = 0, current_right = 0, current_top = 0,
2337     current_bottom = 0;
2338
2339   StrutList::const_iterator it = strutList.begin(), end = strutList.end();
2340
2341   for(; it != end; ++it) {
2342     Strut *strut = *it;
2343     if (strut->left > current_left)
2344       current_left = strut->left;
2345     if (strut->top > current_top)
2346       current_top = strut->top;
2347     if (strut->right > current_right)
2348       current_right = strut->right;
2349     if (strut->bottom > current_bottom)
2350       current_bottom = strut->bottom;
2351   }
2352
2353   usableArea.setPos(current_left, current_top);
2354   usableArea.setSize(usableArea.width() - (current_left + current_right),
2355                      usableArea.height() - (current_top + current_bottom));
2356
2357 #ifdef    XINERAMA
2358   if (isXineramaActive()) {
2359     // keep each of the ximerama-defined areas inside the strut
2360     RectList::iterator xit, xend = xineramaUsableArea.end();
2361     for (xit = xineramaUsableArea.begin(); xit != xend; ++xit) {
2362       if (xit->x() < usableArea.x()) {
2363         xit->setX(usableArea.x());
2364         xit->setWidth(xit->width() - usableArea.x());
2365       }
2366       if (xit->y() < usableArea.y()) {
2367         xit->setY(usableArea.y());
2368         xit->setHeight(xit->height() - usableArea.y());
2369       }
2370       if (xit->x() + xit->width() > usableArea.width())
2371         xit->setWidth(usableArea.width() - xit->x());
2372       if (xit->y() + xit->height() > usableArea.height())
2373         xit->setHeight(usableArea.height() - xit->y());
2374     }
2375   }
2376 #endif // XINERAMA
2377
2378   if (old_area != usableArea) {
2379     BlackboxWindowList::iterator it = windowList.begin(),
2380       end = windowList.end();
2381     for (; it != end; ++it)
2382       if ((*it)->isMaximized()) (*it)->remaximize();
2383   }
2384
2385   updateWorkArea();  
2386 }
2387
2388
2389 Workspace* BScreen::getWorkspace(unsigned int index) {
2390   assert(index < workspacesList.size());
2391   return workspacesList[index];
2392 }
2393
2394
2395 void BScreen::buttonPressEvent(const XButtonEvent *xbutton) {
2396   if (xbutton->button == 1) {
2397     if (! isRootColormapInstalled())
2398       image_control->installRootColormap();
2399
2400     if (workspacemenu->isVisible())
2401       workspacemenu->hide();
2402
2403     if (rootmenu->isVisible())
2404       rootmenu->hide();
2405   } else if (xbutton->button == 2) {
2406     showWorkspaceMenu(xbutton->x_root, xbutton->y_root);
2407   } else if (xbutton->button == 3) {
2408     showRootMenu(xbutton->x_root, xbutton->y_root);
2409   // mouse wheel up
2410   } else if ((xbutton->button == 4 && resource.root_scroll == NormalScroll) ||
2411              (xbutton->button == 5 && resource.root_scroll == ReverseScroll)) {
2412     if (getCurrentWorkspaceID() >= getWorkspaceCount() - 1)
2413       changeWorkspaceID(0);
2414     else
2415       changeWorkspaceID(getCurrentWorkspaceID() + 1);
2416   // mouse wheel down
2417   } else if ((xbutton->button == 5 && resource.root_scroll == NormalScroll) ||
2418              (xbutton->button == 4 && resource.root_scroll == ReverseScroll)) {
2419     if (getCurrentWorkspaceID() == 0)
2420       changeWorkspaceID(getWorkspaceCount() - 1);
2421     else
2422       changeWorkspaceID(getCurrentWorkspaceID() - 1);
2423   }
2424 }
2425
2426
2427 void BScreen::showWorkspaceMenu(int x, int y) {
2428   int mx = x - (workspacemenu->getWidth() / 2);
2429   int my = y - (workspacemenu->getTitleHeight() / 2);
2430
2431   if (mx < 0) mx = 0;
2432   if (my < 0) my = 0;
2433
2434   if (mx + workspacemenu->getWidth() > getWidth())
2435     mx = getWidth() - workspacemenu->getWidth() - getBorderWidth();
2436
2437   if (my + workspacemenu->getHeight() > getHeight())
2438     my = getHeight() - workspacemenu->getHeight() - getBorderWidth();
2439
2440   workspacemenu->move(mx, my);
2441
2442   if (! workspacemenu->isVisible()) {
2443     workspacemenu->removeParent();
2444     workspacemenu->show();
2445   }
2446 }
2447
2448
2449 void BScreen::showRootMenu(int x, int y) {
2450   int mx = x - (rootmenu->getWidth() / 2);
2451   int my = y - (rootmenu->getTitleHeight() / 2);
2452
2453   if (mx < 0) mx = 0;
2454   if (my < 0) my = 0;
2455
2456   if (mx + rootmenu->getWidth() > getWidth())
2457     mx = getWidth() - rootmenu->getWidth() - getBorderWidth();
2458
2459   if (my + rootmenu->getHeight() > getHeight())
2460     my = getHeight() - rootmenu->getHeight() - getBorderWidth();
2461
2462   rootmenu->move(mx, my);
2463
2464   if (! rootmenu->isVisible()) {
2465     blackbox->checkMenu();
2466     rootmenu->show();
2467   }
2468 }
2469
2470
2471 void BScreen::propertyNotifyEvent(const XPropertyEvent *pe) {
2472   if (pe->atom == xatom->getAtom(XAtom::net_desktop_names)) {
2473     // _NET_WM_DESKTOP_NAMES
2474     WorkspaceList::iterator it = workspacesList.begin();
2475     const WorkspaceList::iterator end = workspacesList.end();
2476     for (; it != end; ++it) {
2477       (*it)->readName(); // re-read its name from the window property
2478       workspacemenu->changeWorkspaceLabel((*it)->getID(), (*it)->getName());
2479     }
2480     workspacemenu->update();
2481     toolbar->reconfigure();
2482     saveWorkspaceNames();
2483   }
2484 }
2485
2486
2487 void BScreen::toggleFocusModel(FocusModel model) {
2488   std::for_each(windowList.begin(), windowList.end(),
2489                 std::mem_fun(&BlackboxWindow::ungrabButtons));
2490
2491   if (model == SloppyFocus) {
2492     saveSloppyFocus(True);
2493   } else {
2494     // we're cheating here to save writing the config file 3 times
2495     resource.auto_raise = False;
2496     resource.click_raise = False;
2497     saveSloppyFocus(False);
2498   }
2499
2500   std::for_each(windowList.begin(), windowList.end(),
2501                 std::mem_fun(&BlackboxWindow::grabButtons));
2502 }
2503
2504
2505 BTexture BScreen::readDatabaseTexture(const string &rname,
2506                                       const string &default_color,
2507                                       const Configuration &style) {
2508   BTexture texture;
2509   string s;
2510
2511   if (style.getValue(rname, s))
2512     texture = BTexture(s);
2513   else
2514     texture.setTexture(BTexture::Solid | BTexture::Flat);
2515
2516   // associate this texture with this screen
2517   texture.setDisplay(getBaseDisplay(), getScreenNumber());
2518   texture.setImageControl(image_control);
2519
2520   texture.setColor(readDatabaseColor(rname + ".color", default_color, style));
2521   texture.setColorTo(readDatabaseColor(rname + ".colorTo", default_color,
2522                                        style));
2523   
2524   return texture;
2525 }
2526
2527
2528 BColor BScreen::readDatabaseColor(const string &rname,
2529                                   const string &default_color,
2530                                   const Configuration &style) {
2531   BColor color;
2532   string s;
2533   if (style.getValue(rname, s))
2534     color = BColor(s, getBaseDisplay(), getScreenNumber());
2535   else
2536     color = BColor(default_color, getBaseDisplay(), getScreenNumber());
2537   return color;
2538 }
2539
2540
2541 BFont *BScreen::readDatabaseFont(const string &rbasename,
2542                                  const Configuration &style) {
2543   string fontname;
2544
2545   string s;
2546
2547 #ifdef    XFT
2548   int i;
2549   if (style.getValue(rbasename + "xft.font", s) &&
2550       style.getValue(rbasename + "xft.size", i)) {
2551     string family = s;
2552     bool bold = False;
2553     bool italic = False;
2554     if (style.getValue(rbasename + "xft.flags", s)) {
2555       if (s.find("bold") != string::npos)
2556         bold = True;
2557       if (s.find("italic") != string::npos)
2558         italic = True;
2559     }
2560     
2561     BFont *b = new BFont(blackbox->getXDisplay(), this, family, i, bold,
2562                          italic, resource.aa_fonts);
2563     if (b->valid())
2564       return b;
2565     else
2566       delete b; // fall back to the normal X font stuff
2567   }
2568 #endif // XFT
2569
2570   style.getValue(rbasename + "font", s);
2571   // if this fails, a blank string will be used, which will cause the fallback
2572   // font to load.
2573
2574   BFont *b = new BFont(blackbox->getXDisplay(), this, s);
2575   if (! b->valid())
2576     exit(2);  // can't continue without a font
2577   return b;
2578 }