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