]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/xforms/XFormsToolbar.C
Change glob() API to accept a dir parameter.
[lyx.git] / src / frontends / xforms / XFormsToolbar.C
index 6c818ceec870064b3b1e52c83b7bfe091ed986fd..ccee31a916989d0a83ffd05f9378ebc8ebea209e 100644 (file)
 
 #include "XFormsToolbar.h"
 
+#include "Color.h"
 #include "Tooltips.h"
 #include "xforms_helpers.h"
-#include "XFormsView.h"
 
 #include "buffer.h"
+#include "bufferparams.h"
 #include "debug.h"
 #include "funcrequest.h"
 #include "FuncStatus.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
@@ -38,35 +57,56 @@ const int sepspace = 6; // extra space
 const int buttonwidth = 30; // the standard button width
 const int height = 30; // the height of all items in the toolbar
 
-XFormsToolbar::toolbarItem::toolbarItem()
-       : action(LFUN_NOACTION), 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;
 }
 
 
@@ -80,50 +120,270 @@ XFormsToolbar::toolbarItem::operator=(toolbarItem const & ti)
        // But we don't copy the icon from ti
        kill_icon();
 
-       action = ti.action;
+       func = ti.func;
 
        return *this;
 }
 
 
+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->action == ToolbarBackend::LAYOUTS && combox_) {
-                       LyXFunc const & lf = owner_->getLyXFunc();
-                       bool const disable =
-                               lf.getStatus(LFUN_LAYOUT).disabled();
-                       setEnabled(combox_, !disable);
-                       continue;
-               }
-
                if (!p->icon)
                        continue;
 
-               FuncStatus const status = owner_->getLyXFunc().getStatus(p->action);
+               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
@@ -136,59 +396,74 @@ void XFormsToolbar::update()
                        fl_set_object_color(p->icon, FL_MCOL, FL_BLUE);
                        fl_set_object_boxtype(p->icon, FL_UP_BOX);
                }
-               if (status.disabled()) {
-                       // Is there a way here to specify a
-                       // mask in order to show that the
-                       // button is disabled? (JMarc)
+
+               // 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 {
                        fl_deactivate_object(p->icon);
+                       fl_set_pixmap_pixmap(p->icon,
+                                            p->inactive_pixmap,
+                                            p->mask);
+                       p->unused_pixmap = p->active_pixmap;
                }
-               else
-                       fl_activate_object(p->icon);
        }
+
+       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_);
@@ -202,19 +477,18 @@ void XFormsToolbar::setLayout(string const & layout)
 }
 
 
-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());
                }
        }
@@ -224,126 +498,31 @@ void XFormsToolbar::updateLayoutList()
 }
 
 
-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)
 {
-       XFormsView * owner = static_cast<XFormsView *>(ob->u_vdata);
-
-       owner->getLyXFunc().dispatch(int(ac), true);
-}
-
-
-extern "C" {
-
-void C_Toolbar_ToolbarCB(FL_OBJECT * ob, long data)
-{
-       ToolbarCB(ob, data);
-}
-
+       lyx::frontend::setEnabled(combox_, enable);
 }
 
-} // namespace anon
-
 
-void XFormsToolbar::add(ToolbarBackend::Toolbar const & tb)
+void XLayoutBox::selected()
 {
-       // we can only handle one toolbar
-       if (!toollist_.empty())
-               return;
+       string const layoutguiname = getString(combox_);
 
-       ToolbarBackend::item_iterator it = tb.items.begin();
-       ToolbarBackend::item_iterator end = tb.items.end();
-       for (; it != end; ++it)
-               add(it->first, it->second);
+       layoutSelected(owner_, layoutguiname);
 }
 
-
-void XFormsToolbar::add(int action, string const & tooltip)
-{
-       toolbarItem item;
-       item.action = action;
-
-       switch (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);
-               fl_set_object_callback(obj, C_Toolbar_ToolbarCB,
-                                      static_cast<long>(action));
-               // 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 = owner_;
-
-               string const xpm = toolbarbackend.getIcon(action);
-               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