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