2 * \file XFormsToolbar.C
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Jean-Marc Lasgouttes
9 * Full author contact details are available in file CREDITS.
12 // Added pseudo-action handling, asierra 180296
16 #include "XFormsToolbar.h"
20 #include "xforms_helpers.h"
23 #include "bufferparams.h"
25 #include "funcrequest.h"
26 #include "FuncStatus.h"
30 #include "support/lstrings.h"
32 #include "lyx_forms.h"
36 #include <boost/bind.hpp>
41 using lyx::frontend::Box;
42 using lyx::frontend::BoxList;
44 using lyx::support::compare_ascii_no_case;
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
60 XFormsView::Position getPosition(ToolbarBackend::Flags const & flags)
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;
74 LyXTextClass const & getTextClass(LyXView const & lv)
76 return lv.buffer()->params().getLyXTextClass();
82 XFormsToolbar::toolbarItem::toolbarItem()
91 XFormsToolbar::toolbarItem::~toolbarItem()
97 void XFormsToolbar::toolbarItem::kill_icon()
100 // XForms will take care of cleaning up the other pixmap
101 XFreePixmap(fl_get_display(), unused_pixmap);
111 XFormsToolbar::toolbarItem &
112 XFormsToolbar::toolbarItem::operator=(toolbarItem const & ti)
117 // If we already have an icon, release it.
118 // But we don't copy the icon from ti
127 void XFormsToolbar::toolbarItem::generateInactivePixmaps()
129 if (!icon || icon->objclass != FL_PIXMAPBUTTON)
132 // Store the existing (active) pixmap.
133 fl_get_pixmap_pixmap(icon, &active_pixmap, &mask);
135 if (active_pixmap == 0 || mask == 0)
138 // Ascertain the width and height of the pixmap.
139 Display * display = fl_get_display();
142 unsigned int uidummy;
146 XGetGeometry(display, active_pixmap, &win, &idummy, &idummy,
147 &width, &height, &uidummy, &uidummy);
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));
157 // Generate an XPM dataset for a uniformly-colored pixmap with
158 // the same dimensions as active_pixmap.
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());
175 char ** raw_inactive_data = &*inactive_data.begin();
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);
182 XpmCreatePixmapFromData(display, XRootWindowOfScreen(screen),
183 raw_inactive_data, &inactive_pixmap, 0, 0);
187 Toolbars::ToolbarPtr make_toolbar(ToolbarBackend::Toolbar const & tbb,
190 return Toolbars::ToolbarPtr(new XFormsToolbar(tbb, owner));
194 XFormsToolbar::XFormsToolbar(ToolbarBackend::Toolbar const & tbb,
198 owner_(static_cast<XFormsView &>(o)),
199 tooltip_(new Tooltips)
201 position_ = getPosition(tbb.flags);
202 BoxList & boxlist = owner_.getBox(position_).children();
203 toolbar_ = &boxlist.push_back(Box(0,0));
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.
210 // If it is vertical, then this is swapped around.
212 Box::Orientation const toolbar_orientation =
213 (position_ == XFormsView::Left ||
214 position_ == XFormsView::Right)
215 ? Box::Vertical : Box::Horizontal;
217 Box::Orientation const padding_orientation =
218 (toolbar_orientation == Box::Vertical)
219 ? Box::Horizontal : Box::Vertical;
221 toolbar_->set(padding_orientation);
223 // A bit of a hack, but prevents 'M-x' causing the addition of
226 (tbb.name == "minibuffer") ?
227 0 : 2 + abs(fl_get_border_width());
229 toolbar_->children().push_back(Box(padding, padding));
231 Box & toolbar_center = toolbar_->children().push_back(Box(0,0));
232 toolbar_center.set(toolbar_orientation);
233 toolbar_buttons_ = &toolbar_center.children();
235 toolbar_->children().push_back(Box(padding, padding));
237 using lyx::frontend::WidgetMap;
238 owner_.metricsUpdated.connect(boost::bind(&WidgetMap::updateMetrics,
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);
250 XFormsToolbar::~XFormsToolbar()
252 fl_freeze_form(owner_.getForm());
254 // G++ vector does not have clear defined
256 toollist_.erase(toollist_.begin(), toollist_.end());
258 fl_unfreeze_form(owner_.getForm());
266 void C_ToolbarCB(FL_OBJECT * ob, long ac)
268 if (!ob || !ob->u_vdata)
271 XFormsToolbar * ptr = static_cast<XFormsToolbar *>(ob->u_vdata);
272 XFormsView & owner = ptr->owner_;
273 owner.getLyXFunc().dispatch(ptr->funcs[ac], true);
281 void XFormsToolbar::hide(bool update_metrics)
283 if (!toolbar_->visible())
286 toolbar_->set(Box::Invisible);
288 owner_.updateMetrics();
292 void XFormsToolbar::show(bool update_metrics)
294 if (toolbar_->visible())
297 toolbar_->set(Box::Visible);
300 owner_.updateMetrics();
304 void XFormsToolbar::add(FuncRequest const & func, string const & tooltip)
309 switch (func.action) {
310 case ToolbarBackend::SEPARATOR:
311 toolbar_buttons_->push_back(Box(sepspace, sepspace));
314 case ToolbarBackend::MINIBUFFER:
316 // XForms uses the same widget to display the buffer messages
317 // and to input commands.
320 case ToolbarBackend::LAYOUTS:
321 layout_.reset(new XLayoutBox(owner_, *this));
327 toolbar_buttons_->push_back(Box(standardspacing,
331 fl_add_pixmapbutton(FL_NORMAL_BUTTON,
334 widgets_.add(obj, *toolbar_buttons_, buttonwidth, height);
336 fl_set_object_resize(obj, FL_RESIZE_ALL);
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;
347 fl_set_object_gravity(obj, gravity, gravity);
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);
355 tooltip_->init(obj, tooltip);
357 // The view that this object belongs to.
360 string const xpm = toolbarbackend.getIcon(func);
361 fl_set_pixmapbutton_file(obj, xpm.c_str());
366 toollist_.push_back(item);
370 void XFormsToolbar::update()
372 ToolbarList::iterator p = toollist_.begin();
373 ToolbarList::iterator end = toollist_.end();
374 for (; p != end; ++p) {
378 FuncStatus const status =
379 owner_.getLyXFunc().getStatus(p->func);
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);
390 fl_set_object_color(p->icon, FL_MCOL, FL_BLUE);
391 fl_set_object_boxtype(p->icon, FL_UP_BOX);
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();
399 if (status.enabled()) {
400 fl_activate_object(p->icon);
401 fl_set_pixmap_pixmap(p->icon,
404 p->unused_pixmap = p->inactive_pixmap;
406 fl_deactivate_object(p->icon);
407 fl_set_pixmap_pixmap(p->icon,
410 p->unused_pixmap = p->active_pixmap;
414 bool const enable = owner_.getLyXFunc().
415 getStatus(FuncRequest(LFUN_LAYOUT)).enabled();
418 layout_->setEnabled(enable);
425 void C_LayoutBoxSelectedCB(FL_OBJECT * ob, long)
427 if (!ob || !ob->u_vdata)
429 XLayoutBox * ptr = static_cast<XLayoutBox *>(ob->u_vdata);
436 XLayoutBox::XLayoutBox(LyXView & owner, XFormsToolbar & toolbar)
439 toolbar.toolbar_buttons_->push_back(Box(standardspacing, 0));
441 combox_ = fl_add_combox(FL_DROPLIST_COMBOX,
442 0, 0, 135, height, "");
444 toolbar.widgets_.add(combox_, *toolbar.toolbar_buttons_, 135, height);
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);
452 combox_->u_vdata = this;
453 fl_set_object_callback(combox_, C_LayoutBoxSelectedCB, 0);
457 void XLayoutBox::set(string const & layout)
459 LyXTextClass const & tc = getTextClass(owner_);
461 string const layoutname = _(tc[layout]->name());
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);
474 void XLayoutBox::update()
476 LyXTextClass const & tc = getTextClass(owner_);
478 fl_clear_combox(combox_);
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());
490 // we need to do this.
491 fl_redraw_object(combox_);
495 void XLayoutBox::clear()
497 fl_clear_combox(combox_);
498 fl_redraw_object(combox_);
502 void XLayoutBox::open()
504 fl_show_combox_browser(combox_);
508 void XLayoutBox::setEnabled(bool enable)
510 ::setEnabled(combox_, enable);
514 void XLayoutBox::selected()
516 string const layoutguiname = getString(combox_);
518 LyXTextClass const & tc = getTextClass(owner_);
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) {
526 .dispatch(FuncRequest(LFUN_LAYOUT, name),
531 lyxerr << "ERROR (XLayoutBox::selected): layout not found!" << endl;