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