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