fixed the geometry window not getting the proper pixmap. This was caused by using...
[mikachu/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 // for strcasestr()
33 #ifndef _GNU_SOURCE
34 #  define   _GNU_SOURCE
35 #endif // _GNU_SOURCE
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 <algorithm>
72 #include <functional>
73 using std::string;
74
75 #include "i18n.hh"
76 #include "blackbox.hh"
77 #include "Clientmenu.hh"
78 #include "GCCache.hh"
79 #include "Iconmenu.hh"
80 #include "Image.hh"
81 #include "Screen.hh"
82 #include "Slit.hh"
83 #include "Rootmenu.hh"
84 #include "Toolbar.hh"
85 #include "Util.hh"
86 #include "Window.hh"
87 #include "Workspace.hh"
88 #include "Workspacemenu.hh"
89
90 #ifndef   FONT_ELEMENT_SIZE
91 #define   FONT_ELEMENT_SIZE 50
92 #endif // FONT_ELEMENT_SIZE
93
94
95 static bool running = True;
96
97 static int anotherWMRunning(Display *display, XErrorEvent *) {
98   fprintf(stderr, i18n(ScreenSet, ScreenAnotherWMRunning,
99           "BScreen::BScreen: an error occured while querying the X server.\n"
100           "  another window manager already running on display %s.\n"),
101           DisplayString(display));
102
103   running = False;
104
105   return(-1);
106 }
107
108
109 BScreen::BScreen(Blackbox *bb, unsigned int scrn) : ScreenInfo(bb, scrn) {
110   blackbox = bb;
111
112   event_mask = ColormapChangeMask | EnterWindowMask | PropertyChangeMask |
113     SubstructureRedirectMask | ButtonPressMask | ButtonReleaseMask;
114
115   XErrorHandler old = XSetErrorHandler((XErrorHandler) anotherWMRunning);
116   XSelectInput(getBaseDisplay()->getXDisplay(), getRootWindow(), event_mask);
117   XSync(getBaseDisplay()->getXDisplay(), False);
118   XSetErrorHandler((XErrorHandler) old);
119
120   managed = running;
121   if (! managed) return;
122
123   fprintf(stderr, i18n(ScreenSet, ScreenManagingScreen,
124                        "BScreen::BScreen: managing screen %d "
125                        "using visual 0x%lx, depth %d\n"),
126           getScreenNumber(), XVisualIDFromVisual(getVisual()),
127           getDepth());
128
129   rootmenu = 0;
130   resource.stylerc = 0;
131
132   resource.mstyle.t_fontset = resource.mstyle.f_fontset =
133     resource.tstyle.fontset = resource.wstyle.fontset = (XFontSet) 0;
134   resource.mstyle.t_font = resource.mstyle.f_font = resource.tstyle.font =
135     resource.wstyle.font = (XFontStruct *) 0;
136
137 #ifdef    HAVE_GETPID
138   pid_t bpid = getpid();
139
140   XChangeProperty(blackbox->getXDisplay(), getRootWindow(),
141                   blackbox->getBlackboxPidAtom(), XA_CARDINAL,
142                   sizeof(pid_t) * 8, PropModeReplace,
143                   (unsigned char *) &bpid, 1);
144 #endif // HAVE_GETPID
145
146   XDefineCursor(blackbox->getXDisplay(), getRootWindow(),
147                 blackbox->getSessionCursor());
148
149   // start off full screen, top left.
150   usableArea.setSize(getWidth(), getHeight());
151
152   image_control =
153     new BImageControl(blackbox, this, True, blackbox->getColorsPerChannel(),
154                       blackbox->getCacheLife(), blackbox->getCacheMax());
155   image_control->installRootColormap();
156   root_colormap_installed = True;
157
158   blackbox->load_rc(this);
159
160   image_control->setDither(resource.image_dither);
161
162   LoadStyle();
163
164   XGCValues gcv;
165   unsigned long gc_value_mask = GCForeground;
166   if (! i18n.multibyte()) gc_value_mask |= GCFont;
167
168   gcv.foreground = WhitePixel(blackbox->getXDisplay(), getScreenNumber())
169     ^ BlackPixel(blackbox->getXDisplay(), getScreenNumber());
170   gcv.function = GXxor;
171   gcv.subwindow_mode = IncludeInferiors;
172   opGC = XCreateGC(blackbox->getXDisplay(), getRootWindow(),
173                    GCForeground | GCFunction | GCSubwindowMode, &gcv);
174
175   const char *s =  i18n(ScreenSet, ScreenPositionLength,
176                         "0: 0000 x 0: 0000");
177   int l = strlen(s);
178
179   if (i18n.multibyte()) {
180     XRectangle ink, logical;
181     XmbTextExtents(resource.wstyle.fontset, s, l, &ink, &logical);
182     geom_w = logical.width;
183
184     geom_h = resource.wstyle.fontset_extents->max_ink_extent.height;
185   } else {
186     geom_h = resource.wstyle.font->ascent +
187       resource.wstyle.font->descent;
188
189     geom_w = XTextWidth(resource.wstyle.font, s, l);
190   }
191
192   geom_w += (resource.bevel_width * 2);
193   geom_h += (resource.bevel_width * 2);
194
195   XSetWindowAttributes attrib;
196   unsigned long mask = CWBorderPixel | CWColormap | CWSaveUnder;
197   attrib.border_pixel = getBorderColor()->pixel();
198   attrib.colormap = getColormap();
199   attrib.save_under = True;
200
201   geom_window = XCreateWindow(blackbox->getXDisplay(), getRootWindow(),
202                               0, 0, geom_w, geom_h, resource.border_width,
203                               getDepth(), InputOutput, getVisual(),
204                               mask, &attrib);
205   geom_visible = False;
206
207   BTexture* texture = &(resource.wstyle.l_focus);
208   geom_pixmap = texture->render(geom_w, geom_h, geom_pixmap);
209   if (geom_pixmap == ParentRelative) {
210     texture = &(resource.wstyle.t_focus);
211     geom_pixmap = texture->render(geom_w, geom_h, geom_pixmap);
212   }
213   if (! geom_pixmap)
214     XSetWindowBackground(blackbox->getXDisplay(), geom_window,
215                          texture->color().pixel());
216   else
217     XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
218                                geom_window, geom_pixmap);
219
220   workspacemenu = new Workspacemenu(this);
221   iconmenu = new Iconmenu(this);
222   configmenu = new Configmenu(this);
223
224   Workspace *wkspc = (Workspace *) 0;
225   if (resource.workspaces != 0) {
226     for (unsigned int i = 0; i < resource.workspaces; ++i) {
227       wkspc = new Workspace(this, workspacesList.size());
228       workspacesList.push_back(wkspc);
229       workspacemenu->insert(wkspc->getName(), wkspc->getMenu());
230     }
231   } else {
232     wkspc = new Workspace(this, workspacesList.size());
233     workspacesList.push_back(wkspc);
234     workspacemenu->insert(wkspc->getName(), wkspc->getMenu());
235   }
236
237   workspacemenu->insert(i18n(IconSet, IconIcons, "Icons"), iconmenu);
238   workspacemenu->update();
239
240   current_workspace = workspacesList.front();
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);
250   rootmenu->update();
251
252   updateAvailableArea();
253
254   changeWorkspaceID(0);
255
256   unsigned int i, j, nchild;
257   Window r, p, *children;
258   XQueryTree(blackbox->getXDisplay(), getRootWindow(), &r, &p,
259              &children, &nchild);
260
261   // preen the window list of all icon windows... for better dockapp support
262   for (i = 0; i < nchild; i++) {
263     if (children[i] == None) continue;
264
265     XWMHints *wmhints = XGetWMHints(blackbox->getXDisplay(),
266                                     children[i]);
267
268     if (wmhints) {
269       if ((wmhints->flags & IconWindowHint) &&
270           (wmhints->icon_window != children[i])) {
271         for (j = 0; j < nchild; j++) {
272           if (children[j] == wmhints->icon_window) {
273             children[j] = None;
274             break;
275           }
276         }
277       }
278
279       XFree(wmhints);
280     }
281   }
282
283   // manage shown windows
284   for (i = 0; i < nchild; ++i) {
285     if (children[i] == None || (! blackbox->validateWindow(children[i])))
286       continue;
287
288     XWindowAttributes attrib;
289     if (XGetWindowAttributes(blackbox->getXDisplay(), children[i], &attrib)) {
290       if (attrib.override_redirect) continue;
291
292       if (attrib.map_state != IsUnmapped) {
293         manageWindow(children[i]);
294       }
295     }
296   }
297
298   XFree(children);
299
300   // call this again just in case a window we found updates the Strut list
301   updateAvailableArea();
302 }
303
304
305 BScreen::~BScreen(void) {
306   if (! managed) return;
307
308   if (geom_pixmap != None)
309     image_control->removeImage(geom_pixmap);
310
311   if (geom_window != None)
312     XDestroyWindow(blackbox->getXDisplay(), geom_window);
313
314   std::for_each(workspacesList.begin(), workspacesList.end(),
315                 PointerAssassin());
316
317   std::for_each(iconList.begin(), iconList.end(), PointerAssassin());
318
319   std::for_each(netizenList.begin(), netizenList.end(), PointerAssassin());
320
321   delete rootmenu;
322   delete workspacemenu;
323   delete iconmenu;
324   delete configmenu;
325   delete slit;
326   delete toolbar;
327   delete image_control;
328
329   if (resource.wstyle.fontset)
330     XFreeFontSet(blackbox->getXDisplay(), resource.wstyle.fontset);
331   if (resource.mstyle.t_fontset)
332     XFreeFontSet(blackbox->getXDisplay(), resource.mstyle.t_fontset);
333   if (resource.mstyle.f_fontset)
334     XFreeFontSet(blackbox->getXDisplay(), resource.mstyle.f_fontset);
335   if (resource.tstyle.fontset)
336     XFreeFontSet(blackbox->getXDisplay(), resource.tstyle.fontset);
337
338   if (resource.wstyle.font)
339     XFreeFont(blackbox->getXDisplay(), resource.wstyle.font);
340   if (resource.mstyle.t_font)
341     XFreeFont(blackbox->getXDisplay(), resource.mstyle.t_font);
342   if (resource.mstyle.f_font)
343     XFreeFont(blackbox->getXDisplay(), resource.mstyle.f_font);
344   if (resource.tstyle.font)
345     XFreeFont(blackbox->getXDisplay(), resource.tstyle.font);
346
347   XFreeGC(blackbox->getXDisplay(), opGC);
348 }
349
350
351 void BScreen::removeWorkspaceNames(void) {
352   workspaceNames.clear();
353 }
354
355
356 void BScreen::reconfigure(void) {
357   LoadStyle();
358
359   XGCValues gcv;
360   unsigned long gc_value_mask = GCForeground;
361   if (! i18n.multibyte()) gc_value_mask |= GCFont;
362
363   gcv.foreground = WhitePixel(blackbox->getXDisplay(),
364                               getScreenNumber());
365   gcv.function = GXinvert;
366   gcv.subwindow_mode = IncludeInferiors;
367   XChangeGC(blackbox->getXDisplay(), opGC,
368             GCForeground | GCFunction | GCSubwindowMode, &gcv);
369
370   const char *s = i18n(ScreenSet, ScreenPositionLength,
371                        "0: 0000 x 0: 0000");
372   int l = strlen(s);
373
374   if (i18n.multibyte()) {
375     XRectangle ink, logical;
376     XmbTextExtents(resource.wstyle.fontset, s, l, &ink, &logical);
377     geom_w = logical.width;
378
379     geom_h = resource.wstyle.fontset_extents->max_ink_extent.height;
380   } else {
381     geom_w = XTextWidth(resource.wstyle.font, s, l);
382
383     geom_h = resource.wstyle.font->ascent + resource.wstyle.font->descent;
384   }
385
386   geom_w += (resource.bevel_width * 2);
387   geom_h += (resource.bevel_width * 2);
388
389   BTexture* texture = &(resource.wstyle.l_focus);
390   geom_pixmap = texture->render(geom_w, geom_h, geom_pixmap);
391   if (geom_pixmap == ParentRelative) {
392     texture = &(resource.wstyle.t_focus);
393     geom_pixmap = texture->render(geom_w, geom_h, geom_pixmap);
394   }
395   if (! geom_pixmap)
396     XSetWindowBackground(blackbox->getXDisplay(), geom_window,
397                          texture->color().pixel());
398   else
399     XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
400                                geom_window, geom_pixmap);
401
402   XSetWindowBorderWidth(blackbox->getXDisplay(), geom_window,
403                         resource.border_width);
404   XSetWindowBorder(blackbox->getXDisplay(), geom_window,
405                    resource.border_color.pixel());
406
407   workspacemenu->reconfigure();
408   iconmenu->reconfigure();
409
410   int remember_sub = rootmenu->getCurrentSubmenu();
411   InitMenu();
412   raiseWindows(0, 0);
413   rootmenu->reconfigure();
414   rootmenu->drawSubmenu(remember_sub);
415
416   configmenu->reconfigure();
417
418   toolbar->reconfigure();
419
420   slit->reconfigure();
421
422   std::for_each(workspacesList.begin(), workspacesList.end(),
423                 std::mem_fun(&Workspace::reconfigure));
424
425   BlackboxWindowList::iterator iit = iconList.begin();
426   for (; iit != iconList.end(); ++iit) {
427     BlackboxWindow *bw = *iit;
428     if (bw->validateClient())
429       bw->reconfigure();
430   }
431
432   image_control->timeout();
433 }
434
435
436 void BScreen::rereadMenu(void) {
437   InitMenu();
438   raiseWindows(0, 0);
439
440   rootmenu->reconfigure();
441 }
442
443
444 void BScreen::LoadStyle(void) {
445   resource.stylerc = XrmGetFileDatabase(blackbox->getStyleFilename());
446   if (! resource.stylerc)
447     resource.stylerc = XrmGetFileDatabase(DEFAULTSTYLE);
448
449   XrmValue value;
450   char *value_type;
451
452   // load fonts/fontsets
453   if (resource.wstyle.fontset)
454     XFreeFontSet(blackbox->getXDisplay(), resource.wstyle.fontset);
455   if (resource.tstyle.fontset)
456     XFreeFontSet(blackbox->getXDisplay(), resource.tstyle.fontset);
457   if (resource.mstyle.f_fontset)
458     XFreeFontSet(blackbox->getXDisplay(), resource.mstyle.f_fontset);
459   if (resource.mstyle.t_fontset)
460     XFreeFontSet(blackbox->getXDisplay(), resource.mstyle.t_fontset);
461   resource.wstyle.fontset = 0;
462   resource.tstyle.fontset = 0;
463   resource.mstyle.f_fontset = 0;
464   resource.mstyle.t_fontset = 0;
465   if (resource.wstyle.font)
466     XFreeFont(blackbox->getXDisplay(), resource.wstyle.font);
467   if (resource.tstyle.font)
468     XFreeFont(blackbox->getXDisplay(), resource.tstyle.font);
469   if (resource.mstyle.f_font)
470     XFreeFont(blackbox->getXDisplay(), resource.mstyle.f_font);
471   if (resource.mstyle.t_font)
472     XFreeFont(blackbox->getXDisplay(), resource.mstyle.t_font);
473   resource.wstyle.font = 0;
474   resource.tstyle.font = 0;
475   resource.mstyle.f_font = 0;
476   resource.mstyle.t_font = 0;
477
478   if (i18n.multibyte()) {
479     resource.wstyle.fontset =
480       readDatabaseFontSet("window.font", "Window.Font");
481     resource.tstyle.fontset =
482       readDatabaseFontSet("toolbar.font", "Toolbar.Font");
483     resource.mstyle.t_fontset =
484       readDatabaseFontSet("menu.title.font", "Menu.Title.Font");
485     resource.mstyle.f_fontset =
486       readDatabaseFontSet("menu.frame.font", "Menu.Frame.Font");
487
488     resource.mstyle.t_fontset_extents =
489       XExtentsOfFontSet(resource.mstyle.t_fontset);
490     resource.mstyle.f_fontset_extents =
491       XExtentsOfFontSet(resource.mstyle.f_fontset);
492     resource.tstyle.fontset_extents =
493       XExtentsOfFontSet(resource.tstyle.fontset);
494     resource.wstyle.fontset_extents =
495       XExtentsOfFontSet(resource.wstyle.fontset);
496   } else {
497     resource.wstyle.font =
498       readDatabaseFont("window.font", "Window.Font");
499     resource.tstyle.font =
500       readDatabaseFont("toolbar.font", "Toolbar.Font");
501     resource.mstyle.t_font =
502       readDatabaseFont("menu.title.font", "Menu.Title.Font");
503     resource.mstyle.f_font =
504       readDatabaseFont("menu.frame.font", "Menu.Frame.Font");
505   }
506
507   // load window config
508   resource.wstyle.t_focus =
509     readDatabaseTexture("window.title.focus", "Window.Title.Focus", "white");
510   resource.wstyle.t_unfocus =
511     readDatabaseTexture("window.title.unfocus",
512                         "Window.Title.Unfocus", "black");
513   resource.wstyle.l_focus =
514     readDatabaseTexture("window.label.focus", "Window.Label.Focus", "white" );
515   resource.wstyle.l_unfocus =
516     readDatabaseTexture("window.label.unfocus", "Window.Label.Unfocus",
517                         "black");
518   resource.wstyle.h_focus =
519     readDatabaseTexture("window.handle.focus", "Window.Handle.Focus", "white");
520   resource.wstyle.h_unfocus =
521     readDatabaseTexture("window.handle.unfocus",
522                         "Window.Handle.Unfocus", "black");
523   resource.wstyle.g_focus =
524     readDatabaseTexture("window.grip.focus", "Window.Grip.Focus", "white");
525   resource.wstyle.g_unfocus =
526     readDatabaseTexture("window.grip.unfocus", "Window.Grip.Unfocus", "black");
527   resource.wstyle.b_focus =
528     readDatabaseTexture("window.button.focus", "Window.Button.Focus", "white");
529   resource.wstyle.b_unfocus =
530     readDatabaseTexture("window.button.unfocus",
531                         "Window.Button.Unfocus", "black");
532   resource.wstyle.b_pressed =
533     readDatabaseTexture("window.button.pressed",
534                         "Window.Button.Pressed", "black");
535   resource.wstyle.f_focus =
536     readDatabaseColor("window.frame.focusColor",
537                       "Window.Frame.FocusColor", "white");
538   resource.wstyle.f_unfocus =
539     readDatabaseColor("window.frame.unfocusColor",
540                       "Window.Frame.UnfocusColor", "black");
541   resource.wstyle.l_text_focus =
542     readDatabaseColor("window.label.focus.textColor",
543                       "Window.Label.Focus.TextColor", "black");
544   resource.wstyle.l_text_unfocus =
545     readDatabaseColor("window.label.unfocus.textColor",
546                       "Window.Label.Unfocus.TextColor", "white");
547   resource.wstyle.b_pic_focus =
548     readDatabaseColor("window.button.focus.picColor",
549                       "Window.Button.Focus.PicColor", "black");
550   resource.wstyle.b_pic_unfocus =
551     readDatabaseColor("window.button.unfocus.picColor",
552                       "Window.Button.Unfocus.PicColor", "white");
553
554   resource.wstyle.justify = LeftJustify;
555   if (XrmGetResource(resource.stylerc, "window.justify", "Window.Justify",
556                      &value_type, &value)) {
557     if (strstr(value.addr, "right") || strstr(value.addr, "Right"))
558       resource.wstyle.justify = RightJustify;
559     else if (strstr(value.addr, "center") || strstr(value.addr, "Center"))
560       resource.wstyle.justify = CenterJustify;
561   }
562
563   // load toolbar config
564   resource.tstyle.toolbar =
565     readDatabaseTexture("toolbar", "Toolbar", "black");
566   resource.tstyle.label =
567     readDatabaseTexture("toolbar.label", "Toolbar.Label", "black");
568   resource.tstyle.window =
569     readDatabaseTexture("toolbar.windowLabel", "Toolbar.WindowLabel", "black");
570   resource.tstyle.button =
571     readDatabaseTexture("toolbar.button", "Toolbar.Button", "white");
572   resource.tstyle.pressed =
573     readDatabaseTexture("toolbar.button.pressed",
574                         "Toolbar.Button.Pressed", "black");
575   resource.tstyle.clock =
576     readDatabaseTexture("toolbar.clock", "Toolbar.Clock", "black");
577   resource.tstyle.l_text =
578     readDatabaseColor("toolbar.label.textColor",
579                       "Toolbar.Label.TextColor", "white");
580   resource.tstyle.w_text =
581     readDatabaseColor("toolbar.windowLabel.textColor",
582                       "Toolbar.WindowLabel.TextColor", "white");
583   resource.tstyle.c_text =
584     readDatabaseColor("toolbar.clock.textColor",
585                       "Toolbar.Clock.TextColor", "white");
586   resource.tstyle.b_pic =
587     readDatabaseColor("toolbar.button.picColor",
588                       "Toolbar.Button.PicColor", "black");
589
590   resource.tstyle.justify = LeftJustify;
591   if (XrmGetResource(resource.stylerc, "toolbar.justify",
592                      "Toolbar.Justify", &value_type, &value)) {
593     if (strstr(value.addr, "right") || strstr(value.addr, "Right"))
594       resource.tstyle.justify = RightJustify;
595     else if (strstr(value.addr, "center") || strstr(value.addr, "Center"))
596       resource.tstyle.justify = CenterJustify;
597   }
598
599   // load menu config
600   resource.mstyle.title =
601     readDatabaseTexture("menu.title", "Menu.Title", "white");
602   resource.mstyle.frame =
603     readDatabaseTexture("menu.frame", "Menu.Frame", "black");
604   resource.mstyle.hilite =
605     readDatabaseTexture("menu.hilite", "Menu.Hilite", "white");
606   resource.mstyle.t_text =
607     readDatabaseColor("menu.title.textColor", "Menu.Title.TextColor", "black");
608   resource.mstyle.f_text =
609     readDatabaseColor("menu.frame.textColor", "Menu.Frame.TextColor", "white");
610   resource.mstyle.d_text =
611     readDatabaseColor("menu.frame.disableColor",
612                       "Menu.Frame.DisableColor", "black");
613   resource.mstyle.h_text =
614     readDatabaseColor("menu.hilite.textColor",
615                       "Menu.Hilite.TextColor", "black");
616
617   resource.mstyle.t_justify = LeftJustify;
618   if (XrmGetResource(resource.stylerc, "menu.title.justify",
619                      "Menu.Title.Justify",
620                      &value_type, &value)) {
621     if (strstr(value.addr, "right") || strstr(value.addr, "Right"))
622       resource.mstyle.t_justify = RightJustify;
623     else if (strstr(value.addr, "center") || strstr(value.addr, "Center"))
624       resource.mstyle.t_justify = CenterJustify;
625   }
626
627   resource.mstyle.f_justify = LeftJustify;
628   if (XrmGetResource(resource.stylerc, "menu.frame.justify",
629                      "Menu.Frame.Justify",
630                      &value_type, &value)) {
631     if (strstr(value.addr, "right") || strstr(value.addr, "Right"))
632       resource.mstyle.f_justify = RightJustify;
633     else if (strstr(value.addr, "center") || strstr(value.addr, "Center"))
634       resource.mstyle.f_justify = CenterJustify;
635   }
636
637   resource.mstyle.bullet = Basemenu::Triangle;
638   if (XrmGetResource(resource.stylerc, "menu.bullet", "Menu.Bullet",
639                      &value_type, &value)) {
640     if (! strncasecmp(value.addr, "empty", value.size))
641       resource.mstyle.bullet = Basemenu::Empty;
642     else if (! strncasecmp(value.addr, "square", value.size))
643       resource.mstyle.bullet = Basemenu::Square;
644     else if (! strncasecmp(value.addr, "diamond", value.size))
645       resource.mstyle.bullet = Basemenu::Diamond;
646   }
647
648   resource.mstyle.bullet_pos = Basemenu::Left;
649   if (XrmGetResource(resource.stylerc, "menu.bullet.position",
650                      "Menu.Bullet.Position", &value_type, &value)) {
651     if (! strncasecmp(value.addr, "right", value.size))
652       resource.mstyle.bullet_pos = Basemenu::Right;
653   }
654
655   resource.border_color =
656     readDatabaseColor("borderColor", "BorderColor", "black");
657
658   unsigned int uint_value;
659
660   // load bevel, border and handle widths
661   resource.handle_width = 6;
662   if (XrmGetResource(resource.stylerc, "handleWidth", "HandleWidth",
663                      &value_type, &value) &&
664       sscanf(value.addr, "%u", &uint_value) == 1 &&
665       uint_value <= (getWidth() / 2) && uint_value != 0) {
666     resource.handle_width = uint_value;
667   }
668
669   resource.border_width = 1;
670   if (XrmGetResource(resource.stylerc, "borderWidth", "BorderWidth",
671                      &value_type, &value) &&
672       sscanf(value.addr, "%u", &uint_value) == 1) {
673     resource.border_width = uint_value;
674   }
675
676   resource.bevel_width = 3;
677   if (XrmGetResource(resource.stylerc, "bevelWidth", "BevelWidth",
678                      &value_type, &value) &&
679       sscanf(value.addr, "%u", &uint_value) == 1 &&
680       uint_value <= (getWidth() / 2) && uint_value != 0) {
681     resource.bevel_width = uint_value;
682   }
683
684   resource.frame_width = resource.bevel_width;
685   if (XrmGetResource(resource.stylerc, "frameWidth", "FrameWidth",
686                      &value_type, &value) &&
687       sscanf(value.addr, "%u", &uint_value) == 1 &&
688       uint_value <= (getWidth() / 2)) {
689     resource.frame_width = uint_value;
690   }
691
692   if (XrmGetResource(resource.stylerc, "rootCommand", "RootCommand",
693                      &value_type, &value)) {
694     bexec(value.addr, displayString());
695   }
696
697   XrmDestroyDatabase(resource.stylerc);
698 }
699
700
701 void BScreen::addIcon(BlackboxWindow *w) {
702   if (! w) return;
703
704   w->setWorkspace(BSENTINEL);
705   w->setWindowNumber(iconList.size());
706
707   iconList.push_back(w);
708
709   const char* title = w->getIconTitle();
710   iconmenu->insert(title);
711   iconmenu->update();
712 }
713
714
715 void BScreen::removeIcon(BlackboxWindow *w) {
716   if (! w) return;
717
718   iconList.remove(w);
719
720   iconmenu->remove(w->getWindowNumber());
721   iconmenu->update();
722
723   BlackboxWindowList::iterator it = iconList.begin(),
724     end = iconList.end();
725   for (int i = 0; it != end; ++it)
726     (*it)->setWindowNumber(i++);
727 }
728
729
730 BlackboxWindow *BScreen::getIcon(unsigned int index) {
731   if (index < iconList.size()) {
732     BlackboxWindowList::iterator it = iconList.begin();
733     for (; index > 0; --index, ++it) ; /* increment to index */
734     return *it;
735   }
736
737   return (BlackboxWindow *) 0;
738 }
739
740
741 unsigned int BScreen::addWorkspace(void) {
742   Workspace *wkspc = new Workspace(this, workspacesList.size());
743   workspacesList.push_back(wkspc);
744
745   workspacemenu->insert(wkspc->getName(), wkspc->getMenu(),
746                         wkspc->getID() + 2);
747   workspacemenu->update();
748
749   toolbar->reconfigure();
750
751   updateNetizenWorkspaceCount();
752
753   return workspacesList.size();
754 }
755
756
757 unsigned int BScreen::removeLastWorkspace(void) {
758   if (workspacesList.size() == 1)
759     return 1;
760
761   Workspace *wkspc = workspacesList.back();
762
763   if (current_workspace->getID() == wkspc->getID())
764     changeWorkspaceID(current_workspace->getID() - 1);
765
766   wkspc->removeAll();
767
768   workspacemenu->remove(wkspc->getID() + 2);
769   workspacemenu->update();
770
771   workspacesList.pop_back();
772   delete wkspc;
773
774   toolbar->reconfigure();
775
776   updateNetizenWorkspaceCount();
777
778   return workspacesList.size();
779 }
780
781
782 void BScreen::changeWorkspaceID(unsigned int id) {
783   if (! current_workspace) return;
784
785   if (id != current_workspace->getID()) {
786     current_workspace->hideAll();
787
788     workspacemenu->setItemSelected(current_workspace->getID() + 2, False);
789
790     if (blackbox->getFocusedWindow() &&
791         blackbox->getFocusedWindow()->getScreen() == this &&
792         (! blackbox->getFocusedWindow()->isStuck())) {
793       current_workspace->setLastFocusedWindow(blackbox->getFocusedWindow());
794       blackbox->setFocusedWindow((BlackboxWindow *) 0);
795     }
796
797     current_workspace = getWorkspace(id);
798
799     workspacemenu->setItemSelected(current_workspace->getID() + 2, True);
800     toolbar->redrawWorkspaceLabel(True);
801
802     current_workspace->showAll();
803
804     if (resource.focus_last && current_workspace->getLastFocusedWindow()) {
805       XSync(blackbox->getXDisplay(), False);
806       current_workspace->getLastFocusedWindow()->setInputFocus();
807     }
808   }
809
810   updateNetizenCurrentWorkspace();
811 }
812
813
814 void BScreen::manageWindow(Window w) {
815   new BlackboxWindow(blackbox, w, this);
816
817   BlackboxWindow *win = blackbox->searchWindow(w);
818   if (! win)
819     return;
820
821   windowList.push_back(win);
822
823   XMapRequestEvent mre;
824   mre.window = w;
825   win->restoreAttributes();
826   win->mapRequestEvent(&mre);
827 }
828
829
830 void BScreen::unmanageWindow(BlackboxWindow *w, bool remap) {
831   w->restore(remap);
832
833   if (w->getWorkspaceNumber() != BSENTINEL &&
834       w->getWindowNumber() != BSENTINEL)
835     getWorkspace(w->getWorkspaceNumber())->removeWindow(w);
836   else if (w->isIconic())
837     removeIcon(w);
838
839   windowList.remove(w);
840
841   if (blackbox->getFocusedWindow() == w)
842     blackbox->setFocusedWindow((BlackboxWindow *) 0);
843
844   removeNetizen(w->getClientWindow());
845
846   delete w;
847 }
848
849
850 void BScreen::addNetizen(Netizen *n) {
851   netizenList.push_back(n);
852
853   n->sendWorkspaceCount();
854   n->sendCurrentWorkspace();
855
856   WorkspaceList::iterator it = workspacesList.begin();
857   const WorkspaceList::iterator end = workspacesList.end();
858   for (; it != end; ++it)
859     (*it)->sendWindowList(*n);
860
861   Window f = ((blackbox->getFocusedWindow()) ?
862               blackbox->getFocusedWindow()->getClientWindow() : None);
863   n->sendWindowFocus(f);
864 }
865
866
867 void BScreen::removeNetizen(Window w) {
868   NetizenList::iterator it = netizenList.begin();
869   for (; it != netizenList.end(); ++it) {
870     if ((*it)->getWindowID() == w) {
871       delete *it;
872       netizenList.erase(it);
873       break;
874     }
875   }
876 }
877
878
879 void BScreen::updateNetizenCurrentWorkspace(void) {
880   std::for_each(netizenList.begin(), netizenList.end(),
881                 std::mem_fun(&Netizen::sendCurrentWorkspace));
882 }
883
884
885 void BScreen::updateNetizenWorkspaceCount(void) {
886   std::for_each(netizenList.begin(), netizenList.end(),
887                 std::mem_fun(&Netizen::sendWorkspaceCount));
888 }
889
890
891 void BScreen::updateNetizenWindowFocus(void) {
892   Window f = ((blackbox->getFocusedWindow()) ?
893               blackbox->getFocusedWindow()->getClientWindow() : None);
894   NetizenList::iterator it = netizenList.begin();
895   for (; it != netizenList.end(); ++it)
896     (*it)->sendWindowFocus(f);
897 }
898
899
900 void BScreen::updateNetizenWindowAdd(Window w, unsigned long p) {
901   NetizenList::iterator it = netizenList.begin();
902   for (; it != netizenList.end(); ++it) {
903     (*it)->sendWindowAdd(w, p);
904   }
905 }
906
907
908 void BScreen::updateNetizenWindowDel(Window w) {
909   NetizenList::iterator it = netizenList.begin();
910   for (; it != netizenList.end(); ++it)
911     (*it)->sendWindowDel(w);
912 }
913
914
915 void BScreen::updateNetizenWindowRaise(Window w) {
916   NetizenList::iterator it = netizenList.begin();
917   for (; it != netizenList.end(); ++it)
918     (*it)->sendWindowRaise(w);
919 }
920
921
922 void BScreen::updateNetizenWindowLower(Window w) {
923   NetizenList::iterator it = netizenList.begin();
924   for (; it != netizenList.end(); ++it)
925     (*it)->sendWindowLower(w);
926 }
927
928
929 void BScreen::updateNetizenConfigNotify(XEvent *e) {
930   NetizenList::iterator it = netizenList.begin();
931   for (; it != netizenList.end(); ++it)
932     (*it)->sendConfigNotify(e);
933 }
934
935
936 void BScreen::raiseWindows(Window *workspace_stack, unsigned int num) {
937   // XXX: why 13??
938   Window *session_stack = new
939     Window[(num + workspacesList.size() + rootmenuList.size() + 13)];
940   unsigned int i = 0, k = num;
941
942   XRaiseWindow(blackbox->getXDisplay(), iconmenu->getWindowID());
943   *(session_stack + i++) = iconmenu->getWindowID();
944
945   WorkspaceList::iterator wit = workspacesList.begin();
946   const WorkspaceList::iterator w_end = workspacesList.end();
947   for (; wit != w_end; ++wit)
948     *(session_stack + i++) = (*wit)->getMenu()->getWindowID();
949
950   *(session_stack + i++) = workspacemenu->getWindowID();
951
952   *(session_stack + i++) = configmenu->getFocusmenu()->getWindowID();
953   *(session_stack + i++) = configmenu->getPlacementmenu()->getWindowID();
954   *(session_stack + i++) = configmenu->getWindowID();
955
956   *(session_stack + i++) = slit->getMenu()->getDirectionmenu()->getWindowID();
957   *(session_stack + i++) = slit->getMenu()->getPlacementmenu()->getWindowID();
958   *(session_stack + i++) = slit->getMenu()->getWindowID();
959
960   *(session_stack + i++) =
961     toolbar->getMenu()->getPlacementmenu()->getWindowID();
962   *(session_stack + i++) = toolbar->getMenu()->getWindowID();
963
964   RootmenuList::iterator rit = rootmenuList.begin();
965   for (; rit != rootmenuList.end(); ++rit)
966     *(session_stack + i++) = (*rit)->getWindowID();
967   *(session_stack + i++) = rootmenu->getWindowID();
968
969   if (toolbar->isOnTop())
970     *(session_stack + i++) = toolbar->getWindowID();
971
972   if (slit->isOnTop())
973     *(session_stack + i++) = slit->getWindowID();
974
975   while (k--)
976     *(session_stack + i++) = *(workspace_stack + k);
977
978   XRestackWindows(blackbox->getXDisplay(), session_stack, i);
979
980   delete [] session_stack;
981 }
982
983
984 #ifdef    HAVE_STRFTIME
985 void BScreen::saveStrftimeFormat(const string& format) {
986   resource.strftime_format = format;
987 }
988 #endif // HAVE_STRFTIME
989
990
991 void BScreen::addWorkspaceName(const string& name) {
992   workspaceNames.push_back(name);
993 }
994
995
996 /*
997  * I would love to kill this function and the accompanying workspaceNames
998  * list.  However, we have a chicken and egg situation.  The names are read
999  * in during load_rc() which happens before the workspaces are created.
1000  * The current solution is to read the names into a list, then use the list
1001  * later for constructing the workspaces.  It is only used during initial
1002  * BScreen creation.
1003  */
1004 const string BScreen::getNameOfWorkspace(unsigned int id) {
1005   if (id < workspaceNames.size())
1006     return workspaceNames[id];
1007   return string("");
1008 }
1009
1010
1011 void BScreen::reassociateWindow(BlackboxWindow *w, unsigned int wkspc_id,
1012                                 bool ignore_sticky) {
1013   if (! w) return;
1014
1015   if (wkspc_id == BSENTINEL)
1016     wkspc_id = current_workspace->getID();
1017
1018   if (w->getWorkspaceNumber() == wkspc_id)
1019     return;
1020
1021   if (w->isIconic()) {
1022     removeIcon(w);
1023     getWorkspace(wkspc_id)->addWindow(w);
1024   } else if (ignore_sticky || ! w->isStuck()) {
1025     getWorkspace(w->getWorkspaceNumber())->removeWindow(w);
1026     getWorkspace(wkspc_id)->addWindow(w);
1027   }
1028 }
1029
1030
1031 void BScreen::propagateWindowName(const BlackboxWindow *bw) {
1032   if (bw->isIconic()) {
1033     iconmenu->changeItemLabel(bw->getWindowNumber(), bw->getIconTitle());
1034     iconmenu->update();
1035   }
1036   else {
1037     Clientmenu *clientmenu = getWorkspace(bw->getWorkspaceNumber())->getMenu();
1038     clientmenu->changeItemLabel(bw->getWindowNumber(), bw->getTitle());
1039     clientmenu->update();
1040
1041     if (blackbox->getFocusedWindow() == bw)
1042       toolbar->redrawWindowLabel(True);
1043   }
1044 }
1045
1046
1047 void BScreen::nextFocus(void) {
1048   BlackboxWindow *focused = blackbox->getFocusedWindow(),
1049     *next = focused;
1050
1051   if (focused) {
1052     // if window is not on this screen, ignore it
1053     if (focused->getScreen()->getScreenNumber() != getScreenNumber())
1054       focused = (BlackboxWindow*) 0;
1055   }
1056
1057   if (focused && current_workspace->getCount() > 1) {
1058     // next is the next window to recieve focus, current is a place holder
1059     BlackboxWindow *current;
1060     do {
1061       current = next;
1062       next = current_workspace->getNextWindowInList(current);
1063     } while(!next->setInputFocus() && next != focused);
1064
1065     if (next != focused)
1066       current_workspace->raiseWindow(next);
1067   } else if (current_workspace->getCount() >= 1) {
1068     next = current_workspace->getTopWindowOnStack();
1069
1070     current_workspace->raiseWindow(next);
1071     next->setInputFocus();
1072   }
1073 }
1074
1075
1076 void BScreen::prevFocus(void) {
1077   BlackboxWindow *focused = blackbox->getFocusedWindow(),
1078     *next = focused;
1079
1080   if (focused) {
1081     // if window is not on this screen, ignore it
1082     if (focused->getScreen()->getScreenNumber() != getScreenNumber())
1083       focused = (BlackboxWindow*) 0;
1084   }
1085
1086   if (focused && current_workspace->getCount() > 1) {
1087     // next is the next window to recieve focus, current is a place holder
1088     BlackboxWindow *current;
1089     do {
1090       current = next;
1091       next = current_workspace->getPrevWindowInList(current);
1092     } while(!next->setInputFocus() && next != focused);
1093
1094     if (next != focused)
1095       current_workspace->raiseWindow(next);
1096   } else if (current_workspace->getCount() >= 1) {
1097     next = current_workspace->getTopWindowOnStack();
1098
1099     current_workspace->raiseWindow(next);
1100     next->setInputFocus();
1101   }
1102 }
1103
1104
1105 void BScreen::raiseFocus(void) {
1106   BlackboxWindow *focused = blackbox->getFocusedWindow();
1107   if (! focused)
1108     return;
1109
1110   // if on this Screen, raise it
1111   if (focused->getScreen()->getScreenNumber() == getScreenNumber()) {
1112     Workspace *workspace = getWorkspace(focused->getWorkspaceNumber());
1113     workspace->raiseWindow(focused);
1114   }
1115 }
1116
1117
1118 void BScreen::InitMenu(void) {
1119   if (rootmenu) {
1120     rootmenuList.clear();
1121
1122     while (rootmenu->getCount())
1123       rootmenu->remove(0);
1124   } else {
1125     rootmenu = new Rootmenu(this);
1126   }
1127   bool defaultMenu = True;
1128
1129   FILE *menu_file = (FILE *) 0;
1130   const char *menu_filename = blackbox->getMenuFilename();
1131
1132   if (menu_filename) 
1133     if (!(menu_file = fopen(menu_filename, "r")))
1134       perror(menu_filename);
1135   if (!menu_file) {     // opening the menu file failed, try the default menu
1136     menu_filename = DEFAULTMENU;
1137     if (!(menu_file = fopen(menu_filename, "r")))
1138       perror(menu_filename);
1139   } 
1140
1141   if (menu_file) {
1142     if (feof(menu_file)) {
1143       fprintf(stderr, i18n(ScreenSet, ScreenEmptyMenuFile,
1144                            "%s: Empty menu file"),
1145               menu_filename);
1146     } else {
1147       char line[1024], label[1024];
1148       memset(line, 0, 1024);
1149       memset(label, 0, 1024);
1150
1151       while (fgets(line, 1024, menu_file) && ! feof(menu_file)) {
1152         if (line[0] != '#') {
1153           int i, key = 0, index = -1, len = strlen(line);
1154
1155           for (i = 0; i < len; i++) {
1156             if (line[i] == '[') index = 0;
1157             else if (line[i] == ']') break;
1158             else if (line[i] != ' ')
1159               if (index++ >= 0)
1160                 key += tolower(line[i]);
1161           }
1162
1163           if (key == 517) { // [begin]
1164             index = -1;
1165             for (i = index; i < len; i++) {
1166               if (line[i] == '(') index = 0;
1167               else if (line[i] == ')') break;
1168               else if (index++ >= 0) {
1169                 if (line[i] == '\\' && i < len - 1) i++;
1170                 label[index - 1] = line[i];
1171               }
1172             }
1173
1174             if (index == -1) index = 0;
1175             label[index] = '\0';
1176
1177             rootmenu->setLabel(label);
1178             defaultMenu = parseMenuFile(menu_file, rootmenu);
1179             if (!defaultMenu)
1180               blackbox->addMenuTimestamp(menu_filename);
1181             break;
1182           }
1183         }
1184       }
1185     }
1186     fclose(menu_file);
1187   }
1188
1189   if (defaultMenu) {
1190     rootmenu->setInternalMenu();
1191     rootmenu->insert(i18n(ScreenSet, Screenxterm, "xterm"),
1192                      BScreen::Execute,
1193                      i18n(ScreenSet, Screenxterm, "xterm"));
1194     rootmenu->insert(i18n(ScreenSet, ScreenRestart, "Restart"),
1195                      BScreen::Restart);
1196     rootmenu->insert(i18n(ScreenSet, ScreenExit, "Exit"),
1197                      BScreen::Exit);
1198     rootmenu->setLabel(i18n(BasemenuSet, BasemenuBlackboxMenu,
1199                             "Openbox Menu"));
1200   }
1201 }
1202
1203
1204 bool BScreen::parseMenuFile(FILE *file, Rootmenu *menu) {
1205   char line[1024], label[1024], command[1024];
1206
1207   while (! feof(file)) {
1208     memset(line, 0, 1024);
1209     memset(label, 0, 1024);
1210     memset(command, 0, 1024);
1211
1212     if (fgets(line, 1024, file)) {
1213       if (line[0] != '#') {
1214         int i, key = 0, parse = 0, index = -1, line_length = strlen(line);
1215
1216         // determine the keyword
1217         for (i = 0; i < line_length; i++) {
1218           if (line[i] == '[') parse = 1;
1219           else if (line[i] == ']') break;
1220           else if (line[i] != ' ')
1221             if (parse)
1222               key += tolower(line[i]);
1223         }
1224
1225         // get the label enclosed in ()'s
1226         parse = 0;
1227
1228         for (i = 0; i < line_length; i++) {
1229           if (line[i] == '(') {
1230             index = 0;
1231             parse = 1;
1232           } else if (line[i] == ')') break;
1233           else if (index++ >= 0) {
1234             if (line[i] == '\\' && i < line_length - 1) i++;
1235             label[index - 1] = line[i];
1236           }
1237         }
1238
1239         if (parse) {
1240           label[index] = '\0';
1241         } else {
1242           label[0] = '\0';
1243         }
1244
1245         // get the command enclosed in {}'s
1246         parse = 0;
1247         index = -1;
1248         for (i = 0; i < line_length; i++) {
1249           if (line[i] == '{') {
1250             index = 0;
1251             parse = 1;
1252           } else if (line[i] == '}') break;
1253           else if (index++ >= 0) {
1254             if (line[i] == '\\' && i < line_length - 1) i++;
1255             command[index - 1] = line[i];
1256           }
1257         }
1258
1259         if (parse) {
1260           command[index] = '\0';
1261         } else {
1262           command[0] = '\0';
1263         }
1264
1265         switch (key) {
1266         case 311: // end
1267           return ((menu->getCount() == 0) ? True : False);
1268
1269           break;
1270
1271         case 333: // nop
1272           if (! *label)
1273             label[0] = '\0';
1274           menu->insert(label);
1275
1276           break;
1277
1278         case 421: // exec
1279           if ((! *label) && (! *command)) {
1280             fprintf(stderr, i18n(ScreenSet, ScreenEXECError,
1281                                  "BScreen::parseMenuFile: [exec] error, "
1282                                  "no menu label and/or command defined\n"));
1283             continue;
1284           }
1285
1286           menu->insert(label, BScreen::Execute, command);
1287
1288           break;
1289
1290         case 442: // exit
1291           if (! *label) {
1292             fprintf(stderr, i18n(ScreenSet, ScreenEXITError,
1293                                  "BScreen::parseMenuFile: [exit] error, "
1294                                  "no menu label defined\n"));
1295             continue;
1296           }
1297
1298           menu->insert(label, BScreen::Exit);
1299
1300           break;
1301
1302         case 561: // style
1303           {
1304             if ((! *label) || (! *command)) {
1305               fprintf(stderr,
1306                       i18n(ScreenSet, ScreenSTYLEError,
1307                            "BScreen::parseMenuFile: [style] error, "
1308                            "no menu label and/or filename defined\n"));
1309               continue;
1310             }
1311
1312             string style = expandTilde(command);
1313
1314             menu->insert(label, BScreen::SetStyle, style.c_str());
1315           }
1316
1317           break;
1318
1319         case 630: // config
1320           if (! *label) {
1321             fprintf(stderr, i18n(ScreenSet, ScreenCONFIGError,
1322                                  "BScreen::parseMenufile: [config] error, "
1323                                  "no label defined"));
1324             continue;
1325           }
1326
1327           menu->insert(label, configmenu);
1328
1329           break;
1330
1331         case 740: // include
1332           {
1333             if (! *label) {
1334               fprintf(stderr, i18n(ScreenSet, ScreenINCLUDEError,
1335                                    "BScreen::parseMenuFile: [include] error, "
1336                                    "no filename defined\n"));
1337               continue;
1338             }
1339
1340             string newfile = expandTilde(label);
1341             FILE *submenufile = fopen(newfile.c_str(), "r");
1342
1343             if (submenufile) {
1344               struct stat buf;
1345               if (fstat(fileno(submenufile), &buf) ||
1346                   (! S_ISREG(buf.st_mode))) {
1347                 fprintf(stderr,
1348                         i18n(ScreenSet, ScreenINCLUDEErrorReg,
1349                              "BScreen::parseMenuFile: [include] error: "
1350                              "'%s' is not a regular file\n"), newfile.c_str());
1351                 break;
1352               }
1353
1354               if (! feof(submenufile)) {
1355                 if (! parseMenuFile(submenufile, menu))
1356                   blackbox->addMenuTimestamp(newfile);
1357
1358                 fclose(submenufile);
1359               }
1360             } else {
1361               perror(newfile.c_str());
1362             }
1363           }
1364
1365           break;
1366
1367         case 767: // submenu
1368           {
1369             if (! *label) {
1370               fprintf(stderr, i18n(ScreenSet, ScreenSUBMENUError,
1371                                    "BScreen::parseMenuFile: [submenu] error, "
1372                                    "no menu label defined\n"));
1373               continue;
1374             }
1375
1376             Rootmenu *submenu = new Rootmenu(this);
1377
1378             if (*command)
1379               submenu->setLabel(command);
1380             else
1381               submenu->setLabel(label);
1382
1383             parseMenuFile(file, submenu);
1384             submenu->update();
1385             menu->insert(label, submenu);
1386             rootmenuList.push_back(submenu);
1387           }
1388
1389           break;
1390
1391         case 773: // restart
1392           {
1393             if (! *label) {
1394               fprintf(stderr, i18n(ScreenSet, ScreenRESTARTError,
1395                                    "BScreen::parseMenuFile: [restart] error, "
1396                                    "no menu label defined\n"));
1397               continue;
1398             }
1399
1400             if (*command)
1401               menu->insert(label, BScreen::RestartOther, command);
1402             else
1403               menu->insert(label, BScreen::Restart);
1404           }
1405
1406           break;
1407
1408         case 845: // reconfig
1409           {
1410             if (! *label) {
1411               fprintf(stderr,
1412                       i18n(ScreenSet, ScreenRECONFIGError,
1413                            "BScreen::parseMenuFile: [reconfig] error, "
1414                            "no menu label defined\n"));
1415               continue;
1416             }
1417
1418             menu->insert(label, BScreen::Reconfigure);
1419           }
1420
1421           break;
1422
1423         case 995: // stylesdir
1424         case 1113: // stylesmenu
1425           {
1426             bool newmenu = ((key == 1113) ? True : False);
1427
1428             if ((! *label) || ((! *command) && newmenu)) {
1429               fprintf(stderr,
1430                       i18n(ScreenSet, ScreenSTYLESDIRError,
1431                            "BScreen::parseMenuFile: [stylesdir/stylesmenu]"
1432                            " error, no directory defined\n"));
1433               continue;
1434             }
1435
1436             char *directory = ((newmenu) ? command : label);
1437
1438             string stylesdir = expandTilde(directory);
1439
1440             struct stat statbuf;
1441
1442             if (! stat(stylesdir.c_str(), &statbuf)) {
1443               if (S_ISDIR(statbuf.st_mode)) {
1444                 Rootmenu *stylesmenu;
1445
1446                 if (newmenu)
1447                   stylesmenu = new Rootmenu(this);
1448                 else
1449                   stylesmenu = menu;
1450
1451                 DIR *d = opendir(stylesdir.c_str());
1452                 struct dirent *p;
1453                 std::vector<string> ls;
1454
1455                 while((p = readdir(d)))
1456                   ls.push_back(p->d_name);
1457
1458                 closedir(d);
1459
1460                 std::sort(ls.begin(), ls.end());
1461
1462                 std::vector<string>::iterator it = ls.begin(),
1463                   end = ls.end();
1464                 for (; it != end; ++it) {
1465                   const string& fname = *it;
1466
1467                   if (fname[fname.size()-1] == '~')
1468                     continue;
1469
1470                   string style = stylesdir;
1471                   style += '/';
1472                   style += fname;
1473
1474                   if ((! stat(style.c_str(), &statbuf)) &&
1475                       S_ISREG(statbuf.st_mode))
1476                     stylesmenu->insert(fname, BScreen::SetStyle, style);
1477                 }
1478
1479                 stylesmenu->update();
1480
1481                 if (newmenu) {
1482                   stylesmenu->setLabel(label);
1483                   menu->insert(label, stylesmenu);
1484                   rootmenuList.push_back(stylesmenu);
1485                 }
1486
1487                 blackbox->addMenuTimestamp(stylesdir);
1488               } else {
1489                 fprintf(stderr,
1490                         i18n(ScreenSet, ScreenSTYLESDIRErrorNotDir,
1491                              "BScreen::parseMenuFile:"
1492                              " [stylesdir/stylesmenu] error, %s is not a"
1493                              " directory\n"), stylesdir.c_str());
1494               }
1495             } else {
1496               fprintf(stderr,
1497                       i18n(ScreenSet, ScreenSTYLESDIRErrorNoExist,
1498                            "BScreen::parseMenuFile: [stylesdir/stylesmenu]"
1499                            " error, %s does not exist\n"), stylesdir.c_str());
1500             }
1501             break;
1502           }
1503
1504         case 1090: // workspaces
1505           {
1506             if (! *label) {
1507               fprintf(stderr,
1508                       i18n(ScreenSet, ScreenWORKSPACESError,
1509                            "BScreen:parseMenuFile: [workspaces] error, "
1510                            "no menu label defined\n"));
1511               continue;
1512             }
1513
1514             menu->insert(label, workspacemenu);
1515
1516             break;
1517           }
1518         }
1519       }
1520     }
1521   }
1522
1523   return ((menu->getCount() == 0) ? True : False);
1524 }
1525
1526
1527 void BScreen::shutdown(void) {
1528   XSelectInput(blackbox->getXDisplay(), getRootWindow(), NoEventMask);
1529   XSync(blackbox->getXDisplay(), False);
1530
1531   while(! windowList.empty())
1532     unmanageWindow(windowList.front(), True);
1533
1534   slit->shutdown();
1535 }
1536
1537
1538 void BScreen::showPosition(int x, int y) {
1539   if (! geom_visible) {
1540     XMoveResizeWindow(blackbox->getXDisplay(), geom_window,
1541                       (getWidth() - geom_w) / 2,
1542                       (getHeight() - geom_h) / 2, geom_w, geom_h);
1543     XMapWindow(blackbox->getXDisplay(), geom_window);
1544     XRaiseWindow(blackbox->getXDisplay(), geom_window);
1545
1546     geom_visible = True;
1547   }
1548
1549   char label[1024];
1550
1551   sprintf(label, i18n(ScreenSet, ScreenPositionFormat,
1552                       "X: %4d x Y: %4d"), x, y);
1553
1554   XClearWindow(blackbox->getXDisplay(), geom_window);
1555
1556   BPen pen(resource.wstyle.l_text_focus, resource.wstyle.font);
1557   if (i18n.multibyte()) {
1558     XmbDrawString(blackbox->getXDisplay(), geom_window,
1559                   resource.wstyle.fontset, pen.gc(),
1560                   resource.bevel_width, resource.bevel_width -
1561                   resource.wstyle.fontset_extents->max_ink_extent.y,
1562                   label, strlen(label));
1563   } else {
1564     XDrawString(blackbox->getXDisplay(), geom_window,
1565                 pen.gc(), resource.bevel_width,
1566                 resource.wstyle.font->ascent + resource.bevel_width,
1567                 label, strlen(label));
1568   }
1569 }
1570
1571
1572 void BScreen::showGeometry(unsigned int gx, unsigned int gy) {
1573   if (! geom_visible) {
1574     XMoveResizeWindow(blackbox->getXDisplay(), geom_window,
1575                       (getWidth() - geom_w) / 2,
1576                       (getHeight() - geom_h) / 2, geom_w, geom_h);
1577     XMapWindow(blackbox->getXDisplay(), geom_window);
1578     XRaiseWindow(blackbox->getXDisplay(), geom_window);
1579
1580     geom_visible = True;
1581   }
1582
1583   char label[1024];
1584
1585   sprintf(label, i18n(ScreenSet, ScreenGeometryFormat,
1586                       "W: %4d x H: %4d"), gx, gy);
1587
1588   XClearWindow(blackbox->getXDisplay(), geom_window);
1589
1590   BPen pen(resource.wstyle.l_text_focus, resource.wstyle.font);
1591   if (i18n.multibyte()) {
1592     XmbDrawString(blackbox->getXDisplay(), geom_window,
1593                   resource.wstyle.fontset, pen.gc(),
1594                   resource.bevel_width, resource.bevel_width -
1595                   resource.wstyle.fontset_extents->max_ink_extent.y,
1596                   label, strlen(label));
1597   } else {
1598     XDrawString(blackbox->getXDisplay(), geom_window,
1599                 pen.gc(), resource.bevel_width,
1600                 resource.wstyle.font->ascent +
1601                 resource.bevel_width, label, strlen(label));
1602   }
1603 }
1604
1605
1606 void BScreen::hideGeometry(void) {
1607   if (geom_visible) {
1608     XUnmapWindow(blackbox->getXDisplay(), geom_window);
1609     geom_visible = False;
1610   }
1611 }
1612
1613
1614 void BScreen::addStrut(Strut *strut) {
1615   strutList.push_back(strut);
1616 }
1617
1618
1619 void BScreen::removeStrut(Strut *strut) {
1620   strutList.remove(strut);
1621 }
1622
1623
1624 const Rect& BScreen::availableArea(void) const {
1625   if (doFullMax())
1626     return getRect(); // return the full screen
1627   return usableArea;
1628 }
1629
1630
1631 void BScreen::updateAvailableArea(void) {
1632   Rect old_area = usableArea;
1633   usableArea = getRect(); // reset to full screen
1634
1635   /* these values represent offsets from the screen edge
1636    * we look for the biggest offset on each edge and then apply them
1637    * all at once
1638    * do not be confused by the similarity to the names of Rect's members
1639    */
1640   unsigned int current_left = 0, current_right = 0, current_top = 0,
1641     current_bottom = 0;
1642
1643   StrutList::const_iterator it = strutList.begin(), end = strutList.end();
1644
1645   for(; it != end; ++it) {
1646     Strut *strut = *it;
1647     if (strut->left > current_left)
1648       current_left = strut->left;
1649     if (strut->top > current_top)
1650       current_top = strut->top;
1651     if (strut->right > current_right)
1652       current_right = strut->right;
1653     if (strut->bottom > current_bottom)
1654       current_bottom = strut->bottom;
1655   }
1656
1657   usableArea.setPos(current_left, current_top);
1658   usableArea.setSize(usableArea.width() - (current_left + current_right),
1659                      usableArea.height() - (current_top + current_bottom));
1660
1661   if (old_area != usableArea) {
1662     BlackboxWindowList::iterator it = windowList.begin(),
1663       end = windowList.end();
1664     for (; it != end; ++it)
1665       if ((*it)->isMaximized()) (*it)->remaximize();
1666   }
1667 }
1668
1669
1670 Workspace* BScreen::getWorkspace(unsigned int index) {
1671   assert(index < workspacesList.size());
1672   return workspacesList[index];
1673 }
1674
1675
1676 void BScreen::buttonPressEvent(XButtonEvent *xbutton) {
1677   if (xbutton->button == 1) {
1678     if (! isRootColormapInstalled())
1679       image_control->installRootColormap();
1680
1681     if (workspacemenu->isVisible())
1682       workspacemenu->hide();
1683
1684     if (rootmenu->isVisible())
1685       rootmenu->hide();
1686   } else if (xbutton->button == 2) {
1687     int mx = xbutton->x_root - (workspacemenu->getWidth() / 2);
1688     int my = xbutton->y_root - (workspacemenu->getTitleHeight() / 2);
1689
1690     if (mx < 0) mx = 0;
1691     if (my < 0) my = 0;
1692
1693     if (mx + workspacemenu->getWidth() > getWidth())
1694       mx = getWidth() - workspacemenu->getWidth() - getBorderWidth();
1695
1696     if (my + workspacemenu->getHeight() > getHeight())
1697       my = getHeight() - workspacemenu->getHeight() - getBorderWidth();
1698
1699     workspacemenu->move(mx, my);
1700
1701     if (! workspacemenu->isVisible()) {
1702       workspacemenu->removeParent();
1703       workspacemenu->show();
1704     }
1705   } else if (xbutton->button == 3) {
1706     int mx = xbutton->x_root - (rootmenu->getWidth() / 2);
1707     int my = xbutton->y_root - (rootmenu->getTitleHeight() / 2);
1708
1709     if (mx < 0) mx = 0;
1710     if (my < 0) my = 0;
1711
1712     if (mx + rootmenu->getWidth() > getWidth())
1713       mx = getWidth() - rootmenu->getWidth() - getBorderWidth();
1714
1715     if (my + rootmenu->getHeight() > getHeight())
1716       my = getHeight() - rootmenu->getHeight() - getBorderWidth();
1717
1718     rootmenu->move(mx, my);
1719
1720     if (! rootmenu->isVisible()) {
1721       blackbox->checkMenu();
1722       rootmenu->show();
1723     }
1724   }
1725 }
1726
1727
1728 void BScreen::toggleFocusModel(FocusModel model) {
1729   if (model == SloppyFocus) {
1730     saveSloppyFocus(True);
1731   } else {
1732     saveSloppyFocus(False);
1733     saveAutoRaise(False);
1734     saveClickRaise(False);
1735   }
1736
1737   updateFocusModel();
1738 }
1739
1740
1741 void BScreen::updateFocusModel()
1742 {
1743   std::for_each(workspacesList.begin(), workspacesList.end(),
1744                 std::mem_fun(&Workspace::updateFocusModel));
1745 }
1746
1747
1748 BTexture BScreen::readDatabaseTexture(const string &rname,
1749                                       const string &rclass,
1750                                       const string &default_color) {
1751   BTexture texture;
1752   XrmValue value;
1753   char *value_type;
1754
1755   if (XrmGetResource(resource.stylerc, rname.c_str(), rclass.c_str(),
1756                      &value_type, &value))
1757     texture = BTexture(value.addr);
1758   else
1759     texture.setTexture(BTexture::Solid | BTexture::Flat);
1760
1761   // associate this texture with this screen
1762   texture.setDisplay(getBaseDisplay(), getScreenNumber());
1763   texture.setImageControl(image_control);
1764
1765   if (texture.texture() & BTexture::Solid) {
1766     texture.setColor(readDatabaseColor(rname + ".color",
1767                                        rclass + ".Color",
1768                                        default_color));
1769     texture.setColorTo(readDatabaseColor(rname + ".colorTo",
1770                                          rclass + ".ColorTo",
1771                                          default_color));
1772   } else if (texture.texture() & BTexture::Gradient) {
1773     texture.setColor(readDatabaseColor(rname + ".color",
1774                                        rclass + ".Color",
1775                                        default_color));
1776     texture.setColorTo(readDatabaseColor(rname + ".colorTo",
1777                                          rclass + ".ColorTo",
1778                                          default_color));
1779   }
1780
1781   return texture;
1782 }
1783
1784
1785 BColor BScreen::readDatabaseColor(const string &rname, const string &rclass,
1786                                   const string &default_color) {
1787   BColor color;
1788   XrmValue value;
1789   char *value_type;
1790   if (XrmGetResource(resource.stylerc, rname.c_str(), rclass.c_str(),
1791                      &value_type, &value))
1792     color = BColor(value.addr, getBaseDisplay(), getScreenNumber());
1793   else
1794     color = BColor(default_color, getBaseDisplay(), getScreenNumber());
1795   return color;
1796 }
1797
1798
1799 XFontSet BScreen::readDatabaseFontSet(const string &rname,
1800                                       const string &rclass) {
1801   char *defaultFont = "fixed";
1802
1803   bool load_default = True;
1804   XrmValue value;
1805   char *value_type;
1806   XFontSet fontset = 0;
1807   if (XrmGetResource(resource.stylerc, rname.c_str(), rclass.c_str(),
1808                      &value_type, &value) &&
1809       (fontset = createFontSet(value.addr))) {
1810     load_default = False;
1811   }
1812
1813   if (load_default) {
1814     fontset = createFontSet(defaultFont);
1815
1816     if (! fontset) {
1817       fprintf(stderr,
1818               i18n(ScreenSet, ScreenDefaultFontLoadFail,
1819                    "BScreen::setCurrentStyle(): couldn't load default font.\n"));
1820       exit(2);
1821     }
1822   }
1823
1824   return fontset;
1825 }
1826
1827
1828 XFontStruct *BScreen::readDatabaseFont(const string &rname,
1829                                        const string &rclass) {
1830   char *defaultFont = "fixed";
1831
1832   bool load_default = False;
1833   XrmValue value;
1834   char *value_type;
1835   XFontStruct *font = 0;
1836   if (XrmGetResource(resource.stylerc, rname.c_str(), rclass.c_str(),
1837                      &value_type, &value)) {
1838     if ((font = XLoadQueryFont(blackbox->getXDisplay(), value.addr)) == NULL) {
1839       fprintf(stderr,
1840               i18n(ScreenSet, ScreenFontLoadFail,
1841                    "BScreen::setCurrentStyle(): couldn't load font '%s'\n"),
1842               value.addr);
1843
1844       load_default = True;
1845     }
1846   } else {
1847     load_default = True;
1848   }
1849
1850   if (load_default) {
1851     font = XLoadQueryFont(blackbox->getXDisplay(), defaultFont);
1852     if (font == NULL) {
1853       fprintf(stderr,
1854               i18n(ScreenSet, ScreenDefaultFontLoadFail,
1855                    "BScreen::setCurrentStyle(): couldn't load default font.\n"));
1856       exit(2);
1857     }
1858   }
1859
1860   return font;
1861 }
1862
1863
1864 #ifndef    HAVE_STRCASESTR
1865 static const char * strcasestr(const char *str, const char *ptn) {
1866   const char *s2, *p2;
1867   for(; *str; str++) {
1868     for(s2=str,p2=ptn; ; s2++,p2++) {
1869       if (!*p2) return str;
1870       if (toupper(*s2) != toupper(*p2)) break;
1871     }
1872   }
1873   return NULL;
1874 }
1875 #endif // HAVE_STRCASESTR
1876
1877
1878 static const char *getFontElement(const char *pattern, char *buf,
1879                                   int bufsiz, ...) {
1880   const char *p, *v;
1881   char *p2;
1882   va_list va;
1883
1884   va_start(va, bufsiz);
1885   buf[bufsiz-1] = 0;
1886   buf[bufsiz-2] = '*';
1887   while((v = va_arg(va, char *)) != NULL) {
1888     p = strcasestr(pattern, v);
1889     if (p) {
1890       strncpy(buf, p+1, bufsiz-2);
1891       p2 = strchr(buf, '-');
1892       if (p2) *p2=0;
1893       va_end(va);
1894       return p;
1895     }
1896   }
1897   va_end(va);
1898   strncpy(buf, "*", bufsiz);
1899   return NULL;
1900 }
1901
1902
1903 static const char *getFontSize(const char *pattern, int *size) {
1904   const char *p;
1905   const char *p2=NULL;
1906   int n=0;
1907
1908   for (p=pattern; 1; p++) {
1909     if (!*p) {
1910       if (p2!=NULL && n>1 && n<72) {
1911         *size = n; return p2+1;
1912       } else {
1913         *size = 16; return NULL;
1914       }
1915     } else if (*p=='-') {
1916       if (n>1 && n<72 && p2!=NULL) {
1917         *size = n;
1918         return p2+1;
1919       }
1920       p2=p; n=0;
1921     } else if (*p>='0' && *p<='9' && p2!=NULL) {
1922       n *= 10;
1923       n += *p-'0';
1924     } else {
1925       p2=NULL; n=0;
1926     }
1927   }
1928 }
1929
1930
1931 XFontSet BScreen::createFontSet(const string &fontname) {
1932   XFontSet fs;
1933   char **missing, *def = "-";
1934   int nmissing, pixel_size = 0, buf_size = 0;
1935   char weight[FONT_ELEMENT_SIZE], slant[FONT_ELEMENT_SIZE];
1936
1937   fs = XCreateFontSet(blackbox->getXDisplay(),
1938                       fontname.c_str(), &missing, &nmissing, &def);
1939   if (fs && (! nmissing))
1940     return fs;
1941
1942   const char *nfontname = fontname.c_str();
1943 #ifdef    HAVE_SETLOCALE
1944   if (! fs) {
1945     if (nmissing) XFreeStringList(missing);
1946
1947     setlocale(LC_CTYPE, "C");
1948     fs = XCreateFontSet(blackbox->getXDisplay(), fontname.c_str(),
1949                         &missing, &nmissing, &def);
1950     setlocale(LC_CTYPE, "");
1951   }
1952 #endif // HAVE_SETLOCALE
1953
1954   if (fs) {
1955     XFontStruct **fontstructs;
1956     char **fontnames;
1957     XFontsOfFontSet(fs, &fontstructs, &fontnames);
1958     nfontname = fontnames[0];
1959   }
1960
1961   getFontElement(nfontname, weight, FONT_ELEMENT_SIZE,
1962                  "-medium-", "-bold-", "-demibold-", "-regular-", NULL);
1963   getFontElement(nfontname, slant, FONT_ELEMENT_SIZE,
1964                  "-r-", "-i-", "-o-", "-ri-", "-ro-", NULL);
1965   getFontSize(nfontname, &pixel_size);
1966
1967   if (! strcmp(weight, "*"))
1968     strncpy(weight, "medium", FONT_ELEMENT_SIZE);
1969   if (! strcmp(slant, "*"))
1970     strncpy(slant, "r", FONT_ELEMENT_SIZE);
1971   if (pixel_size < 3)
1972     pixel_size = 3;
1973   else if (pixel_size > 97)
1974     pixel_size = 97;
1975
1976   buf_size = strlen(nfontname) + (FONT_ELEMENT_SIZE * 2) + 64;
1977   char *pattern2 = new char[buf_size];
1978   sprintf(pattern2,
1979            "%s,"
1980            "-*-*-%s-%s-*-*-%d-*-*-*-*-*-*-*,"
1981            "-*-*-*-*-*-*-%d-*-*-*-*-*-*-*,*",
1982            nfontname, weight, slant, pixel_size, pixel_size);
1983   nfontname = pattern2;
1984
1985   if (nmissing)
1986     XFreeStringList(missing);
1987   if (fs)
1988     XFreeFontSet(blackbox->getXDisplay(), fs);
1989
1990   fs = XCreateFontSet(blackbox->getXDisplay(), nfontname, &missing,
1991                       &nmissing, &def);
1992
1993   delete [] pattern2;
1994
1995   return fs;
1996 }