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