]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/XFormsToolbar.C
Do nothing on calls to XFormsToolbar::show, hide if the toolbar is
[lyx.git] / src / frontends / xforms / XFormsToolbar.C
1 /**
2  * \file XFormsToolbar.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jean-Marc Lasgouttes
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 //  Added pseudo-action handling, asierra 180296
13
14 #include <config.h>
15
16 #include "XFormsToolbar.h"
17
18 #include "Color.h"
19 #include "Tooltips.h"
20 #include "xforms_helpers.h"
21
22 #include "buffer.h"
23 #include "bufferparams.h"
24 #include "debug.h"
25 #include "funcrequest.h"
26 #include "FuncStatus.h"
27 #include "gettext.h"
28 #include "lyxfunc.h"
29
30 #include "support/lstrings.h"
31
32 #include "lyx_forms.h"
33 #include "lyx_xpm.h"
34 #include "combox.h"
35
36 #include <boost/bind.hpp>
37
38 #include <sstream>
39 #include <vector>
40
41 using lyx::frontend::Box;
42 using lyx::frontend::BoxList;
43
44 using lyx::support::compare_ascii_no_case;
45
46 using std::distance;
47 using std::endl;
48 using std::string;
49 using std::vector;
50
51
52 // some constants
53 const int standardspacing = 2; // the usual space between items
54 const int sepspace = 6; // extra space
55 const int buttonwidth = 30; // the standard button width
56 const int height = 30; // the height of all items in the toolbar
57
58 namespace {
59
60 XFormsView::Position getPosition(ToolbarBackend::Flags const & flags)
61 {
62         if (flags & ToolbarBackend::TOP)
63                 return XFormsView::Top;
64         if (flags & ToolbarBackend::BOTTOM)
65                 return XFormsView::Bottom;
66         if (flags & ToolbarBackend::LEFT)
67                 return XFormsView::Left;
68         if (flags & ToolbarBackend::RIGHT)
69                 return XFormsView::Right;
70         return XFormsView::Top;
71 }
72
73
74 LyXTextClass const & getTextClass(LyXView const & lv)
75 {
76         return lv.buffer()->params().getLyXTextClass();
77 }
78
79 } // namespace anon
80
81
82 XFormsToolbar::toolbarItem::toolbarItem()
83         : icon(0),
84           unused_pixmap(0),
85           active_pixmap(0),
86           inactive_pixmap(0),
87           mask(0)
88 {}
89
90
91 XFormsToolbar::toolbarItem::~toolbarItem()
92 {
93         kill_icon();
94 }
95
96
97 void XFormsToolbar::toolbarItem::kill_icon()
98 {
99         if (unused_pixmap)
100                 // XForms will take care of cleaning up the other pixmap
101                 XFreePixmap(fl_get_display(), unused_pixmap);
102
103         unused_pixmap = 0;
104         active_pixmap = 0;
105         inactive_pixmap = 0;
106         mask = 0;
107         icon = 0;
108 }
109
110
111 XFormsToolbar::toolbarItem &
112 XFormsToolbar::toolbarItem::operator=(toolbarItem const & ti)
113 {
114         if (this == &ti)
115                 return *this;
116
117         // If we already have an icon, release it.
118         // But we don't copy the icon from ti
119         kill_icon();
120
121         func = ti.func;
122
123         return *this;
124 }
125
126
127 void XFormsToolbar::toolbarItem::generateInactivePixmaps()
128 {
129         if (!icon || icon->objclass != FL_PIXMAPBUTTON)
130                 return;
131
132         // Store the existing (active) pixmap.
133         fl_get_pixmap_pixmap(icon, &active_pixmap, &mask);
134
135         if (active_pixmap == 0 || mask == 0)
136                 return;
137
138         // Ascertain the width and height of the pixmap.
139         Display * display = fl_get_display();
140         unsigned int width;
141         unsigned int height;
142         unsigned int uidummy;
143         int idummy;
144         Window win;
145
146         XGetGeometry(display, active_pixmap, &win, &idummy, &idummy,
147                      &width, &height, &uidummy, &uidummy);
148
149         // Produce a darker shade of the button background as the
150         // inactive color. Note the 'hsv.v - 0.2'.
151         unsigned int r, g, b;
152         fl_getmcolor(FL_PIXMAPBUTTON_COL1, &r, &g, &b);
153         HSVColor hsv(RGBColor(r, g, b));
154         hsv.v = std::max(0.0, hsv.v - 0.2);
155         string const inactive_color = X11hexname(RGBColor(hsv));
156
157         // Generate an XPM dataset for a uniformly-colored pixmap with
158         // the same dimensions as active_pixmap.
159
160         // The data set has the form:
161         // "<width> <height> <ncolors> <chars per pixel>",
162         // "o c <inactive_color>",
163         // "oooooooooooooooo", // <width> 'o' chars.
164         // repeated <height> times.
165         std::ostringstream line1_ss;
166         line1_ss << width << ' ' << height << " 1 1";
167         string const line1 = line1_ss.str();
168         string const line2 = "o c " + inactive_color;
169         string const data(width, 'o');
170         vector<char *> inactive_data(height + 2,
171                                      const_cast<char *>(data.c_str()));
172         inactive_data[0] = const_cast<char *>(line1.c_str());
173         inactive_data[1] = const_cast<char *>(line2.c_str());
174
175         char ** raw_inactive_data = &*inactive_data.begin();
176
177         // Generate a pixmap of this data set.
178         // Together with 'mask' above, this is sufficient to display
179         // an inactive version of our active_pixmap.
180         Screen * screen = ScreenOfDisplay(display, fl_screen);
181
182         XpmCreatePixmapFromData(display, XRootWindowOfScreen(screen),
183                                 raw_inactive_data, &inactive_pixmap, 0, 0);
184 }
185
186
187 Toolbars::ToolbarPtr make_toolbar(ToolbarBackend::Toolbar const & tbb,
188                                   LyXView & owner)
189 {
190         return Toolbars::ToolbarPtr(new XFormsToolbar(tbb, owner));
191 }
192
193
194 XFormsToolbar::XFormsToolbar(ToolbarBackend::Toolbar const & tbb,
195                              LyXView & o)
196         : toolbar_(0),
197           toolbar_buttons_(0),
198           owner_(static_cast<XFormsView &>(o)),
199           tooltip_(new Tooltips)
200 {
201         position_ = getPosition(tbb.flags);
202         BoxList & boxlist = owner_.getBox(position_).children();
203         toolbar_ = &boxlist.push_back(Box(0,0));
204
205         // If the toolbar is horizontal, then it contains three
206         // vertically-aligned Boxes,the center one of which is to
207         // contain the buttons, aligned horizontally.
208         // The other two provide some visual padding.
209
210         // If it is vertical, then this is swapped around.
211
212         Box::Orientation const toolbar_orientation =
213                 (position_ == XFormsView::Left ||
214                  position_ == XFormsView::Right)
215                 ? Box::Vertical : Box::Horizontal;
216
217         Box::Orientation const padding_orientation =
218                 (toolbar_orientation == Box::Vertical)
219                 ? Box::Horizontal : Box::Vertical;
220
221         toolbar_->set(padding_orientation);
222
223         // A bit of a hack, but prevents 'M-x' causing the addition of
224         // visible borders.
225         int const padding =
226                 (tbb.name == "minibuffer") ?
227                 0 : 2 + abs(fl_get_border_width());
228
229         toolbar_->children().push_back(Box(padding, padding));
230
231         Box & toolbar_center = toolbar_->children().push_back(Box(0,0));
232         toolbar_center.set(toolbar_orientation);
233         toolbar_buttons_ = &toolbar_center.children();
234
235         toolbar_->children().push_back(Box(padding, padding));
236
237         using lyx::frontend::WidgetMap;
238         owner_.metricsUpdated.connect(boost::bind(&WidgetMap::updateMetrics,
239                                                   &widgets_));
240
241         // Populate the toolbar.
242         ToolbarBackend::item_iterator it = tbb.items.begin();
243         ToolbarBackend::item_iterator end = tbb.items.end();
244         for (; it != end; ++it)
245                 add(it->first, it->second);
246
247 }
248
249
250 XFormsToolbar::~XFormsToolbar()
251 {
252         fl_freeze_form(owner_.getForm());
253
254         // G++ vector does not have clear defined
255         //toollist.clear();
256         toollist_.erase(toollist_.begin(), toollist_.end());
257
258         fl_unfreeze_form(owner_.getForm());
259 }
260
261
262 namespace {
263
264 extern "C" {
265
266 void C_ToolbarCB(FL_OBJECT * ob, long ac)
267 {
268         if (!ob || !ob->u_vdata)
269                 return;
270
271         XFormsToolbar * ptr = static_cast<XFormsToolbar *>(ob->u_vdata);
272         XFormsView & owner = ptr->owner_;
273         owner.getLyXFunc().dispatch(ptr->funcs[ac], true);
274 }
275
276 } // extern "C"
277
278 } // namespace anon
279
280
281 void XFormsToolbar::hide(bool update_metrics)
282 {
283         if (!toolbar_->visible())
284                 return;
285
286         toolbar_->set(Box::Invisible);
287         if (update_metrics)
288                 owner_.updateMetrics();
289 }
290
291
292 void XFormsToolbar::show(bool update_metrics)
293 {
294         if (toolbar_->visible())
295                 return;
296
297         toolbar_->set(Box::Visible);
298         toolbar_->show();
299         if (update_metrics)
300                 owner_.updateMetrics();
301 }
302
303
304 void XFormsToolbar::add(FuncRequest const & func, string const & tooltip)
305 {
306         toolbarItem item;
307         item.func = func;
308
309         switch (func.action) {
310         case ToolbarBackend::SEPARATOR:
311                 toolbar_buttons_->push_back(Box(sepspace, sepspace));
312                 break;
313
314         case ToolbarBackend::MINIBUFFER:
315                 // Not implemented.
316                 // XForms uses the same widget to display the buffer messages
317                 // and to input commands.
318                 break;
319
320         case ToolbarBackend::LAYOUTS:
321                 layout_.reset(new XLayoutBox(owner_, *this));
322                 break;
323
324         default: {
325                 FL_OBJECT * obj;
326
327                 toolbar_buttons_->push_back(Box(standardspacing,
328                                                 standardspacing));
329
330                 item.icon = obj =
331                         fl_add_pixmapbutton(FL_NORMAL_BUTTON,
332                                             0, 0, 0, 0, "");
333
334                 widgets_.add(obj, *toolbar_buttons_, buttonwidth, height);
335
336                 fl_set_object_resize(obj, FL_RESIZE_ALL);
337
338                 int gravity = 0;
339                 if (position_ == XFormsView::Top ||
340                     position_ == XFormsView::Left)
341                         gravity = NorthWestGravity;
342                 else if (position_ == XFormsView::Right)
343                         gravity = NorthEastGravity;
344                 else if (position_ == XFormsView::Bottom)
345                         gravity = SouthWestGravity;
346
347                 fl_set_object_gravity(obj, gravity, gravity);
348
349                 Funcs::iterator fit = funcs.insert(funcs.end(), func);
350                 int const index = distance(funcs.begin(), fit);
351                 fl_set_object_callback(obj, C_ToolbarCB, index);
352                 // Remove the blue feedback rectangle
353                 fl_set_pixmapbutton_focus_outline(obj, 0);
354
355                 tooltip_->init(obj, tooltip);
356
357                 // The view that this object belongs to.
358                 obj->u_vdata = this;
359
360                 string const xpm = toolbarbackend.getIcon(func);
361                 fl_set_pixmapbutton_file(obj, xpm.c_str());
362                 break;
363         }
364         }
365
366         toollist_.push_back(item);
367 }
368
369
370 void XFormsToolbar::update()
371 {
372         ToolbarList::iterator p = toollist_.begin();
373         ToolbarList::iterator end = toollist_.end();
374         for (; p != end; ++p) {
375                 if (!p->icon)
376                         continue;
377
378                 FuncStatus const status =
379                         owner_.getLyXFunc().getStatus(p->func);
380
381                 if (status.onoff(true)) {
382                         // I'd like to use a different color
383                         // here, but then the problem is to
384                         // know how to use transparency with
385                         // Xpm library. It seems pretty
386                         // complicated to me (JMarc)
387                         fl_set_object_color(p->icon, FL_LEFT_BCOL, FL_BLUE);
388                         fl_set_object_boxtype(p->icon, FL_DOWN_BOX);
389                 } else {
390                         fl_set_object_color(p->icon, FL_MCOL, FL_BLUE);
391                         fl_set_object_boxtype(p->icon, FL_UP_BOX);
392                 }
393
394                 // This must go here rather than in XFormsToolbar::add, else
395                 // LyX aborts with a BadPixmap error.
396                 if (!p->active_pixmap)
397                         p->generateInactivePixmaps();
398
399                 if (status.enabled()) {
400                         fl_activate_object(p->icon);
401                         fl_set_pixmap_pixmap(p->icon,
402                                              p->active_pixmap,
403                                              p->mask);
404                         p->unused_pixmap = p->inactive_pixmap;
405                 } else {
406                         fl_deactivate_object(p->icon);
407                         fl_set_pixmap_pixmap(p->icon,
408                                              p->inactive_pixmap,
409                                              p->mask);
410                         p->unused_pixmap = p->active_pixmap;
411                 }
412         }
413
414         bool const enable = owner_.getLyXFunc().
415                 getStatus(FuncRequest(LFUN_LAYOUT)).enabled();
416
417         if (layout_.get())
418                 layout_->setEnabled(enable);
419 }
420
421
422 namespace {
423
424 extern "C"
425 void C_LayoutBoxSelectedCB(FL_OBJECT * ob, long)
426 {
427         if (!ob || !ob->u_vdata)
428                 return;
429         XLayoutBox * ptr = static_cast<XLayoutBox *>(ob->u_vdata);
430         ptr->selected();
431 }
432
433 } // namespace anon
434
435
436 XLayoutBox::XLayoutBox(LyXView & owner, XFormsToolbar & toolbar)
437         : owner_(owner)
438 {
439         toolbar.toolbar_buttons_->push_back(Box(standardspacing, 0));
440
441         combox_ = fl_add_combox(FL_DROPLIST_COMBOX,
442                                 0, 0, 135, height, "");
443
444         toolbar.widgets_.add(combox_, *toolbar.toolbar_buttons_, 135, height);
445
446         fl_set_combox_browser_height(combox_, 400);
447         fl_set_object_boxtype(combox_, FL_DOWN_BOX);
448         fl_set_object_color(combox_, FL_MCOL, FL_MCOL);
449         fl_set_object_gravity(combox_, FL_NorthWest, FL_NorthWest);
450         fl_set_object_resize(combox_, FL_RESIZE_ALL);
451
452         combox_->u_vdata = this;
453         fl_set_object_callback(combox_, C_LayoutBoxSelectedCB, 0);
454 }
455
456
457 void XLayoutBox::set(string const & layout)
458 {
459         LyXTextClass const & tc = getTextClass(owner_);
460
461         string const layoutname = _(tc[layout]->name());
462
463         int const nnames = fl_get_combox_maxitems(combox_);
464         for (int i = 1; i <= nnames; ++i) {
465                 string const name = fl_get_combox_line(combox_, i);
466                 if (name == layoutname) {
467                         fl_set_combox(combox_, i);
468                         break;
469                 }
470         }
471 }
472
473
474 void XLayoutBox::update()
475 {
476         LyXTextClass const & tc = getTextClass(owner_);
477
478         fl_clear_combox(combox_);
479
480         LyXTextClass::const_iterator it = tc.begin();
481         LyXTextClass::const_iterator const end = tc.end();
482         for (; it != end; ++it) {
483                 // ignore obsolete entries
484                 if ((*it)->obsoleted_by().empty()) {
485                         string const & name = _((*it)->name());
486                         fl_addto_combox(combox_, name.c_str());
487                 }
488         }
489
490         // we need to do this.
491         fl_redraw_object(combox_);
492 }
493
494
495 void XLayoutBox::clear()
496 {
497         fl_clear_combox(combox_);
498         fl_redraw_object(combox_);
499 }
500
501
502 void XLayoutBox::open()
503 {
504         fl_show_combox_browser(combox_);
505 }
506
507
508 void XLayoutBox::setEnabled(bool enable)
509 {
510         ::setEnabled(combox_, enable);
511 }
512
513
514 void XLayoutBox::selected()
515 {
516         string const layoutguiname = getString(combox_);
517
518         LyXTextClass const & tc = getTextClass(owner_);
519
520         LyXTextClass::const_iterator it  = tc.begin();
521         LyXTextClass::const_iterator const end = tc.end();
522         for (; it != end; ++it) {
523                 string const & name = (*it)->name();
524                 if (_(name) == layoutguiname) {
525                         owner_.getLyXFunc()
526                                 .dispatch(FuncRequest(LFUN_LAYOUT, name),
527                                           true);
528                         return;
529                 }
530         }
531         lyxerr << "ERROR (XLayoutBox::selected): layout not found!" << endl;
532 }