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>
48 using 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);
186 } // namespace frontend
190 Toolbars::ToolbarPtr make_toolbar(ToolbarBackend::Toolbar const & tbb,
193 using lyx::frontend::XFormsToolbar;
194 return Toolbars::ToolbarPtr(new XFormsToolbar(tbb, owner));
201 XFormsToolbar::XFormsToolbar(ToolbarBackend::Toolbar const & tbb,
205 owner_(static_cast<XFormsView &>(o)),
206 tooltip_(new Tooltips)
208 position_ = getPosition(tbb.flags);
209 BoxList & boxlist = owner_.getBox(position_).children();
210 toolbar_ = &boxlist.push_back(Box(0,0));
212 // If the toolbar is horizontal, then it contains three
213 // vertically-aligned Boxes,the center one of which is to
214 // contain the buttons, aligned horizontally.
215 // The other two provide some visual padding.
217 // If it is vertical, then this is swapped around.
219 Box::Orientation const toolbar_orientation =
220 (position_ == XFormsView::Left ||
221 position_ == XFormsView::Right)
222 ? Box::Vertical : Box::Horizontal;
224 Box::Orientation const padding_orientation =
225 (toolbar_orientation == Box::Vertical)
226 ? Box::Horizontal : Box::Vertical;
228 toolbar_->set(padding_orientation);
230 // A bit of a hack, but prevents 'M-x' causing the addition of
233 (tbb.name == "minibuffer") ?
234 0 : 2 + abs(fl_get_border_width());
236 toolbar_->children().push_back(Box(padding, padding));
238 Box & toolbar_center = toolbar_->children().push_back(Box(0,0));
239 toolbar_center.set(toolbar_orientation);
240 toolbar_buttons_ = &toolbar_center.children();
242 toolbar_->children().push_back(Box(padding, padding));
244 owner_.metricsUpdated.connect(boost::bind(&WidgetMap::updateMetrics,
247 // Populate the toolbar.
248 ToolbarBackend::item_iterator it = tbb.items.begin();
249 ToolbarBackend::item_iterator end = tbb.items.end();
250 for (; it != end; ++it)
251 add(it->first, it->second);
256 XFormsToolbar::~XFormsToolbar()
258 fl_freeze_form(owner_.getForm());
260 // G++ vector does not have clear defined
262 toollist_.erase(toollist_.begin(), toollist_.end());
264 fl_unfreeze_form(owner_.getForm());
272 void C_ToolbarCB(FL_OBJECT * ob, long ac)
274 if (!ob || !ob->u_vdata)
277 XFormsToolbar * ptr = static_cast<XFormsToolbar *>(ob->u_vdata);
278 XFormsView & owner = ptr->owner_;
279 owner.getLyXFunc().dispatch(ptr->funcs[ac], true);
287 void XFormsToolbar::hide(bool update_metrics)
289 if (!toolbar_->visible())
292 toolbar_->set(Box::Invisible);
294 owner_.updateMetrics();
298 void XFormsToolbar::show(bool update_metrics)
300 if (toolbar_->visible())
303 toolbar_->set(Box::Visible);
306 owner_.updateMetrics();
310 void XFormsToolbar::add(FuncRequest const & func, string const & tooltip)
315 switch (func.action) {
316 case ToolbarBackend::SEPARATOR:
317 toolbar_buttons_->push_back(Box(sepspace, sepspace));
320 case ToolbarBackend::MINIBUFFER:
322 // XForms uses the same widget to display the buffer messages
323 // and to input commands.
326 case ToolbarBackend::LAYOUTS:
327 layout_.reset(new XLayoutBox(owner_, *this));
333 toolbar_buttons_->push_back(Box(standardspacing,
337 fl_add_pixmapbutton(FL_NORMAL_BUTTON,
340 widgets_.add(obj, *toolbar_buttons_, buttonwidth, height);
342 fl_set_object_resize(obj, FL_RESIZE_ALL);
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;
353 fl_set_object_gravity(obj, gravity, gravity);
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);
361 tooltip_->init(obj, tooltip);
363 // The view that this object belongs to.
366 string const xpm = toolbarbackend.getIcon(func);
367 fl_set_pixmapbutton_file(obj, xpm.c_str());
372 toollist_.push_back(item);
376 void XFormsToolbar::update()
378 ToolbarList::iterator p = toollist_.begin();
379 ToolbarList::iterator end = toollist_.end();
380 for (; p != end; ++p) {
384 FuncStatus const status =
385 owner_.getLyXFunc().getStatus(p->func);
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);
396 fl_set_object_color(p->icon, FL_MCOL, FL_BLUE);
397 fl_set_object_boxtype(p->icon, FL_UP_BOX);
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();
405 if (status.enabled()) {
406 fl_activate_object(p->icon);
407 fl_set_pixmap_pixmap(p->icon,
410 p->unused_pixmap = p->inactive_pixmap;
412 fl_deactivate_object(p->icon);
413 fl_set_pixmap_pixmap(p->icon,
416 p->unused_pixmap = p->active_pixmap;
420 bool const enable = owner_.getLyXFunc().
421 getStatus(FuncRequest(LFUN_LAYOUT)).enabled();
424 layout_->setEnabled(enable);
431 void C_LayoutBoxSelectedCB(FL_OBJECT * ob, long)
433 if (!ob || !ob->u_vdata)
435 XLayoutBox * ptr = static_cast<XLayoutBox *>(ob->u_vdata);
442 XLayoutBox::XLayoutBox(LyXView & owner, XFormsToolbar & toolbar)
445 toolbar.toolbar_buttons_->push_back(Box(standardspacing, 0));
447 combox_ = fl_add_combox(FL_DROPLIST_COMBOX,
448 0, 0, 135, height, "");
450 toolbar.widgets_.add(combox_, *toolbar.toolbar_buttons_, 135, height);
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);
458 combox_->u_vdata = this;
459 fl_set_object_callback(combox_, C_LayoutBoxSelectedCB, 0);
463 void XLayoutBox::set(string const & layout)
465 LyXTextClass const & tc = getTextClass(owner_);
467 string const layoutname = _(tc[layout]->name());
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);
480 void XLayoutBox::update()
482 LyXTextClass const & tc = getTextClass(owner_);
484 fl_clear_combox(combox_);
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());
496 // we need to do this.
497 fl_redraw_object(combox_);
501 void XLayoutBox::clear()
503 fl_clear_combox(combox_);
504 fl_redraw_object(combox_);
508 void XLayoutBox::open()
510 fl_show_combox_browser(combox_);
514 void XLayoutBox::setEnabled(bool enable)
516 lyx::frontend::setEnabled(combox_, enable);
520 void XLayoutBox::selected()
522 string const layoutguiname = getString(combox_);
524 LyXTextClass const & tc = getTextClass(owner_);
526 LyXTextClass::const_iterator it = tc.begin();
527 LyXTextClass::const_iterator const end = tc.end();
528 for (; it != end; ++it) {
529 string const & name = (*it)->name();
530 if (_(name) == layoutguiname) {
532 .dispatch(FuncRequest(LFUN_LAYOUT, name),
537 lyxerr << "ERROR (XLayoutBox::selected): layout not found!" << endl;
540 } // namespace frontend