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 toolbar_->set(Box::Invisible);
285 owner_.updateMetrics();
289 void XFormsToolbar::show(bool update_metrics)
291 toolbar_->set(Box::Visible);
294 owner_.updateMetrics();
298 void XFormsToolbar::add(FuncRequest const & func, string const & tooltip)
303 switch (func.action) {
304 case ToolbarBackend::SEPARATOR:
305 toolbar_buttons_->push_back(Box(sepspace, sepspace));
308 case ToolbarBackend::MINIBUFFER:
310 // XForms uses the same widget to display the buffer messages
311 // and to input commands.
314 case ToolbarBackend::LAYOUTS:
315 layout_.reset(new XLayoutBox(owner_, *this));
321 toolbar_buttons_->push_back(Box(standardspacing,
325 fl_add_pixmapbutton(FL_NORMAL_BUTTON,
328 widgets_.add(obj, *toolbar_buttons_, buttonwidth, height);
330 fl_set_object_resize(obj, FL_RESIZE_ALL);
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;
341 fl_set_object_gravity(obj, gravity, gravity);
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);
349 tooltip_->init(obj, tooltip);
351 // The view that this object belongs to.
354 string const xpm = toolbarbackend.getIcon(func);
355 fl_set_pixmapbutton_file(obj, xpm.c_str());
360 toollist_.push_back(item);
364 void XFormsToolbar::update()
366 ToolbarList::iterator p = toollist_.begin();
367 ToolbarList::iterator end = toollist_.end();
368 for (; p != end; ++p) {
372 FuncStatus const status =
373 owner_.getLyXFunc().getStatus(p->func);
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);
384 fl_set_object_color(p->icon, FL_MCOL, FL_BLUE);
385 fl_set_object_boxtype(p->icon, FL_UP_BOX);
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();
393 if (status.enabled()) {
394 fl_activate_object(p->icon);
395 fl_set_pixmap_pixmap(p->icon,
398 p->unused_pixmap = p->inactive_pixmap;
400 fl_deactivate_object(p->icon);
401 fl_set_pixmap_pixmap(p->icon,
404 p->unused_pixmap = p->active_pixmap;
408 bool const enable = owner_.getLyXFunc().
409 getStatus(FuncRequest(LFUN_LAYOUT)).enabled();
412 layout_->setEnabled(enable);
419 void C_LayoutBoxSelectedCB(FL_OBJECT * ob, long)
421 if (!ob || !ob->u_vdata)
423 XLayoutBox * ptr = static_cast<XLayoutBox *>(ob->u_vdata);
430 XLayoutBox::XLayoutBox(LyXView & owner, XFormsToolbar & toolbar)
433 toolbar.toolbar_buttons_->push_back(Box(standardspacing, 0));
435 combox_ = fl_add_combox(FL_DROPLIST_COMBOX,
436 0, 0, 135, height, "");
438 toolbar.widgets_.add(combox_, *toolbar.toolbar_buttons_, 135, height);
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);
446 combox_->u_vdata = this;
447 fl_set_object_callback(combox_, C_LayoutBoxSelectedCB, 0);
451 void XLayoutBox::set(string const & layout)
453 LyXTextClass const & tc = getTextClass(owner_);
455 string const layoutname = _(tc[layout]->name());
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);
468 void XLayoutBox::update()
470 LyXTextClass const & tc = getTextClass(owner_);
472 fl_clear_combox(combox_);
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());
484 // we need to do this.
485 fl_redraw_object(combox_);
489 void XLayoutBox::clear()
491 fl_clear_combox(combox_);
492 fl_redraw_object(combox_);
496 void XLayoutBox::open()
498 fl_show_combox_browser(combox_);
502 void XLayoutBox::setEnabled(bool enable)
504 ::setEnabled(combox_, enable);
508 void XLayoutBox::selected()
510 string const layoutguiname = getString(combox_);
512 LyXTextClass const & tc = getTextClass(owner_);
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) {
520 .dispatch(FuncRequest(LFUN_LAYOUT, name),
525 lyxerr << "ERROR (XLayoutBox::selected): layout not found!" << endl;