]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/XFormsToolbar.C
add possibility to specify the origin of a FuncRequest; use it to get rid of the...
[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 std::distance;
42 using std::endl;
43 using std::string;
44 using std::vector;
45
46 namespace lyx {
47
48 using support::compare_ascii_no_case;
49
50 namespace frontend {
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 } // namespace frontend
187 } // namespace lyx
188
189
190 Toolbars::ToolbarPtr make_toolbar(ToolbarBackend::Toolbar const & tbb,
191                                   LyXView & owner)
192 {
193         using lyx::frontend::XFormsToolbar;
194         return Toolbars::ToolbarPtr(new XFormsToolbar(tbb, owner));
195 }
196
197
198 namespace lyx {
199 namespace frontend {
200
201 XFormsToolbar::XFormsToolbar(ToolbarBackend::Toolbar const & tbb,
202                              LyXView & o)
203         : toolbar_(0),
204           toolbar_buttons_(0),
205           owner_(static_cast<XFormsView &>(o)),
206           tooltip_(new Tooltips)
207 {
208         position_ = getPosition(tbb.flags);
209         BoxList & boxlist = owner_.getBox(position_).children();
210         toolbar_ = &boxlist.push_back(Box(0,0));
211
212         // If the toolbar is horizontal, then it contains three
213         // vertically-aligned Boxes,the center one of which is to
214         // contain the buttons, aligned horizontally.
215         // The other two provide some visual padding.
216
217         // If it is vertical, then this is swapped around.
218
219         Box::Orientation const toolbar_orientation =
220                 (position_ == XFormsView::Left ||
221                  position_ == XFormsView::Right)
222                 ? Box::Vertical : Box::Horizontal;
223
224         Box::Orientation const padding_orientation =
225                 (toolbar_orientation == Box::Vertical)
226                 ? Box::Horizontal : Box::Vertical;
227
228         toolbar_->set(padding_orientation);
229
230         // A bit of a hack, but prevents 'M-x' causing the addition of
231         // visible borders.
232         int const padding =
233                 (tbb.name == "minibuffer") ?
234                 0 : 2 + abs(fl_get_border_width());
235
236         toolbar_->children().push_back(Box(padding, padding));
237
238         Box & toolbar_center = toolbar_->children().push_back(Box(0,0));
239         toolbar_center.set(toolbar_orientation);
240         toolbar_buttons_ = &toolbar_center.children();
241
242         toolbar_->children().push_back(Box(padding, padding));
243
244         owner_.metricsUpdated.connect(boost::bind(&WidgetMap::updateMetrics,
245                                                   &widgets_));
246
247         // Populate the toolbar.
248         ToolbarBackend::item_iterator it = tbb.items.begin();
249         ToolbarBackend::item_iterator end = tbb.items.end();
250         for (; it != end; ++it)
251                 add(it->first, it->second);
252
253 }
254
255
256 XFormsToolbar::~XFormsToolbar()
257 {
258         fl_freeze_form(owner_.getForm());
259
260         // G++ vector does not have clear defined
261         //toollist.clear();
262         toollist_.erase(toollist_.begin(), toollist_.end());
263
264         fl_unfreeze_form(owner_.getForm());
265 }
266
267
268 namespace {
269
270 extern "C" {
271
272 void C_ToolbarCB(FL_OBJECT * ob, long ac)
273 {
274         if (!ob || !ob->u_vdata)
275                 return;
276
277         XFormsToolbar * ptr = static_cast<XFormsToolbar *>(ob->u_vdata);
278         XFormsView & owner = ptr->owner_;
279         owner.getLyXFunc().dispatch(ptr->funcs[ac]);
280 }
281
282 } // extern "C"
283
284 } // namespace anon
285
286
287 void XFormsToolbar::hide(bool update_metrics)
288 {
289         if (!toolbar_->visible())
290                 return;
291
292         toolbar_->set(Box::Invisible);
293         if (update_metrics)
294                 owner_.updateMetrics();
295 }
296
297
298 void XFormsToolbar::show(bool update_metrics)
299 {
300         if (toolbar_->visible())
301                 return;
302
303         toolbar_->set(Box::Visible);
304         toolbar_->show();
305         if (update_metrics)
306                 owner_.updateMetrics();
307 }
308
309
310 void XFormsToolbar::add(FuncRequest const & func, string const & tooltip)
311 {
312         toolbarItem item;
313         item.func = func;
314
315         switch (func.action) {
316         case ToolbarBackend::SEPARATOR:
317                 toolbar_buttons_->push_back(Box(sepspace, sepspace));
318                 break;
319
320         case ToolbarBackend::MINIBUFFER:
321                 // Not implemented.
322                 // XForms uses the same widget to display the buffer messages
323                 // and to input commands.
324                 break;
325
326         case ToolbarBackend::LAYOUTS:
327                 layout_.reset(new XLayoutBox(owner_, *this));
328                 break;
329
330         default: {
331                 FL_OBJECT * obj;
332
333                 toolbar_buttons_->push_back(Box(standardspacing,
334                                                 standardspacing));
335
336                 item.icon = obj =
337                         fl_add_pixmapbutton(FL_NORMAL_BUTTON,
338                                             0, 0, 0, 0, "");
339
340                 widgets_.add(obj, *toolbar_buttons_, buttonwidth, height);
341
342                 fl_set_object_resize(obj, FL_RESIZE_ALL);
343
344                 int gravity = 0;
345                 if (position_ == XFormsView::Top ||
346                     position_ == XFormsView::Left)
347                         gravity = NorthWestGravity;
348                 else if (position_ == XFormsView::Right)
349                         gravity = NorthEastGravity;
350                 else if (position_ == XFormsView::Bottom)
351                         gravity = SouthWestGravity;
352
353                 fl_set_object_gravity(obj, gravity, gravity);
354
355                 Funcs::iterator fit = funcs.insert(funcs.end(), func);
356                 int const index = distance(funcs.begin(), fit);
357                 fl_set_object_callback(obj, C_ToolbarCB, index);
358                 // Remove the blue feedback rectangle
359                 fl_set_pixmapbutton_focus_outline(obj, 0);
360
361                 tooltip_->init(obj, tooltip);
362
363                 // The view that this object belongs to.
364                 obj->u_vdata = this;
365
366                 string const xpm = toolbarbackend.getIcon(func);
367                 fl_set_pixmapbutton_file(obj, xpm.c_str());
368                 break;
369         }
370         }
371
372         toollist_.push_back(item);
373 }
374
375
376 void XFormsToolbar::update()
377 {
378         ToolbarList::iterator p = toollist_.begin();
379         ToolbarList::iterator end = toollist_.end();
380         for (; p != end; ++p) {
381                 if (!p->icon)
382                         continue;
383
384                 FuncStatus const status =
385                         owner_.getLyXFunc().getStatus(p->func);
386
387                 if (status.onoff(true)) {
388                         // I'd like to use a different color
389                         // here, but then the problem is to
390                         // know how to use transparency with
391                         // Xpm library. It seems pretty
392                         // complicated to me (JMarc)
393                         fl_set_object_color(p->icon, FL_LEFT_BCOL, FL_BLUE);
394                         fl_set_object_boxtype(p->icon, FL_DOWN_BOX);
395                 } else {
396                         fl_set_object_color(p->icon, FL_MCOL, FL_BLUE);
397                         fl_set_object_boxtype(p->icon, FL_UP_BOX);
398                 }
399
400                 // This must go here rather than in XFormsToolbar::add, else
401                 // LyX aborts with a BadPixmap error.
402                 if (!p->active_pixmap)
403                         p->generateInactivePixmaps();
404
405                 if (status.enabled()) {
406                         fl_activate_object(p->icon);
407                         fl_set_pixmap_pixmap(p->icon,
408                                              p->active_pixmap,
409                                              p->mask);
410                         p->unused_pixmap = p->inactive_pixmap;
411                 } else {
412                         fl_deactivate_object(p->icon);
413                         fl_set_pixmap_pixmap(p->icon,
414                                              p->inactive_pixmap,
415                                              p->mask);
416                         p->unused_pixmap = p->active_pixmap;
417                 }
418         }
419
420         bool const enable = owner_.getLyXFunc().
421                 getStatus(FuncRequest(LFUN_LAYOUT)).enabled();
422
423         if (layout_.get())
424                 layout_->setEnabled(enable);
425 }
426
427
428 namespace {
429
430 extern "C"
431 void C_LayoutBoxSelectedCB(FL_OBJECT * ob, long)
432 {
433         if (!ob || !ob->u_vdata)
434                 return;
435         XLayoutBox * ptr = static_cast<XLayoutBox *>(ob->u_vdata);
436         ptr->selected();
437 }
438
439 } // namespace anon
440
441
442 XLayoutBox::XLayoutBox(LyXView & owner, XFormsToolbar & toolbar)
443         : owner_(owner)
444 {
445         toolbar.toolbar_buttons_->push_back(Box(standardspacing, 0));
446
447         combox_ = fl_add_combox(FL_DROPLIST_COMBOX,
448                                 0, 0, 135, height, "");
449
450         toolbar.widgets_.add(combox_, *toolbar.toolbar_buttons_, 135, height);
451
452         fl_set_combox_browser_height(combox_, 400);
453         fl_set_object_boxtype(combox_, FL_DOWN_BOX);
454         fl_set_object_color(combox_, FL_MCOL, FL_MCOL);
455         fl_set_object_gravity(combox_, FL_NorthWest, FL_NorthWest);
456         fl_set_object_resize(combox_, FL_RESIZE_ALL);
457
458         combox_->u_vdata = this;
459         fl_set_object_callback(combox_, C_LayoutBoxSelectedCB, 0);
460 }
461
462
463 void XLayoutBox::set(string const & layout)
464 {
465         LyXTextClass const & tc = getTextClass(owner_);
466
467         string const layoutname = _(tc[layout]->name());
468
469         int const nnames = fl_get_combox_maxitems(combox_);
470         for (int i = 1; i <= nnames; ++i) {
471                 string const name = fl_get_combox_line(combox_, i);
472                 if (name == layoutname) {
473                         fl_set_combox(combox_, i);
474                         break;
475                 }
476         }
477 }
478
479
480 void XLayoutBox::update()
481 {
482         LyXTextClass const & tc = getTextClass(owner_);
483
484         fl_clear_combox(combox_);
485
486         LyXTextClass::const_iterator it = tc.begin();
487         LyXTextClass::const_iterator const end = tc.end();
488         for (; it != end; ++it) {
489                 // ignore obsolete entries
490                 if ((*it)->obsoleted_by().empty()) {
491                         string const & name = _((*it)->name());
492                         fl_addto_combox(combox_, name.c_str());
493                 }
494         }
495
496         // we need to do this.
497         fl_redraw_object(combox_);
498 }
499
500
501 void XLayoutBox::clear()
502 {
503         fl_clear_combox(combox_);
504         fl_redraw_object(combox_);
505 }
506
507
508 void XLayoutBox::open()
509 {
510         fl_show_combox_browser(combox_);
511 }
512
513
514 void XLayoutBox::setEnabled(bool enable)
515 {
516         lyx::frontend::setEnabled(combox_, enable);
517 }
518
519
520 void XLayoutBox::selected()
521 {
522         string const layoutguiname = getString(combox_);
523
524         LyXTextClass const & tc = getTextClass(owner_);
525
526         LyXTextClass::const_iterator it  = tc.begin();
527         LyXTextClass::const_iterator const end = tc.end();
528         for (; it != end; ++it) {
529                 string const & name = (*it)->name();
530                 if (_(name) == layoutguiname) {
531                         FuncRequest const func(LFUN_LAYOUT, name, 
532                                                FuncRequest::UI);
533                         owner_.getLyXFunc().dispatch(func);
534                         return;
535                 }
536         }
537         lyxerr << "ERROR (XLayoutBox::selected): layout not found!" << endl;
538 }
539
540 } // namespace frontend
541 } // namespace lyx