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