]> icculus.org git repositories - mikachu/openbox.git/blob - src/Toolbar.cc
Take menu file from rc file if it isn't specified on the command line.
[mikachu/openbox.git] / src / Toolbar.cc
1 // -*- mode: C++; indent-tabs-mode: nil; -*-
2 // Toolbar.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/keysym.h>
30
31 #ifdef HAVE_STRING_H
32 #  include <string.h>
33 #endif // HAVE_STRING_H
34
35 #ifdef    HAVE_STDIO_H
36 #  include <stdio.h>
37 #endif // HAVE_STDIO_H
38
39 #ifdef    TIME_WITH_SYS_TIME
40 # include <sys/time.h>
41 # include <time.h>
42 #else // !TIME_WITH_SYS_TIME
43 # ifdef    HAVE_SYS_TIME_H
44 #  include <sys/time.h>
45 # else // !HAVE_SYS_TIME_H
46 #  include <time.h>
47 # endif // HAVE_SYS_TIME_H
48 #endif // TIME_WITH_SYS_TIME
49 }
50
51 #include <string>
52 using std::string;
53
54 #include "i18n.hh"
55 #include "blackbox.hh"
56 #include "Font.hh"
57 #include "GCCache.hh"
58 #include "Image.hh"
59 #include "Screen.hh"
60 #include "Toolbar.hh"
61 #include "Window.hh"
62 #include "Workspace.hh"
63 #include "Clientmenu.hh"
64 #include "Workspacemenu.hh"
65 #include "Slit.hh"
66
67
68 static long aMinuteFromNow(void) {
69   timeval now;
70   gettimeofday(&now, 0);
71   return ((60 - (now.tv_sec % 60)) * 1000);
72 }
73
74
75 Toolbar::Toolbar(BScreen *scrn) {
76   screen = scrn;
77   blackbox = screen->getBlackbox();
78   toolbarstr = "session.screen" + itostring(screen->getScreenNumber()) +
79     ".toolbar.";
80   config = blackbox->getConfig();
81
82   load_rc();
83
84   // get the clock updating every minute
85   clock_timer = new BTimer(blackbox, this);
86   clock_timer->setTimeout(aMinuteFromNow());
87   clock_timer->recurring(True);
88   clock_timer->start();
89   frame.minute = frame.hour = -1;
90
91   hide_handler.toolbar = this;
92   hide_timer = new BTimer(blackbox, &hide_handler);
93   hide_timer->setTimeout(blackbox->getAutoRaiseDelay());
94
95   editing = False;
96   new_name_pos = 0;
97
98   toolbarmenu = new Toolbarmenu(this);
99
100   display = blackbox->getXDisplay();
101   XSetWindowAttributes attrib;
102   unsigned long create_mask = CWBackPixmap | CWBackPixel | CWBorderPixel |
103                               CWColormap | CWOverrideRedirect | CWEventMask;
104   attrib.background_pixmap = None;
105   attrib.background_pixel = attrib.border_pixel =
106     screen->getBorderColor()->pixel();
107   attrib.colormap = screen->getColormap();
108   attrib.override_redirect = True;
109   attrib.event_mask = ButtonPressMask | ButtonReleaseMask |
110                       EnterWindowMask | LeaveWindowMask;
111
112   frame.window =
113     XCreateWindow(display, screen->getRootWindow(), 0, 0, 1, 1, 0,
114                   screen->getDepth(), InputOutput, screen->getVisual(),
115                   create_mask, &attrib);
116   blackbox->saveToolbarSearch(frame.window, this);
117
118   attrib.event_mask = ButtonPressMask | ButtonReleaseMask | ExposureMask |
119                       KeyPressMask | EnterWindowMask;
120
121   frame.workspace_label =
122     XCreateWindow(display, frame.window, 0, 0, 1, 1, 0, screen->getDepth(),
123                   InputOutput, screen->getVisual(), create_mask, &attrib);
124   blackbox->saveToolbarSearch(frame.workspace_label, this);
125
126   frame.window_label =
127     XCreateWindow(display, frame.window, 0, 0, 1, 1, 0, screen->getDepth(),
128                   InputOutput, screen->getVisual(), create_mask, &attrib);
129   blackbox->saveToolbarSearch(frame.window_label, this);
130
131   frame.clock =
132     XCreateWindow(display, frame.window, 0, 0, 1, 1, 0, screen->getDepth(),
133                   InputOutput, screen->getVisual(), create_mask, &attrib);
134   blackbox->saveToolbarSearch(frame.clock, this);
135
136   frame.psbutton =
137     XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0, screen->getDepth(),
138                   InputOutput, screen->getVisual(), create_mask, &attrib);
139   blackbox->saveToolbarSearch(frame.psbutton, this);
140
141   frame.nsbutton =
142     XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0, screen->getDepth(),
143                   InputOutput, screen->getVisual(), create_mask, &attrib);
144   blackbox->saveToolbarSearch(frame.nsbutton, this);
145
146   frame.pwbutton =
147     XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0, screen->getDepth(),
148                   InputOutput, screen->getVisual(), create_mask, &attrib);
149   blackbox->saveToolbarSearch(frame.pwbutton, this);
150
151   frame.nwbutton =
152     XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0, screen->getDepth(),
153                   InputOutput, screen->getVisual(), create_mask, &attrib);
154   blackbox->saveToolbarSearch(frame.nwbutton, this);
155
156   frame.base = frame.label = frame.wlabel = frame.clk = frame.button =
157     frame.pbutton = None;
158
159   reconfigure();
160   mapToolbar();
161 }
162
163
164 Toolbar::~Toolbar(void) {
165   unmapToolbar();
166
167   if (frame.base) screen->getImageControl()->removeImage(frame.base);
168   if (frame.label) screen->getImageControl()->removeImage(frame.label);
169   if (frame.wlabel) screen->getImageControl()->removeImage(frame.wlabel);
170   if (frame.clk) screen->getImageControl()->removeImage(frame.clk);
171   if (frame.button) screen->getImageControl()->removeImage(frame.button);
172   if (frame.pbutton) screen->getImageControl()->removeImage(frame.pbutton);
173
174   blackbox->removeToolbarSearch(frame.window);
175   blackbox->removeToolbarSearch(frame.workspace_label);
176   blackbox->removeToolbarSearch(frame.window_label);
177   blackbox->removeToolbarSearch(frame.clock);
178   blackbox->removeToolbarSearch(frame.psbutton);
179   blackbox->removeToolbarSearch(frame.nsbutton);
180   blackbox->removeToolbarSearch(frame.pwbutton);
181   blackbox->removeToolbarSearch(frame.nwbutton);
182
183   XDestroyWindow(display, frame.workspace_label);
184   XDestroyWindow(display, frame.window_label);
185   XDestroyWindow(display, frame.clock);
186
187   XDestroyWindow(display, frame.window);
188
189   delete hide_timer;
190   delete clock_timer;
191   delete toolbarmenu;
192 }
193
194
195 void Toolbar::mapToolbar() {
196   if (!screen->doHideToolbar()) {
197     //not hidden, so windows should not maximize over the toolbar
198     XMapSubwindows(display, frame.window);
199     XMapWindow(display, frame.window);
200   }
201   screen->addStrut(&strut);
202   updateStrut();
203 }
204
205
206 void Toolbar::unmapToolbar() {
207   if (toolbarmenu->isVisible())
208     toolbarmenu->hide();
209   //hidden so we can maximize over the toolbar
210   screen->removeStrut(&strut);
211   screen->updateAvailableArea();
212
213   XUnmapWindow(display, frame.window);
214   updateStrut();
215 }
216
217
218 void Toolbar::saveOnTop(bool b) {
219   on_top = b;
220   config->setValue(toolbarstr + "onTop", on_top);
221 }
222
223
224 void Toolbar::saveAutoHide(bool b) {
225   do_auto_hide = b;
226   config->setValue(toolbarstr + "autoHide", do_auto_hide);
227 }
228
229
230 void Toolbar::saveWidthPercent(unsigned int w) {
231   width_percent = w;
232   config->setValue(toolbarstr + "widthPercent", width_percent);
233 }
234
235
236 void Toolbar::savePlacement(int p) {
237   placement = p;
238   const char *pname;
239   switch (placement) {
240   case TopLeft: pname = "TopLeft"; break;
241   case BottomLeft: pname = "BottomLeft"; break;
242   case TopCenter: pname = "TopCenter"; break;
243   case TopRight: pname = "TopRight"; break;
244   case BottomRight: pname = "BottomRight"; break;
245   case BottomCenter: default: pname = "BottomCenter"; break;
246   }
247   config->setValue(toolbarstr + "placement", pname);
248 }
249
250
251 void Toolbar::save_rc(void) {
252   saveOnTop(on_top);
253   saveAutoHide(do_auto_hide);
254   saveWidthPercent(width_percent);
255   savePlacement(placement);
256 }
257
258
259 void Toolbar::load_rc(void) {
260   string s;
261   
262   if (! config->getValue(toolbarstr + "onTop", on_top))
263     on_top = false;
264
265   if (! config->getValue(toolbarstr + "autoHide", do_auto_hide))
266     do_auto_hide = false;
267   hidden = do_auto_hide;
268
269   if (! config->getValue(toolbarstr + "widthPercent", width_percent) ||
270       width_percent == 0 || width_percent > 100)
271     width_percent = 66;
272
273   if (config->getValue(toolbarstr + "placement", s)) {
274     if (s == "TopLeft")
275       placement = TopLeft;
276     else if (s == "BottomLeft")
277       placement = BottomLeft;
278     else if (s == "TopCenter")
279       placement = TopCenter;
280     else if (s == "TopRight")
281       placement = TopRight;
282     else if (s == "BottomRight")
283       placement = BottomRight;
284     else //if (s == "BottomCenter")
285       placement = BottomCenter;
286   } else
287     placement = BottomCenter;
288 }
289
290
291 void Toolbar::reconfigure(void) {
292   unsigned int width, height;
293  
294   width = (screen->getWidth() * width_percent) / 100;
295   height = screen->getToolbarStyle()->font->height();
296
297   frame.bevel_w = screen->getBevelWidth();
298   frame.button_w = height;
299   height += 2;
300   frame.label_h = height;
301   height += (frame.bevel_w * 2);
302
303   frame.rect.setSize(width, height);
304
305   int x, y;
306   switch (placement) {
307   case TopLeft:
308   case TopRight:
309   case TopCenter:
310     if (placement == TopLeft)
311       x = 0;
312     else if (placement == TopRight)
313       x = screen->getWidth() - frame.rect.width()
314         - (screen->getBorderWidth() * 2);
315     else
316       x = (screen->getWidth() - frame.rect.width()) / 2;
317
318     y = 0;
319
320     frame.x_hidden = x;
321     frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
322                      - frame.rect.height();
323     break;
324
325   case BottomLeft:
326   case BottomRight:
327   case BottomCenter:
328   default:
329     if (placement == BottomLeft)
330       x = 0;
331     else if (placement == BottomRight)
332       x = screen->getWidth() - frame.rect.width()
333         - (screen->getBorderWidth() * 2);
334     else
335       x = (screen->getWidth() - frame.rect.width()) / 2;
336
337     y = screen->getHeight() - frame.rect.height()
338       - (screen->getBorderWidth() * 2);
339
340     frame.x_hidden = x;
341     frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
342                      - screen->getBorderWidth();
343     break;
344   }
345
346   frame.rect.setPos(x, y);
347
348   updateStrut();
349
350 #ifdef    HAVE_STRFTIME
351   time_t ttmp = time(NULL);
352
353   frame.clock_w = 0;
354   if (ttmp != -1) {
355     struct tm *tt = localtime(&ttmp);
356     if (tt) {
357       char t[1024];
358       int len = strftime(t, 1024, screen->getStrftimeFormat(), tt);
359       if (len == 0) { // invalid time format found
360         screen->saveStrftimeFormat("%I:%M %p"); // so use the default
361         strftime(t, 1024, screen->getStrftimeFormat(), tt);
362       }
363       // find the length of the rendered string and add room for two extra
364       // characters to it.  This allows for variable width output of the fonts
365       BFont *font = screen->getToolbarStyle()->font;
366       frame.clock_w = font->measureString(t) + font->maxCharWidth() * 2;
367     }
368   }
369 #else // !HAVE_STRFTIME
370   {
371     string s = i18n(ToolbarSet, ToolbarNoStrftimeLength, "00:00000");
372     frame.clock_w = screen->getToolbarStyle()->font->measureString(s);
373   }
374 #endif // HAVE_STRFTIME
375
376   frame.workspace_label_w = 0;
377
378   for (unsigned int i = 0; i < screen->getWorkspaceCount(); i++) {
379     const string& workspace_name = screen->getWorkspace(i)->getName();
380     width = screen->getToolbarStyle()->font->measureString(workspace_name);
381     if (width > frame.workspace_label_w) frame.workspace_label_w = width;
382   }
383
384   frame.workspace_label_w = frame.clock_w =
385     std::max(frame.workspace_label_w, frame.clock_w) + (frame.bevel_w * 4);
386
387   // XXX: where'd the +6 come from?
388   frame.window_label_w =
389     (frame.rect.width() - (frame.clock_w + (frame.button_w * 4) +
390                            frame.workspace_label_w + (frame.bevel_w * 8) + 6));
391
392   if (hidden) {
393     XMoveResizeWindow(display, frame.window, frame.x_hidden, frame.y_hidden,
394                       frame.rect.width(), frame.rect.height());
395   } else {
396     XMoveResizeWindow(display, frame.window, frame.rect.x(), frame.rect.y(),
397                       frame.rect.width(), frame.rect.height());
398   }
399
400   XMoveResizeWindow(display, frame.workspace_label, frame.bevel_w,
401                     frame.bevel_w, frame.workspace_label_w,
402                     frame.label_h);
403   XMoveResizeWindow(display, frame.psbutton,
404                     ((frame.bevel_w * 2) + frame.workspace_label_w + 1),
405                     frame.bevel_w + 1, frame.button_w, frame.button_w);
406   XMoveResizeWindow(display, frame.nsbutton,
407                     ((frame.bevel_w * 3) + frame.workspace_label_w +
408                      frame.button_w + 2), frame.bevel_w + 1, frame.button_w,
409                     frame.button_w);
410   XMoveResizeWindow(display, frame.window_label,
411                     ((frame.bevel_w * 4) + (frame.button_w * 2) +
412                      frame.workspace_label_w + 3), frame.bevel_w,
413                     frame.window_label_w, frame.label_h);
414   XMoveResizeWindow(display, frame.pwbutton,
415                     ((frame.bevel_w * 5) + (frame.button_w * 2) +
416                      frame.workspace_label_w + frame.window_label_w + 4),
417                     frame.bevel_w + 1, frame.button_w, frame.button_w);
418   XMoveResizeWindow(display, frame.nwbutton,
419                     ((frame.bevel_w * 6) + (frame.button_w * 3) +
420                      frame.workspace_label_w + frame.window_label_w + 5),
421                     frame.bevel_w + 1, frame.button_w, frame.button_w);
422   XMoveResizeWindow(display, frame.clock,
423                     frame.rect.width() - frame.clock_w - (frame.bevel_w * 2),
424                     frame.bevel_w, frame.clock_w, frame.label_h);
425
426   ToolbarStyle *style = screen->getToolbarStyle();
427   frame.base = style->toolbar.render(frame.rect.width(), frame.rect.height(),
428                                      frame.base);
429   if (! frame.base)
430     XSetWindowBackground(display, frame.window,
431                          style->toolbar.color().pixel());
432   else
433     XSetWindowBackgroundPixmap(display, frame.window, frame.base);
434
435   frame.label = style->window.render(frame.window_label_w, frame.label_h,
436                                      frame.label);
437   if (! frame.label)
438     XSetWindowBackground(display, frame.window_label,
439                          style->window.color().pixel());
440   else
441     XSetWindowBackgroundPixmap(display, frame.window_label, frame.label);
442
443   frame.wlabel = style->label.render(frame.workspace_label_w, frame.label_h,
444                                 frame.wlabel);
445   if (! frame.wlabel)
446     XSetWindowBackground(display, frame.workspace_label,
447                          style->label.color().pixel());
448   else
449     XSetWindowBackgroundPixmap(display, frame.workspace_label, frame.wlabel);
450
451   frame.clk = style->clock.render(frame.clock_w, frame.label_h, frame.clk);
452   if (! frame.clk)
453     XSetWindowBackground(display, frame.clock, style->clock.color().pixel());
454   else
455     XSetWindowBackgroundPixmap(display, frame.clock, frame.clk);
456
457   frame.button = style->button.render(frame.button_w, frame.button_w,
458                                 frame.button);
459   if (! frame.button) {
460     frame.button_pixel = style->button.color().pixel();
461     XSetWindowBackground(display, frame.psbutton, frame.button_pixel);
462     XSetWindowBackground(display, frame.nsbutton, frame.button_pixel);
463     XSetWindowBackground(display, frame.pwbutton, frame.button_pixel);
464     XSetWindowBackground(display, frame.nwbutton, frame.button_pixel);
465   } else {
466     XSetWindowBackgroundPixmap(display, frame.psbutton, frame.button);
467     XSetWindowBackgroundPixmap(display, frame.nsbutton, frame.button);
468     XSetWindowBackgroundPixmap(display, frame.pwbutton, frame.button);
469     XSetWindowBackgroundPixmap(display, frame.nwbutton, frame.button);
470   }
471
472   frame.pbutton = style->pressed.render(frame.button_w, frame.button_w,
473                                         frame.pbutton);
474   if (! frame.pbutton)
475     frame.pbutton_pixel = style->pressed.color().pixel();
476
477   XSetWindowBorder(display, frame.window,
478                    screen->getBorderColor()->pixel());
479   XSetWindowBorderWidth(display, frame.window, screen->getBorderWidth());
480
481   XClearWindow(display, frame.window);
482   XClearWindow(display, frame.workspace_label);
483   XClearWindow(display, frame.window_label);
484   XClearWindow(display, frame.clock);
485   XClearWindow(display, frame.psbutton);
486   XClearWindow(display, frame.nsbutton);
487   XClearWindow(display, frame.pwbutton);
488   XClearWindow(display, frame.nwbutton);
489
490   redrawWindowLabel();
491   redrawWorkspaceLabel();
492   redrawPrevWorkspaceButton();
493   redrawNextWorkspaceButton();
494   redrawPrevWindowButton();
495   redrawNextWindowButton();
496   checkClock(True);
497
498   toolbarmenu->reconfigure();
499 }
500
501
502 void Toolbar::updateStrut(void) {
503   // left and right are always 0
504   strut.top = strut.bottom = 0;
505
506   // when hidden only one border is visible
507   unsigned int border_width = screen->getBorderWidth();
508   if (! do_auto_hide)
509     border_width *= 2;
510
511   if (! screen->doHideToolbar()) {
512     switch(placement) {
513     case TopLeft:
514     case TopCenter:
515     case TopRight:
516       strut.top = getExposedHeight() + border_width;
517       break;
518     default:
519       strut.bottom = getExposedHeight() + border_width;
520     }
521   }
522
523   screen->updateAvailableArea();
524 }
525
526
527 #ifdef    HAVE_STRFTIME
528 void Toolbar::checkClock(bool redraw) {
529 #else // !HAVE_STRFTIME
530 void Toolbar::checkClock(bool redraw, bool date) {
531 #endif // HAVE_STRFTIME
532   time_t tmp = 0;
533   struct tm *tt = 0;
534
535   if ((tmp = time(NULL)) != -1) {
536     if (! (tt = localtime(&tmp))) return;
537     if (tt->tm_min != frame.minute || tt->tm_hour != frame.hour) {
538       frame.hour = tt->tm_hour;
539       frame.minute = tt->tm_min;
540       XClearWindow(display, frame.clock);
541       redraw = True;
542     }
543   }
544
545   if (redraw) {
546 #ifdef    HAVE_STRFTIME
547     char t[1024];
548     if (! strftime(t, 1024, screen->getStrftimeFormat(), tt))
549       return;
550 #else // !HAVE_STRFTIME
551     char t[9];
552     if (date) {
553       // format the date... with special consideration for y2k ;)
554       if (screen->getDateFormat() == Blackbox::B_EuropeanDate)
555         sprintf(t, 18n(ToolbarSet, ToolbarNoStrftimeDateFormatEu,
556                        "%02d.%02d.%02d"),
557                 tt->tm_mday, tt->tm_mon + 1,
558                 (tt->tm_year >= 100) ? tt->tm_year - 100 : tt->tm_year);
559       else
560         sprintf(t, i18n(ToolbarSet, ToolbarNoStrftimeDateFormat,
561                         "%02d/%02d/%02d"),
562                 tt->tm_mon + 1, tt->tm_mday,
563                 (tt->tm_year >= 100) ? tt->tm_year - 100 : tt->tm_year);
564     } else {
565       if (screen->isClock24Hour())
566         sprintf(t, i18n(ToolbarSet, ToolbarNoStrftimeTimeFormat24,
567                         "  %02d:%02d "),
568                 frame.hour, frame.minute);
569       else
570         sprintf(t, i18n(ToolbarSet, ToolbarNoStrftimeTimeFormat12,
571                         "%02d:%02d %sm"),
572                 ((frame.hour > 12) ? frame.hour - 12 :
573                  ((frame.hour == 0) ? 12 : frame.hour)), frame.minute,
574                 ((frame.hour >= 12) ?
575                  i18n(ToolbarSet, ToolbarNoStrftimeTimeFormatP, "p") :
576                  i18n(ToolbarSet, ToolbarNoStrftimeTimeFormatA, "a")));
577     }
578 #endif // HAVE_STRFTIME
579
580     ToolbarStyle *style = screen->getToolbarStyle();
581
582     int pos = frame.bevel_w * 2; // this is modified by doJustify()
583     style->doJustify(t, pos, frame.clock_w, frame.bevel_w * 4);
584
585 #ifdef    XFT
586     XClearWindow(display, frame.clock);
587 #endif // XFT
588
589     style->font->drawString(frame.clock, pos, 1, style->c_text, t);
590   }
591 }
592
593
594 void Toolbar::redrawWindowLabel(bool redraw) {
595   BlackboxWindow *foc = screen->getBlackbox()->getFocusedWindow();
596   if (! foc) {
597     XClearWindow(display, frame.window_label);
598     return;
599   }
600
601 #ifdef    XFT
602   redraw = true;
603 #endif // XFT
604
605   if (redraw)
606     XClearWindow(display, frame.window_label);
607
608   if (foc->getScreen() != screen) return;
609
610   const char *title = foc->getTitle();
611   ToolbarStyle *style = screen->getToolbarStyle();
612
613   int pos = frame.bevel_w * 2; // modified by doJustify()
614   style->doJustify(title, pos, frame.window_label_w, frame.bevel_w * 4);
615   style->font->drawString(frame.window_label, pos, 1, style->w_text, title);
616 }
617
618
619 void Toolbar::redrawWorkspaceLabel(bool redraw) {
620   const string& name = screen->getCurrentWorkspace()->getName();
621
622 #ifdef    XFT
623   redraw = true;
624 #endif // XFT
625
626   if (redraw)
627     XClearWindow(display, frame.workspace_label);
628
629   ToolbarStyle *style = screen->getToolbarStyle();
630
631   int pos = frame.bevel_w * 2;
632   style->doJustify(name.c_str(), pos, frame.workspace_label_w,
633                    frame.bevel_w * 4);
634   style->font->drawString(frame.workspace_label, pos, 1, style->l_text, name);
635 }
636
637
638 void Toolbar::drawArrow(Drawable surface, bool left) const {
639   ToolbarStyle *style = screen->getToolbarStyle();
640
641   BPen pen(style->b_pic);
642
643   int hh = frame.button_w / 2, hw = frame.button_w / 2;
644   XPoint pts[3];
645   const int bullet_size = 3;
646
647
648   if (left) {
649 #ifdef    BITMAPBUTTONS
650     if (style->left_button.mask != None) {
651       XSetClipMask(blackbox->getXDisplay(), pen.gc(), style->left_button.mask);
652       XSetClipOrigin(blackbox->getXDisplay(), pen.gc(),
653                      (frame.button_w - style->left_button.w)/2,
654                      (frame.button_w - style->left_button.h)/2);
655
656       XFillRectangle(blackbox->getXDisplay(), surface, pen.gc(),
657                      (frame.button_w - style->left_button.w)/2,
658                      (frame.button_w - style->left_button.h)/2,
659                      style->left_button.w, style->left_button.h);
660
661       XSetClipMask(blackbox->getXDisplay(), pen.gc(), None);
662       XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0);
663     } else {
664 #endif // BITMAPBUTTONS
665       pts[0].x = hw - bullet_size;
666       pts[0].y = hh;
667       pts[1].x = 2 * bullet_size;
668       pts[1].y = bullet_size;
669       pts[2].x = 0;
670       pts[2].y = -(2 * bullet_size);
671       XFillPolygon(display, surface, pen.gc(), pts, 3, Convex,
672                    CoordModePrevious);
673 #ifdef    BITMAPBUTTONS      
674     }
675 #endif // BITMAPBUTTONS
676   } else {
677 #ifdef    BITMAPBUTTONS
678     if (style->right_button.mask != None) {
679       XSetClipMask(blackbox->getXDisplay(), pen.gc(),
680                    style->right_button.mask);
681       XSetClipOrigin(blackbox->getXDisplay(), pen.gc(),
682                      (frame.button_w - style->right_button.w)/2,
683                      (frame.button_w - style->right_button.h)/2);
684
685       XFillRectangle(blackbox->getXDisplay(), surface, pen.gc(),
686                      (frame.button_w - style->right_button.w)/2,
687                      (frame.button_w - style->right_button.h)/2,
688                      (frame.button_w + style->right_button.w)/2,
689                      (frame.button_w + style->right_button.h)/2);
690
691       XSetClipMask(blackbox->getXDisplay(), pen.gc(), None);
692       XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0);
693     } else {
694 #endif // BITMAPBUTTONS
695       pts[0].x = hw - bullet_size;
696       pts[0].y = hh - bullet_size;
697       pts[1].x = (2 * bullet_size);
698       pts[1].y =  bullet_size;
699       pts[2].x = -(2 * bullet_size);
700       pts[2].y = bullet_size;
701       XFillPolygon(display, surface, pen.gc(), pts, 3, Convex,
702                    CoordModePrevious);
703 #ifdef    BITMAPBUTTONS
704     }
705 #endif
706   }
707 }
708
709
710 void Toolbar::redrawPrevWorkspaceButton(bool pressed, bool redraw) {
711   if (redraw) {
712     if (pressed) {
713       if (frame.pbutton)
714         XSetWindowBackgroundPixmap(display, frame.psbutton, frame.pbutton);
715       else
716         XSetWindowBackground(display, frame.psbutton, frame.pbutton_pixel);
717     } else {
718       if (frame.button)
719         XSetWindowBackgroundPixmap(display, frame.psbutton, frame.button);
720       else
721         XSetWindowBackground(display, frame.psbutton, frame.button_pixel);
722     }
723     XClearWindow(display, frame.psbutton);
724   }
725
726   drawArrow(frame.psbutton, True);
727 }
728
729
730 void Toolbar::redrawNextWorkspaceButton(bool pressed, bool redraw) {
731   if (redraw) {
732     if (pressed) {
733       if (frame.pbutton)
734         XSetWindowBackgroundPixmap(display, frame.nsbutton, frame.pbutton);
735       else
736         XSetWindowBackground(display, frame.nsbutton, frame.pbutton_pixel);
737     } else {
738       if (frame.button)
739         XSetWindowBackgroundPixmap(display, frame.nsbutton, frame.button);
740       else
741         XSetWindowBackground(display, frame.nsbutton, frame.button_pixel);
742     }
743     XClearWindow(display, frame.nsbutton);
744   }
745
746   drawArrow(frame.nsbutton, False);
747 }
748
749
750 void Toolbar::redrawPrevWindowButton(bool pressed, bool redraw) {
751   if (redraw) {
752     if (pressed) {
753       if (frame.pbutton)
754         XSetWindowBackgroundPixmap(display, frame.pwbutton, frame.pbutton);
755       else
756         XSetWindowBackground(display, frame.pwbutton, frame.pbutton_pixel);
757     } else {
758       if (frame.button)
759         XSetWindowBackgroundPixmap(display, frame.pwbutton, frame.button);
760       else
761         XSetWindowBackground(display, frame.pwbutton, frame.button_pixel);
762     }
763     XClearWindow(display, frame.pwbutton);
764   }
765
766   drawArrow(frame.pwbutton, True);
767 }
768
769
770 void Toolbar::redrawNextWindowButton(bool pressed, bool redraw) {
771   if (redraw) {
772     if (pressed) {
773       if (frame.pbutton)
774         XSetWindowBackgroundPixmap(display, frame.nwbutton, frame.pbutton);
775       else
776         XSetWindowBackground(display, frame.nwbutton, frame.pbutton_pixel);
777     } else {
778       if (frame.button)
779         XSetWindowBackgroundPixmap(display, frame.nwbutton, frame.button);
780       else
781         XSetWindowBackground(display, frame.nwbutton, frame.button_pixel);
782     }
783     XClearWindow(display, frame.nwbutton);
784   }
785
786   drawArrow(frame.nwbutton, False);
787 }
788
789
790 void Toolbar::edit(void) {
791   Window window;
792   int foo;
793
794   editing = True;
795   XGetInputFocus(display, &window, &foo);
796   if (window == frame.workspace_label)
797     return;
798
799   XSetInputFocus(display, frame.workspace_label,
800                  RevertToPointerRoot, CurrentTime);
801   XClearWindow(display, frame.workspace_label);
802
803   blackbox->setNoFocus(True);
804   if (blackbox->getFocusedWindow())
805     blackbox->getFocusedWindow()->setFocusFlag(False);
806
807   ToolbarStyle *style = screen->getToolbarStyle();
808   BPen pen(style->l_text);
809   XDrawRectangle(display, frame.workspace_label, pen.gc(),
810                  frame.workspace_label_w / 2, 0, 1,
811                  frame.label_h - 1);
812   // change the background of the window to that of an active window label
813   BTexture *texture = &(screen->getWindowStyle()->l_focus);
814   frame.wlabel = texture->render(frame.workspace_label_w, frame.label_h,
815                                  frame.wlabel);
816   if (! frame.wlabel)
817     XSetWindowBackground(display, frame.workspace_label,
818                          texture->color().pixel());
819   else
820     XSetWindowBackgroundPixmap(display, frame.workspace_label, frame.wlabel);
821 }
822
823
824 void Toolbar::buttonPressEvent(const XButtonEvent *be) {
825   if (be->button == 1) {
826     if (be->window == frame.psbutton)
827       redrawPrevWorkspaceButton(True, True);
828     else if (be->window == frame.nsbutton)
829       redrawNextWorkspaceButton(True, True);
830     else if (be->window == frame.pwbutton)
831       redrawPrevWindowButton(True, True);
832     else if (be->window == frame.nwbutton)
833       redrawNextWindowButton(True, True);
834 #ifndef   HAVE_STRFTIME
835     else if (be->window == frame.clock) {
836       XClearWindow(display, frame.clock);
837       checkClock(True, True);
838     }
839 #endif // HAVE_STRFTIME
840     else if (! on_top) {
841       Window w[1] = { frame.window };
842       screen->raiseWindows(w, 1);
843     }
844   } else if (be->button == 2 && (! on_top)) {
845     XLowerWindow(display, frame.window);
846   } else if (be->button == 3) {
847     if (toolbarmenu->isVisible()) {
848       toolbarmenu->hide();
849     } else {
850       int x, y;
851
852       x = be->x_root - (toolbarmenu->getWidth() / 2);
853       y = be->y_root - (toolbarmenu->getHeight() / 2);
854
855       if (x < 0)
856         x = 0;
857       else if (x + toolbarmenu->getWidth() > screen->getWidth())
858         x = screen->getWidth() - toolbarmenu->getWidth();
859
860       if (y < 0)
861         y = 0;
862       else if (y + toolbarmenu->getHeight() > screen->getHeight())
863         y = screen->getHeight() - toolbarmenu->getHeight();
864
865       toolbarmenu->move(x, y);
866       toolbarmenu->show();
867     }
868   }
869 }
870
871
872
873 void Toolbar::buttonReleaseEvent(const XButtonEvent *re) {
874   if (re->button == 1) {
875     if (re->window == frame.psbutton) {
876       redrawPrevWorkspaceButton(False, True);
877
878       if (re->x >= 0 && re->x < static_cast<signed>(frame.button_w) &&
879           re->y >= 0 && re->y < static_cast<signed>(frame.button_w))
880        if (screen->getCurrentWorkspace()->getID() > 0)
881           screen->changeWorkspaceID(screen->getCurrentWorkspace()->
882                                     getID() - 1);
883         else
884           screen->changeWorkspaceID(screen->getWorkspaceCount() - 1);
885     } else if (re->window == frame.nsbutton) {
886       redrawNextWorkspaceButton(False, True);
887
888       if (re->x >= 0 && re->x < static_cast<signed>(frame.button_w) &&
889           re->y >= 0 && re->y < static_cast<signed>(frame.button_w))
890         if (screen->getCurrentWorkspace()->getID() <
891             (screen->getWorkspaceCount() - 1))
892           screen->changeWorkspaceID(screen->getCurrentWorkspace()->
893                                     getID() + 1);
894         else
895           screen->changeWorkspaceID(0);
896     } else if (re->window == frame.pwbutton) {
897       redrawPrevWindowButton(False, True);
898
899       if (re->x >= 0 && re->x < static_cast<signed>(frame.button_w) &&
900           re->y >= 0 && re->y < static_cast<signed>(frame.button_w))
901         screen->prevFocus();
902     } else if (re->window == frame.nwbutton) {
903       redrawNextWindowButton(False, True);
904
905       if (re->x >= 0 && re->x < static_cast<signed>(frame.button_w) &&
906           re->y >= 0 && re->y < static_cast<signed>(frame.button_w))
907         screen->nextFocus();
908     } else if (re->window == frame.window_label)
909       screen->raiseFocus();
910 #ifndef   HAVE_STRFTIME
911     else if (re->window == frame.clock) {
912       XClearWindow(display, frame.clock);
913       checkClock(True);
914     }
915 #endif // HAVE_STRFTIME
916   }
917 }
918
919
920 void Toolbar::enterNotifyEvent(const XCrossingEvent *) {
921   if (! do_auto_hide)
922     return;
923
924   if (hidden) {
925     if (! hide_timer->isTiming()) hide_timer->start();
926   } else {
927     if (hide_timer->isTiming()) hide_timer->stop();
928   }
929 }
930
931 void Toolbar::leaveNotifyEvent(const XCrossingEvent *) {
932   if (! do_auto_hide)
933     return;
934
935   if (hidden) {
936     if (hide_timer->isTiming()) hide_timer->stop();
937   } else if (! toolbarmenu->isVisible()) {
938     if (! hide_timer->isTiming()) hide_timer->start();
939   }
940 }
941
942
943 void Toolbar::exposeEvent(const XExposeEvent *ee) {
944   if (ee->window == frame.clock) checkClock(True);
945   else if (ee->window == frame.workspace_label && (! editing))
946     redrawWorkspaceLabel();
947   else if (ee->window == frame.window_label) redrawWindowLabel();
948   else if (ee->window == frame.psbutton) redrawPrevWorkspaceButton();
949   else if (ee->window == frame.nsbutton) redrawNextWorkspaceButton();
950   else if (ee->window == frame.pwbutton) redrawPrevWindowButton();
951   else if (ee->window == frame.nwbutton) redrawNextWindowButton();
952 }
953
954
955 void Toolbar::keyPressEvent(const XKeyEvent *ke) {
956   if (ke->window == frame.workspace_label && editing) {
957     if (new_workspace_name.empty()) {
958       new_name_pos = 0;
959     }
960
961     KeySym ks;
962     char keychar[1];
963     XLookupString(const_cast<XKeyEvent*>(ke), keychar, 1, &ks, 0);
964
965     // either we are told to end with a return or we hit 127 chars
966     if (ks == XK_Return || new_name_pos == 127) {
967       editing = False;
968
969       blackbox->setNoFocus(False);
970       if (blackbox->getFocusedWindow())
971         blackbox->getFocusedWindow()->setInputFocus();
972       else
973         blackbox->setFocusedWindow(0);
974
975       // the toolbar will be reconfigured when the change to the workspace name
976       // gets caught in the PropertyNotify event handler
977       screen->getCurrentWorkspace()->setName(new_workspace_name);
978
979       new_workspace_name.erase();
980       new_name_pos = 0;
981
982       // reset the background to that of the workspace label (its normal
983       // setting)
984       BTexture *texture = &(screen->getToolbarStyle()->label);
985       frame.wlabel = texture->render(frame.workspace_label_w, frame.label_h,
986                                      frame.wlabel);
987       if (! frame.wlabel)
988         XSetWindowBackground(display, frame.workspace_label,
989                              texture->color().pixel());
990       else
991         XSetWindowBackgroundPixmap(display, frame.workspace_label,
992                                    frame.wlabel);
993     } else if (! (ks == XK_Shift_L || ks == XK_Shift_R ||
994                   ks == XK_Control_L || ks == XK_Control_R ||
995                   ks == XK_Caps_Lock || ks == XK_Shift_Lock ||
996                   ks == XK_Meta_L || ks == XK_Meta_R ||
997                   ks == XK_Alt_L || ks == XK_Alt_R ||
998                   ks == XK_Super_L || ks == XK_Super_R ||
999                   ks == XK_Hyper_L || ks == XK_Hyper_R)) {
1000       if (ks == XK_BackSpace) {
1001         if (new_name_pos > 0) {
1002           --new_name_pos;
1003           new_workspace_name.erase(new_name_pos);
1004         } else {
1005           new_workspace_name.resize(0);
1006         }
1007       } else {
1008         new_workspace_name += (*keychar);
1009         ++new_name_pos;
1010       }
1011
1012       XClearWindow(display, frame.workspace_label);
1013       unsigned int tw, x;
1014
1015       tw = screen->getToolbarStyle()->font->measureString(new_workspace_name);
1016       x = (frame.workspace_label_w - tw) / 2;
1017
1018       if (x < frame.bevel_w) x = frame.bevel_w;
1019
1020       ToolbarStyle *style = screen->getToolbarStyle();
1021       style->font->drawString(frame.workspace_label, x, 1, style->l_text,
1022                               new_workspace_name);
1023       BPen pen(style->l_text);
1024       XDrawRectangle(display, frame.workspace_label, pen.gc(), x + tw, 0, 1,
1025                      frame.label_h - 1);
1026     }
1027   }
1028 }
1029
1030
1031 void Toolbar::timeout(void) {
1032   checkClock(True);
1033
1034   clock_timer->setTimeout(aMinuteFromNow());
1035 }
1036
1037
1038 void Toolbar::HideHandler::timeout(void) {
1039   toolbar->hidden = ! toolbar->hidden;
1040   if (toolbar->hidden)
1041     XMoveWindow(toolbar->display, toolbar->frame.window,
1042                 toolbar->frame.x_hidden, toolbar->frame.y_hidden);
1043   else
1044     XMoveWindow(toolbar->display, toolbar->frame.window,
1045                 toolbar->frame.rect.x(), toolbar->frame.rect.y());
1046 }
1047
1048
1049 void Toolbar::toggleAutoHide(void) {
1050   saveAutoHide(! doAutoHide());
1051
1052   updateStrut();
1053   screen->getSlit()->reposition();
1054
1055   if (do_auto_hide == False && hidden) {
1056     // force the slit to be visible
1057     if (hide_timer->isTiming()) hide_timer->stop();
1058     hide_handler.timeout();
1059   }
1060 }
1061
1062
1063 Toolbarmenu::Toolbarmenu(Toolbar *tb) : Basemenu(tb->screen) {
1064   toolbar = tb;
1065
1066   setLabel(i18n(ToolbarSet, ToolbarToolbarTitle, "Toolbar"));
1067   setInternalMenu();
1068
1069   placementmenu = new Placementmenu(this);
1070
1071   insert(i18n(CommonSet, CommonPlacementTitle, "Placement"),
1072          placementmenu);
1073   insert(i18n(CommonSet, CommonAlwaysOnTop, "Always on top"), 1);
1074   insert(i18n(CommonSet, CommonAutoHide, "Auto hide"), 2);
1075   insert(i18n(ToolbarSet, ToolbarEditWkspcName,
1076               "Edit current workspace name"), 3);
1077
1078   update();
1079   setValues();
1080 }
1081
1082
1083 void Toolbarmenu::setValues() {
1084   setItemSelected(1, toolbar->isOnTop());
1085   setItemSelected(2, toolbar->doAutoHide());
1086 }
1087
1088
1089 Toolbarmenu::~Toolbarmenu(void) {
1090   delete placementmenu;
1091 }
1092
1093
1094 void Toolbarmenu::itemSelected(int button, unsigned int index) {
1095   if (button != 1)
1096     return;
1097
1098   BasemenuItem *item = find(index);
1099   if (! item) return;
1100
1101   switch (item->function()) {
1102   case 1: { // always on top
1103     toolbar->saveOnTop(! toolbar->isOnTop());
1104     setItemSelected(1, toolbar->isOnTop());
1105
1106     if (toolbar->isOnTop()) getScreen()->raiseWindows((Window *) 0, 0);
1107     break;
1108   }
1109
1110   case 2: { // auto hide
1111     toolbar->toggleAutoHide();
1112     setItemSelected(2, toolbar->doAutoHide());
1113
1114     break;
1115   }
1116
1117   case 3: { // edit current workspace name
1118     toolbar->edit();
1119     hide();
1120
1121     break;
1122   }
1123   } // switch
1124 }
1125
1126
1127 void Toolbarmenu::internal_hide(void) {
1128   Basemenu::internal_hide();
1129   if (toolbar->doAutoHide() && ! toolbar->isEditing())
1130     toolbar->hide_handler.timeout();
1131 }
1132
1133
1134 void Toolbarmenu::reconfigure(void) {
1135   setValues();
1136   placementmenu->reconfigure();
1137
1138   Basemenu::reconfigure();
1139 }
1140
1141
1142 Toolbarmenu::Placementmenu::Placementmenu(Toolbarmenu *tm)
1143   : Basemenu(tm->toolbar->screen), toolbar(tm->toolbar) {
1144   setLabel(i18n(ToolbarSet, ToolbarToolbarPlacement, "Toolbar Placement"));
1145   setInternalMenu();
1146   setMinimumSublevels(3);
1147
1148   insert(i18n(CommonSet, CommonPlacementTopLeft, "Top Left"),
1149          Toolbar::TopLeft);
1150   insert(i18n(CommonSet, CommonPlacementBottomLeft, "Bottom Left"),
1151          Toolbar::BottomLeft);
1152   insert(i18n(CommonSet, CommonPlacementTopCenter, "Top Center"),
1153          Toolbar::TopCenter);
1154   insert(i18n(CommonSet, CommonPlacementBottomCenter, "Bottom Center"),
1155          Toolbar::BottomCenter);
1156   insert(i18n(CommonSet, CommonPlacementTopRight, "Top Right"),
1157          Toolbar::TopRight);
1158   insert(i18n(CommonSet, CommonPlacementBottomRight, "Bottom Right"),
1159          Toolbar::BottomRight);
1160   update();
1161   setValues();
1162 }
1163
1164
1165 void Toolbarmenu::Placementmenu::setValues(void) {
1166   int place = 0;
1167   switch (toolbar->getPlacement()) {
1168   case Toolbar::BottomRight:
1169     place++;
1170   case Toolbar::TopRight:
1171     place++;
1172   case Toolbar::BottomCenter:
1173     place++;
1174   case Toolbar::TopCenter:
1175     place++;
1176   case Toolbar::BottomLeft:
1177     place++;
1178   case Toolbar::TopLeft:
1179     break;
1180   }
1181   setItemSelected(0, 0 == place);
1182   setItemSelected(1, 1 == place);
1183   setItemSelected(2, 2 == place);
1184   setItemSelected(3, 3 == place);
1185   setItemSelected(4, 4 == place);
1186   setItemSelected(5, 5 == place);
1187 }
1188
1189
1190 void Toolbarmenu::Placementmenu::reconfigure(void) {
1191   setValues();
1192   Basemenu::reconfigure();
1193 }
1194
1195
1196 void Toolbarmenu::Placementmenu::itemSelected(int button, unsigned int index) {
1197   if (button != 1)
1198     return;
1199
1200   BasemenuItem *item = find(index);
1201   if (! item) return;
1202
1203   toolbar->savePlacement(item->function());
1204   hide();
1205   toolbar->reconfigure();
1206
1207   // reposition the slit as well to make sure it doesn't intersect the
1208   // toolbar
1209   getScreen()->getSlit()->reposition();
1210 }
1211
1212
1213 void ToolbarStyle::doJustify(const std::string &text, int &start_pos,
1214                              unsigned int max_length,
1215                              unsigned int modifier) const {
1216   size_t text_len = text.size();
1217   unsigned int length;
1218
1219   do {
1220     length = font->measureString(string(text, 0, text_len)) + modifier;
1221   } while (length > max_length && text_len-- > 0);
1222
1223   switch (justify) {
1224   case RightJustify:
1225     start_pos += max_length - length;
1226     break;
1227
1228   case CenterJustify:
1229     start_pos += (max_length - length) / 2;
1230     break;
1231
1232   case LeftJustify:
1233   default:
1234     break;
1235   }
1236 }