]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/XFormsToolbar.C
Change glob() API to accept a dir parameter.
[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 boost::shared_ptr;
42
43 using std::distance;
44 using std::endl;
45 using std::string;
46 using std::vector;
47
48 namespace lyx {
49
50 using support::compare_ascii_no_case;
51
52 namespace frontend {
53
54 // some constants
55 const int standardspacing = 2; // the usual space between items
56 const int sepspace = 6; // extra space
57 const int buttonwidth = 30; // the standard button width
58 const int height = 30; // the height of all items in the toolbar
59
60 namespace {
61
62 XFormsView::Position getPosition(ToolbarBackend::Flags const & flags)
63 {
64         if (flags & ToolbarBackend::TOP)
65                 return XFormsView::Top;
66         if (flags & ToolbarBackend::BOTTOM)
67                 return XFormsView::Bottom;
68         if (flags & ToolbarBackend::LEFT)
69                 return XFormsView::Left;
70         if (flags & ToolbarBackend::RIGHT)
71                 return XFormsView::Right;
72         return XFormsView::Top;
73 }
74
75
76 LyXTextClass const & getTextClass(LyXView const & lv)
77 {
78         return lv.buffer()->params().getLyXTextClass();
79 }
80
81 } // namespace anon
82
83
84 XFormsToolbar::toolbarItem::toolbarItem()
85         : icon(0),
86           unused_pixmap(0),
87           active_pixmap(0),
88           inactive_pixmap(0),
89           mask(0)
90 {}
91
92
93 XFormsToolbar::toolbarItem::~toolbarItem()
94 {
95         kill_icon();
96 }
97
98
99 void XFormsToolbar::toolbarItem::kill_icon()
100 {
101         if (unused_pixmap)
102                 // XForms will take care of cleaning up the other pixmap
103                 XFreePixmap(fl_get_display(), unused_pixmap);
104
105         unused_pixmap = 0;
106         active_pixmap = 0;
107         inactive_pixmap = 0;
108         mask = 0;
109         icon = 0;
110 }
111
112
113 XFormsToolbar::toolbarItem &
114 XFormsToolbar::toolbarItem::operator=(toolbarItem const & ti)
115 {
116         if (this == &ti)
117                 return *this;
118
119         // If we already have an icon, release it.
120         // But we don't copy the icon from ti
121         kill_icon();
122
123         func = ti.func;
124
125         return *this;
126 }
127
128
129 void XFormsToolbar::toolbarItem::generateInactivePixmaps()
130 {
131         if (!icon || icon->objclass != FL_PIXMAPBUTTON)
132                 return;
133
134         // Store the existing (active) pixmap.
135         fl_get_pixmap_pixmap(icon, &active_pixmap, &mask);
136
137         if (active_pixmap == 0 || mask == 0)
138                 return;
139
140         // Ascertain the width and height of the pixmap.
141         Display * display = fl_get_display();
142         unsigned int width;
143         unsigned int height;
144         unsigned int uidummy;
145         int idummy;
146         Window win;
147
148         XGetGeometry(display, active_pixmap, &win, &idummy, &idummy,
149                      &width, &height, &uidummy, &uidummy);
150
151         // Produce a darker shade of the button background as the
152         // inactive color. Note the 'hsv.v - 0.2'.
153         unsigned int r, g, b;
154         fl_getmcolor(FL_PIXMAPBUTTON_COL1, &r, &g, &b);
155         HSVColor hsv(RGBColor(r, g, b));
156         hsv.v = std::max(0.0, hsv.v - 0.2);
157         string const inactive_color = X11hexname(RGBColor(hsv));
158
159         // Generate an XPM dataset for a uniformly-colored pixmap with
160         // the same dimensions as active_pixmap.
161
162         // The data set has the form:
163         // "<width> <height> <ncolors> <chars per pixel>",
164         // "o c <inactive_color>",
165         // "oooooooooooooooo", // <width> 'o' chars.
166         // repeated <height> times.
167         std::ostringstream line1_ss;
168         line1_ss << width << ' ' << height << " 1 1";
169         string const line1 = line1_ss.str();
170         string const line2 = "o c " + inactive_color;
171         string const data(width, 'o');
172         vector<char *> inactive_data(height + 2,
173                                      const_cast<char *>(data.c_str()));
174         inactive_data[0] = const_cast<char *>(line1.c_str());
175         inactive_data[1] = const_cast<char *>(line2.c_str());
176
177         char ** raw_inactive_data = &*inactive_data.begin();
178
179         // Generate a pixmap of this data set.
180         // Together with 'mask' above, this is sufficient to display
181         // an inactive version of our active_pixmap.
182         Screen * screen = ScreenOfDisplay(display, fl_screen);
183
184         XpmCreatePixmapFromData(display, XRootWindowOfScreen(screen),
185                                 raw_inactive_data, &inactive_pixmap, 0, 0);
186 }
187
188 } // namespace frontend
189 } // namespace lyx
190
191
192 Toolbars::ToolbarPtr make_toolbar(ToolbarBackend::Toolbar const & tbb,
193                                   LyXView & owner)
194 {
195         using lyx::frontend::XFormsToolbar;
196         return Toolbars::ToolbarPtr(new XFormsToolbar(tbb, owner));
197 }
198
199
200 namespace lyx {
201 namespace frontend {
202
203 XFormsToolbar::XFormsToolbar(ToolbarBackend::Toolbar const & tbb,
204                              LyXView & o)
205         : toolbar_buttons_(0),
206           owner_(static_cast<XFormsView &>(o)),
207           tooltip_(new Tooltips)
208 {
209         position_ = getPosition(tbb.flags);
210         BoxList & boxlist = owner_.getBox(position_)->children();
211         toolbar_ = boxlist.push_back(Box(0,0));
212
213         // If the toolbar is horizontal, then it contains three
214         // vertically-aligned Boxes,the center one of which is to
215         // contain the buttons, aligned horizontally.
216         // The other two provide some visual padding.
217
218         // If it is vertical, then this is swapped around.
219
220         Box::Orientation const toolbar_orientation =
221                 (position_ == XFormsView::Left ||
222                  position_ == XFormsView::Right)
223                 ? Box::Vertical : Box::Horizontal;
224
225         Box::Orientation const padding_orientation =
226                 (toolbar_orientation == Box::Vertical)
227                 ? Box::Horizontal : Box::Vertical;
228
229         toolbar_->set(padding_orientation);
230
231         // A bit of a hack, but prevents 'M-x' causing the addition of
232         // visible borders.
233         int const padding =
234                 (tbb.name == "minibuffer") ?
235                 0 : 2 + abs(fl_get_border_width());
236
237         toolbar_->children().push_back(Box(padding, padding));
238
239         shared_ptr<Box> toolbar_center = toolbar_->children().push_back(Box(0,0));
240         toolbar_center->set(toolbar_orientation);
241         toolbar_buttons_ = &toolbar_center->children();
242
243         toolbar_->children().push_back(Box(padding, padding));
244
245         owner_.metricsUpdated.connect(boost::bind(&WidgetMap::updateMetrics,
246                                                   &widgets_));
247
248         // Populate the toolbar.
249         ToolbarBackend::item_iterator it = tbb.items.begin();
250         ToolbarBackend::item_iterator end = tbb.items.end();
251         for (; it != end; ++it)
252                 add(it->first, it->second);
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         layoutSelected(owner_, layoutguiname);
525 }
526
527 } // namespace frontend
528 } // namespace lyx