#include "XFormsToolbar.h"
+#include "Color.h"
#include "Tooltips.h"
#include "xforms_helpers.h"
-#include "XFormsView.h"
#include "buffer.h"
#include "bufferparams.h"
#include "gettext.h"
#include "lyxfunc.h"
+#include "support/lstrings.h"
+
#include "lyx_forms.h"
+#include "lyx_xpm.h"
#include "combox.h"
+#include <boost/bind.hpp>
+
+#include <sstream>
+#include <vector>
+
+using boost::shared_ptr;
+
using std::distance;
using std::endl;
using std::string;
+using std::vector;
+namespace lyx {
+
+using support::compare_ascii_no_case;
+
+namespace frontend {
// some constants
const int standardspacing = 2; // the usual space between items
const int buttonwidth = 30; // the standard button width
const int height = 30; // the height of all items in the toolbar
-XFormsToolbar::toolbarItem::toolbarItem()
- : icon(0)
-{}
+namespace {
+
+XFormsView::Position getPosition(ToolbarBackend::Flags const & flags)
+{
+ if (flags & ToolbarBackend::TOP)
+ return XFormsView::Top;
+ if (flags & ToolbarBackend::BOTTOM)
+ return XFormsView::Bottom;
+ if (flags & ToolbarBackend::LEFT)
+ return XFormsView::Left;
+ if (flags & ToolbarBackend::RIGHT)
+ return XFormsView::Right;
+ return XFormsView::Top;
+}
-XFormsToolbar::toolbarItem::~toolbarItem()
+LyXTextClass const & getTextClass(LyXView const & lv)
{
- // Lars said here that ~XFormsView() dealt with the icons.
- // This is not true. But enabling this causes crashes,
- // because somehow we kill the same icon twice :(
- // FIXME
- //kill_icon();
+ return lv.buffer()->params().getLyXTextClass();
}
+} // namespace anon
+
-/// Display toolbar, not implemented. But moved out of line so that
-/// linking will work properly.
-void XFormsToolbar::displayToolbar(ToolbarBackend::Toolbar const & /*tb*/,
- bool /*show*/)
+XFormsToolbar::toolbarItem::toolbarItem()
+ : icon(0),
+ unused_pixmap(0),
+ active_pixmap(0),
+ inactive_pixmap(0),
+ mask(0)
{}
+XFormsToolbar::toolbarItem::~toolbarItem()
+{
+ kill_icon();
+}
+
+
void XFormsToolbar::toolbarItem::kill_icon()
{
- if (icon) {
- fl_delete_object(icon);
- fl_free_object(icon);
- icon = 0;
- }
+ if (unused_pixmap)
+ // XForms will take care of cleaning up the other pixmap
+ XFreePixmap(fl_get_display(), unused_pixmap);
+
+ unused_pixmap = 0;
+ active_pixmap = 0;
+ inactive_pixmap = 0;
+ mask = 0;
+ icon = 0;
}
}
+void XFormsToolbar::toolbarItem::generateInactivePixmaps()
+{
+ if (!icon || icon->objclass != FL_PIXMAPBUTTON)
+ return;
+
+ // Store the existing (active) pixmap.
+ fl_get_pixmap_pixmap(icon, &active_pixmap, &mask);
+
+ if (active_pixmap == 0 || mask == 0)
+ return;
+
+ // Ascertain the width and height of the pixmap.
+ Display * display = fl_get_display();
+ unsigned int width;
+ unsigned int height;
+ unsigned int uidummy;
+ int idummy;
+ Window win;
+
+ XGetGeometry(display, active_pixmap, &win, &idummy, &idummy,
+ &width, &height, &uidummy, &uidummy);
+
+ // Produce a darker shade of the button background as the
+ // inactive color. Note the 'hsv.v - 0.2'.
+ unsigned int r, g, b;
+ fl_getmcolor(FL_PIXMAPBUTTON_COL1, &r, &g, &b);
+ HSVColor hsv(RGBColor(r, g, b));
+ hsv.v = std::max(0.0, hsv.v - 0.2);
+ string const inactive_color = X11hexname(RGBColor(hsv));
+
+ // Generate an XPM dataset for a uniformly-colored pixmap with
+ // the same dimensions as active_pixmap.
+
+ // The data set has the form:
+ // "<width> <height> <ncolors> <chars per pixel>",
+ // "o c <inactive_color>",
+ // "oooooooooooooooo", // <width> 'o' chars.
+ // repeated <height> times.
+ std::ostringstream line1_ss;
+ line1_ss << width << ' ' << height << " 1 1";
+ string const line1 = line1_ss.str();
+ string const line2 = "o c " + inactive_color;
+ string const data(width, 'o');
+ vector<char *> inactive_data(height + 2,
+ const_cast<char *>(data.c_str()));
+ inactive_data[0] = const_cast<char *>(line1.c_str());
+ inactive_data[1] = const_cast<char *>(line2.c_str());
+
+ char ** raw_inactive_data = &*inactive_data.begin();
+
+ // Generate a pixmap of this data set.
+ // Together with 'mask' above, this is sufficient to display
+ // an inactive version of our active_pixmap.
+ Screen * screen = ScreenOfDisplay(display, fl_screen);
+
+ XpmCreatePixmapFromData(display, XRootWindowOfScreen(screen),
+ raw_inactive_data, &inactive_pixmap, 0, 0);
+}
+
+} // namespace frontend
+} // namespace lyx
+
-XFormsToolbar::XFormsToolbar(LyXView * o, int x, int y)
- : owner_(static_cast<XFormsView *>(o)), combox_(0), xpos(x), ypos(y)
+Toolbars::ToolbarPtr make_toolbar(ToolbarBackend::Toolbar const & tbb,
+ LyXView & owner)
{
- tooltip_ = new Tooltips;
+ using lyx::frontend::XFormsToolbar;
+ return Toolbars::ToolbarPtr(new XFormsToolbar(tbb, owner));
+}
+
+
+namespace lyx {
+namespace frontend {
+
+XFormsToolbar::XFormsToolbar(ToolbarBackend::Toolbar const & tbb,
+ LyXView & o)
+ : toolbar_buttons_(0),
+ owner_(static_cast<XFormsView &>(o)),
+ tooltip_(new Tooltips)
+{
+ position_ = getPosition(tbb.flags);
+ BoxList & boxlist = owner_.getBox(position_)->children();
+ toolbar_ = boxlist.push_back(Box(0,0));
+
+ // If the toolbar is horizontal, then it contains three
+ // vertically-aligned Boxes,the center one of which is to
+ // contain the buttons, aligned horizontally.
+ // The other two provide some visual padding.
+
+ // If it is vertical, then this is swapped around.
+
+ Box::Orientation const toolbar_orientation =
+ (position_ == XFormsView::Left ||
+ position_ == XFormsView::Right)
+ ? Box::Vertical : Box::Horizontal;
+
+ Box::Orientation const padding_orientation =
+ (toolbar_orientation == Box::Vertical)
+ ? Box::Horizontal : Box::Vertical;
+
+ toolbar_->set(padding_orientation);
+
+ // A bit of a hack, but prevents 'M-x' causing the addition of
+ // visible borders.
+ int const padding =
+ (tbb.name == "minibuffer") ?
+ 0 : 2 + abs(fl_get_border_width());
+
+ toolbar_->children().push_back(Box(padding, padding));
+
+ shared_ptr<Box> toolbar_center = toolbar_->children().push_back(Box(0,0));
+ toolbar_center->set(toolbar_orientation);
+ toolbar_buttons_ = &toolbar_center->children();
+
+ toolbar_->children().push_back(Box(padding, padding));
+
+ owner_.metricsUpdated.connect(boost::bind(&WidgetMap::updateMetrics,
+ &widgets_));
+
+ // Populate the toolbar.
+ ToolbarBackend::item_iterator it = tbb.items.begin();
+ ToolbarBackend::item_iterator end = tbb.items.end();
+ for (; it != end; ++it)
+ add(it->first, it->second);
}
XFormsToolbar::~XFormsToolbar()
{
- fl_freeze_form(owner_->getForm());
+ fl_freeze_form(owner_.getForm());
// G++ vector does not have clear defined
//toollist.clear();
toollist_.erase(toollist_.begin(), toollist_.end());
- fl_unfreeze_form(owner_->getForm());
- delete tooltip_;
+ fl_unfreeze_form(owner_.getForm());
+}
+
+
+namespace {
+
+extern "C" {
+
+void C_ToolbarCB(FL_OBJECT * ob, long ac)
+{
+ if (!ob || !ob->u_vdata)
+ return;
+
+ XFormsToolbar * ptr = static_cast<XFormsToolbar *>(ob->u_vdata);
+ XFormsView & owner = ptr->owner_;
+ owner.getLyXFunc().dispatch(ptr->funcs[ac]);
+}
+
+} // extern "C"
+
+} // namespace anon
+
+
+void XFormsToolbar::hide(bool update_metrics)
+{
+ if (!toolbar_->visible())
+ return;
+
+ toolbar_->set(Box::Invisible);
+ if (update_metrics)
+ owner_.updateMetrics();
+}
+
+
+void XFormsToolbar::show(bool update_metrics)
+{
+ if (toolbar_->visible())
+ return;
+
+ toolbar_->set(Box::Visible);
+ toolbar_->show();
+ if (update_metrics)
+ owner_.updateMetrics();
+}
+
+
+void XFormsToolbar::add(FuncRequest const & func, string const & tooltip)
+{
+ toolbarItem item;
+ item.func = func;
+
+ switch (func.action) {
+ case ToolbarBackend::SEPARATOR:
+ toolbar_buttons_->push_back(Box(sepspace, sepspace));
+ break;
+
+ case ToolbarBackend::MINIBUFFER:
+ // Not implemented.
+ // XForms uses the same widget to display the buffer messages
+ // and to input commands.
+ break;
+
+ case ToolbarBackend::LAYOUTS:
+ layout_.reset(new XLayoutBox(owner_, *this));
+ break;
+
+ default: {
+ FL_OBJECT * obj;
+
+ toolbar_buttons_->push_back(Box(standardspacing,
+ standardspacing));
+
+ item.icon = obj =
+ fl_add_pixmapbutton(FL_NORMAL_BUTTON,
+ 0, 0, 0, 0, "");
+
+ widgets_.add(obj, *toolbar_buttons_, buttonwidth, height);
+
+ fl_set_object_resize(obj, FL_RESIZE_ALL);
+
+ int gravity = 0;
+ if (position_ == XFormsView::Top ||
+ position_ == XFormsView::Left)
+ gravity = NorthWestGravity;
+ else if (position_ == XFormsView::Right)
+ gravity = NorthEastGravity;
+ else if (position_ == XFormsView::Bottom)
+ gravity = SouthWestGravity;
+
+ fl_set_object_gravity(obj, gravity, gravity);
+
+ Funcs::iterator fit = funcs.insert(funcs.end(), func);
+ int const index = distance(funcs.begin(), fit);
+ fl_set_object_callback(obj, C_ToolbarCB, index);
+ // Remove the blue feedback rectangle
+ fl_set_pixmapbutton_focus_outline(obj, 0);
+
+ tooltip_->init(obj, tooltip);
+
+ // The view that this object belongs to.
+ obj->u_vdata = this;
+
+ string const xpm = toolbarbackend.getIcon(func);
+ fl_set_pixmapbutton_file(obj, xpm.c_str());
+ break;
+ }
+ }
+
+ toollist_.push_back(item);
}
void XFormsToolbar::update()
{
- ToolbarList::const_iterator p = toollist_.begin();
- ToolbarList::const_iterator end = toollist_.end();
+ ToolbarList::iterator p = toollist_.begin();
+ ToolbarList::iterator end = toollist_.end();
for (; p != end; ++p) {
- if (p->func.action == int(ToolbarBackend::LAYOUTS) && combox_) {
- LyXFunc const & lf = owner_->getLyXFunc();
- bool const enable =
- lf.getStatus(FuncRequest(LFUN_LAYOUT)).enabled();
- setEnabled(combox_, enable);
- continue;
- }
-
if (!p->icon)
continue;
- FuncStatus const status = owner_->getLyXFunc().getStatus(p->func);
+ FuncStatus const status =
+ owner_.getLyXFunc().getStatus(p->func);
+
if (status.onoff(true)) {
// I'd like to use a different color
// here, but then the problem is to
fl_set_object_color(p->icon, FL_MCOL, FL_BLUE);
fl_set_object_boxtype(p->icon, FL_UP_BOX);
}
+
+ // This must go here rather than in XFormsToolbar::add, else
+ // LyX aborts with a BadPixmap error.
+ if (!p->active_pixmap)
+ p->generateInactivePixmaps();
+
if (status.enabled()) {
fl_activate_object(p->icon);
+ fl_set_pixmap_pixmap(p->icon,
+ p->active_pixmap,
+ p->mask);
+ p->unused_pixmap = p->inactive_pixmap;
} else {
- // Is there a way here to specify a
- // mask in order to show that the
- // button is disabled? (JMarc)
fl_deactivate_object(p->icon);
+ fl_set_pixmap_pixmap(p->icon,
+ p->inactive_pixmap,
+ p->mask);
+ p->unused_pixmap = p->active_pixmap;
}
}
+
+ bool const enable = owner_.getLyXFunc().
+ getStatus(FuncRequest(LFUN_LAYOUT)).enabled();
+
+ if (layout_.get())
+ layout_->setEnabled(enable);
}
namespace {
-void C_layoutSelectedCB(FL_OBJECT * ob, long)
+extern "C"
+void C_LayoutBoxSelectedCB(FL_OBJECT * ob, long)
{
if (!ob || !ob->u_vdata)
return;
- XFormsToolbar * ptr = static_cast<XFormsToolbar *>(ob->u_vdata);
- ptr->layoutSelected();
+ XLayoutBox * ptr = static_cast<XLayoutBox *>(ob->u_vdata);
+ ptr->selected();
}
} // namespace anon
-void XFormsToolbar::layoutSelected()
+XLayoutBox::XLayoutBox(LyXView & owner, XFormsToolbar & toolbar)
+ : owner_(owner)
{
- if (!combox_)
- return;
+ toolbar.toolbar_buttons_->push_back(Box(standardspacing, 0));
- string const & layoutguiname = getString(combox_);
- LyXTextClass const & tc =
- owner_->buffer()->params().getLyXTextClass();
+ combox_ = fl_add_combox(FL_DROPLIST_COMBOX,
+ 0, 0, 135, height, "");
- LyXTextClass::const_iterator end = tc.end();
- for (LyXTextClass::const_iterator cit = tc.begin();
- cit != end; ++cit) {
- if (_((*cit)->name()) == layoutguiname) {
- owner_->getLyXFunc().dispatch(FuncRequest(LFUN_LAYOUT, (*cit)->name()), true);
- return;
- }
- }
- lyxerr << "ERROR (XFormsToolbar::layoutSelected): layout not found!"
- << endl;
+ toolbar.widgets_.add(combox_, *toolbar.toolbar_buttons_, 135, height);
+
+ fl_set_combox_browser_height(combox_, 400);
+ fl_set_object_boxtype(combox_, FL_DOWN_BOX);
+ fl_set_object_color(combox_, FL_MCOL, FL_MCOL);
+ fl_set_object_gravity(combox_, FL_NorthWest, FL_NorthWest);
+ fl_set_object_resize(combox_, FL_RESIZE_ALL);
+
+ combox_->u_vdata = this;
+ fl_set_object_callback(combox_, C_LayoutBoxSelectedCB, 0);
}
-void XFormsToolbar::setLayout(string const & layout)
+void XLayoutBox::set(string const & layout)
{
- if (!combox_)
- return;
+ LyXTextClass const & tc = getTextClass(owner_);
- LyXTextClass const & tc = owner_->buffer()->params().getLyXTextClass();
string const layoutname = _(tc[layout]->name());
int const nnames = fl_get_combox_maxitems(combox_);
}
-void XFormsToolbar::updateLayoutList()
+void XLayoutBox::update()
{
- if (!combox_)
- return;
+ LyXTextClass const & tc = getTextClass(owner_);
fl_clear_combox(combox_);
- LyXTextClass const & tc = owner_->buffer()->params().getLyXTextClass();
- LyXTextClass::const_iterator end = tc.end();
- for (LyXTextClass::const_iterator cit = tc.begin();
- cit != end; ++cit) {
+
+ LyXTextClass::const_iterator it = tc.begin();
+ LyXTextClass::const_iterator const end = tc.end();
+ for (; it != end; ++it) {
// ignore obsolete entries
- if ((*cit)->obsoleted_by().empty()) {
- string const & name = _((*cit)->name());
+ if ((*it)->obsoleted_by().empty()) {
+ string const & name = _((*it)->name());
fl_addto_combox(combox_, name.c_str());
}
}
}
-void XFormsToolbar::clearLayoutList()
+void XLayoutBox::clear()
{
- if (!combox_)
- return;
-
- Toolbar::clearLayoutList();
fl_clear_combox(combox_);
fl_redraw_object(combox_);
}
-void XFormsToolbar::openLayoutList()
+void XLayoutBox::open()
{
- if (!combox_)
- return;
-
fl_show_combox_browser(combox_);
}
-namespace {
-
-void ToolbarCB(FL_OBJECT * ob, long ac)
+void XLayoutBox::setEnabled(bool enable)
{
- if (!ob || !ob->u_vdata)
- return;
-
- XFormsToolbar * ptr = static_cast<XFormsToolbar *>(ob->u_vdata);
- XFormsView * owner = ptr->owner_;
- owner->getLyXFunc().dispatch(ptr->funcs[ac], true);
+ lyx::frontend::setEnabled(combox_, enable);
}
-extern "C" {
-
-void C_Toolbar_ToolbarCB(FL_OBJECT * ob, long data)
+void XLayoutBox::selected()
{
- ToolbarCB(ob, data);
-}
+ string const layoutguiname = getString(combox_);
+ layoutSelected(owner_, layoutguiname);
}
-} // namespace anon
-
-
-void XFormsToolbar::add(ToolbarBackend::Toolbar const & tb)
-{
- // we can only handle one toolbar
- if (!toollist_.empty())
- return;
-
- funcs.clear();
-
- ToolbarBackend::item_iterator it = tb.items.begin();
- ToolbarBackend::item_iterator end = tb.items.end();
- for (; it != end; ++it)
- add(it->first, it->second);
-}
-
-
-void XFormsToolbar::add(FuncRequest const & func, string const & tooltip)
-{
- toolbarItem item;
- item.func = func;
-
- switch (func.action) {
- case ToolbarBackend::SEPARATOR:
- xpos += sepspace;
- break;
- case ToolbarBackend::MINIBUFFER:
- // Not implemented
- break;
- case ToolbarBackend::LAYOUTS:
- xpos += standardspacing;
- if (combox_)
- break;
-
- combox_ = fl_add_combox(FL_DROPLIST_COMBOX,
- xpos, ypos, 135, height, "");
- fl_set_combox_browser_height(combox_, 400);
- fl_set_object_boxtype(combox_, FL_DOWN_BOX);
- fl_set_object_color(combox_, FL_MCOL, FL_MCOL);
- fl_set_object_gravity(combox_, FL_NorthWest, FL_NorthWest);
- fl_set_object_resize(combox_, FL_RESIZE_ALL);
-
- combox_->u_vdata = this;
- fl_set_object_callback(combox_, C_layoutSelectedCB, 0);
- xpos += 135;
- break;
- default: {
- FL_OBJECT * obj;
-
- xpos += standardspacing;
- item.icon = obj =
- fl_add_pixmapbutton(FL_NORMAL_BUTTON,
- xpos, ypos,
- buttonwidth,
- height, "");
- fl_set_object_resize(obj, FL_RESIZE_ALL);
- fl_set_object_gravity(obj,
- NorthWestGravity,
- NorthWestGravity);
-
- Funcs::iterator fit = funcs.insert(funcs.end(), func);
- int const index = distance(funcs.begin(), fit);
- fl_set_object_callback(obj, C_Toolbar_ToolbarCB, index);
- // Remove the blue feedback rectangle
- fl_set_pixmapbutton_focus_outline(obj, 0);
-
- tooltip_->init(obj, tooltip);
-
- // The view that this object belongs to.
- obj->u_vdata = this;
-
- string const xpm = toolbarbackend.getIcon(func);
- fl_set_pixmapbutton_file(obj, xpm.c_str());
-
- // we must remember to update the positions
- xpos += buttonwidth;
- // ypos is constant
- /* Here will come a check to see if the new
- * pos is within the bounds of the main frame,
- * and perhaps wrap the toolbar if not.
- */
- break;
- }
- }
-
- toollist_.push_back(item);
-}
+} // namespace frontend
+} // namespace lyx