]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/XFormsToolbar.C
... and sanitize the simplified code...
[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         toolbar_->set(Box::Invisible);
284         if (update_metrics)
285                 owner_.updateMetrics();
286 }
287
288
289 void XFormsToolbar::show(bool update_metrics)
290 {
291         toolbar_->set(Box::Visible);
292         toolbar_->show();
293         if (update_metrics)
294                 owner_.updateMetrics();
295 }
296
297
298 void XFormsToolbar::add(FuncRequest const & func, string const & tooltip)
299 {
300         toolbarItem item;
301         item.func = func;
302
303         switch (func.action) {
304         case ToolbarBackend::SEPARATOR:
305                 toolbar_buttons_->push_back(Box(sepspace, sepspace));
306                 break;
307
308         case ToolbarBackend::MINIBUFFER:
309                 // Not implemented.
310                 // XForms uses the same widget to display the buffer messages
311                 // and to input commands.
312                 break;
313
314         case ToolbarBackend::LAYOUTS:
315                 layout_.reset(new XLayoutBox(owner_, *this));
316                 break;
317
318         default: {
319                 FL_OBJECT * obj;
320
321                 toolbar_buttons_->push_back(Box(standardspacing,
322                                                 standardspacing));
323
324                 item.icon = obj =
325                         fl_add_pixmapbutton(FL_NORMAL_BUTTON,
326                                             0, 0, 0, 0, "");
327
328                 widgets_.add(obj, *toolbar_buttons_, buttonwidth, height);
329
330                 fl_set_object_resize(obj, FL_RESIZE_ALL);
331
332                 int gravity = 0;
333                 if (position_ == XFormsView::Top ||
334                     position_ == XFormsView::Left)
335                         gravity = NorthWestGravity;
336                 else if (position_ == XFormsView::Right)
337                         gravity = NorthEastGravity;
338                 else if (position_ == XFormsView::Bottom)
339                         gravity = SouthWestGravity;
340
341                 fl_set_object_gravity(obj, gravity, gravity);
342
343                 Funcs::iterator fit = funcs.insert(funcs.end(), func);
344                 int const index = distance(funcs.begin(), fit);
345                 fl_set_object_callback(obj, C_ToolbarCB, index);
346                 // Remove the blue feedback rectangle
347                 fl_set_pixmapbutton_focus_outline(obj, 0);
348
349                 tooltip_->init(obj, tooltip);
350
351                 // The view that this object belongs to.
352                 obj->u_vdata = this;
353
354                 string const xpm = toolbarbackend.getIcon(func);
355                 fl_set_pixmapbutton_file(obj, xpm.c_str());
356                 break;
357         }
358         }
359
360         toollist_.push_back(item);
361 }
362
363
364 void XFormsToolbar::update()
365 {
366         ToolbarList::iterator p = toollist_.begin();
367         ToolbarList::iterator end = toollist_.end();
368         for (; p != end; ++p) {
369                 if (!p->icon)
370                         continue;
371
372                 FuncStatus const status =
373                         owner_.getLyXFunc().getStatus(p->func);
374
375                 if (status.onoff(true)) {
376                         // I'd like to use a different color
377                         // here, but then the problem is to
378                         // know how to use transparency with
379                         // Xpm library. It seems pretty
380                         // complicated to me (JMarc)
381                         fl_set_object_color(p->icon, FL_LEFT_BCOL, FL_BLUE);
382                         fl_set_object_boxtype(p->icon, FL_DOWN_BOX);
383                 } else {
384                         fl_set_object_color(p->icon, FL_MCOL, FL_BLUE);
385                         fl_set_object_boxtype(p->icon, FL_UP_BOX);
386                 }
387
388                 // This must go here rather than in XFormsToolbar::add, else
389                 // LyX aborts with a BadPixmap error.
390                 if (!p->active_pixmap)
391                         p->generateInactivePixmaps();
392
393                 if (status.enabled()) {
394                         fl_activate_object(p->icon);
395                         fl_set_pixmap_pixmap(p->icon,
396                                              p->active_pixmap,
397                                              p->mask);
398                         p->unused_pixmap = p->inactive_pixmap;
399                 } else {
400                         fl_deactivate_object(p->icon);
401                         fl_set_pixmap_pixmap(p->icon,
402                                              p->inactive_pixmap,
403                                              p->mask);
404                         p->unused_pixmap = p->active_pixmap;
405                 }
406         }
407
408         bool const enable = owner_.getLyXFunc().
409                 getStatus(FuncRequest(LFUN_LAYOUT)).enabled();
410
411         if (layout_.get())
412                 layout_->setEnabled(enable);
413 }
414
415
416 namespace {
417
418 extern "C"
419 void C_LayoutBoxSelectedCB(FL_OBJECT * ob, long)
420 {
421         if (!ob || !ob->u_vdata)
422                 return;
423         XLayoutBox * ptr = static_cast<XLayoutBox *>(ob->u_vdata);
424         ptr->selected();
425 }
426
427 } // namespace anon
428
429
430 XLayoutBox::XLayoutBox(LyXView & owner, XFormsToolbar & toolbar)
431         : owner_(owner)
432 {
433         toolbar.toolbar_buttons_->push_back(Box(standardspacing, 0));
434
435         combox_ = fl_add_combox(FL_DROPLIST_COMBOX,
436                                 0, 0, 135, height, "");
437
438         toolbar.widgets_.add(combox_, *toolbar.toolbar_buttons_, 135, height);
439
440         fl_set_combox_browser_height(combox_, 400);
441         fl_set_object_boxtype(combox_, FL_DOWN_BOX);
442         fl_set_object_color(combox_, FL_MCOL, FL_MCOL);
443         fl_set_object_gravity(combox_, FL_NorthWest, FL_NorthWest);
444         fl_set_object_resize(combox_, FL_RESIZE_ALL);
445
446         combox_->u_vdata = this;
447         fl_set_object_callback(combox_, C_LayoutBoxSelectedCB, 0);
448 }
449
450
451 void XLayoutBox::set(string const & layout)
452 {
453         LyXTextClass const & tc = getTextClass(owner_);
454
455         string const layoutname = _(tc[layout]->name());
456
457         int const nnames = fl_get_combox_maxitems(combox_);
458         for (int i = 1; i <= nnames; ++i) {
459                 string const name = fl_get_combox_line(combox_, i);
460                 if (name == layoutname) {
461                         fl_set_combox(combox_, i);
462                         break;
463                 }
464         }
465 }
466
467
468 void XLayoutBox::update()
469 {
470         LyXTextClass const & tc = getTextClass(owner_);
471
472         fl_clear_combox(combox_);
473
474         LyXTextClass::const_iterator it = tc.begin();
475         LyXTextClass::const_iterator const end = tc.end();
476         for (; it != end; ++it) {
477                 // ignore obsolete entries
478                 if ((*it)->obsoleted_by().empty()) {
479                         string const & name = _((*it)->name());
480                         fl_addto_combox(combox_, name.c_str());
481                 }
482         }
483
484         // we need to do this.
485         fl_redraw_object(combox_);
486 }
487
488
489 void XLayoutBox::clear()
490 {
491         fl_clear_combox(combox_);
492         fl_redraw_object(combox_);
493 }
494
495
496 void XLayoutBox::open()
497 {
498         fl_show_combox_browser(combox_);
499 }
500
501
502 void XLayoutBox::setEnabled(bool enable)
503 {
504         ::setEnabled(combox_, enable);
505 }
506
507
508 void XLayoutBox::selected()
509 {
510         string const layoutguiname = getString(combox_);
511
512         LyXTextClass const & tc = getTextClass(owner_);
513
514         LyXTextClass::const_iterator it  = tc.begin();
515         LyXTextClass::const_iterator const end = tc.end();
516         for (; it != end; ++it) {
517                 string const & name = (*it)->name();
518                 if (_(name) == layoutguiname) {
519                         owner_.getLyXFunc()
520                                 .dispatch(FuncRequest(LFUN_LAYOUT, name),
521                                           true);
522                         return;
523                 }
524         }
525         lyxerr << "ERROR (XLayoutBox::selected): layout not found!" << endl;
526 }