X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2FGuiToolbar.cpp;h=f5c68b4c268d3c7824e4ef3bce64f48453f19af8;hb=2280e16db4ce39bec4ec206bfbe684337d8d31f3;hp=10311b6f202c1eee44d086e970facca25e765402;hpb=94e968e72eebe0e26c00d3a7713f8b6e9e26106a;p=lyx.git diff --git a/src/frontends/qt4/GuiToolbar.cpp b/src/frontends/qt4/GuiToolbar.cpp index 10311b6f20..f5c68b4c26 100644 --- a/src/frontends/qt4/GuiToolbar.cpp +++ b/src/frontends/qt4/GuiToolbar.cpp @@ -14,16 +14,6 @@ #include -#include "Buffer.h" -#include "BufferParams.h" -#include "debug.h" -#include "FuncRequest.h" -#include "FuncStatus.h" -#include "gettext.h" -#include "IconPalette.h" -#include "LyXFunc.h" -#include "ToolbarBackend.h" - #include "GuiView.h" #include "GuiCommandBuffer.h" #include "GuiToolbar.h" @@ -32,128 +22,609 @@ #include "qt_helpers.h" #include "InsertTableWidget.h" +#include "Buffer.h" +#include "BufferParams.h" +#include "BufferView.h" +#include "Cursor.h" +#include "FuncRequest.h" +#include "FuncStatus.h" +#include "IconPalette.h" +#include "Layout.h" +#include "LyXFunc.h" +#include "LyXRC.h" +#include "Paragraph.h" +#include "TextClass.h" +#include "ToolbarBackend.h" + +#include "support/debug.h" #include "support/filetools.h" +#include "support/gettext.h" #include "support/lstrings.h" +#include "support/lyxalgo.h" // sorted -#include "controllers/ControlMath.h" - +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include +#include -namespace lyx { +#include + +using namespace std; +using namespace lyx::support; + +static void initializeResources() +{ + static bool initialized = false; + if (!initialized) { + Q_INIT_RESOURCE(Resources); + initialized = true; + } +} -using std::string; -using std::endl; -using support::FileName; -using support::libFileSearch; -using support::subst; +namespace lyx { namespace frontend { -static TextClass const & textClass(LyXView const & lv) +namespace { + +struct PngMap { + char const * key; + char const * value; +}; + + +bool operator<(PngMap const & lhs, PngMap const & rhs) { - return lv.buffer()->params().getTextClass(); + return strcmp(lhs.key, rhs.key) < 0; +} + + +class CompareKey { +public: + CompareKey(string const & name) : name_(name) {} + bool operator()(PngMap const & other) const { return other.key == name_; } +private: + string const name_; +}; + + +PngMap sorted_png_map[] = { + { "Bumpeq", "bumpeq2" }, + { "Cap", "cap2" }, + { "Cup", "cup2" }, + { "Delta", "delta2" }, + { "Downarrow", "downarrow2" }, + { "Gamma", "gamma2" }, + { "Lambda", "lambda2" }, + { "Leftarrow", "leftarrow2" }, + { "Leftrightarrow", "leftrightarrow2" }, + { "Longleftarrow", "longleftarrow2" }, + { "Longleftrightarrow", "longleftrightarrow2" }, + { "Longrightarrow", "longrightarrow2" }, + { "Omega", "omega2" }, + { "Phi", "phi2" }, + { "Pi", "pi2" }, + { "Psi", "psi2" }, + { "Rightarrow", "rightarrow2" }, + { "Sigma", "sigma2" }, + { "Subset", "subset2" }, + { "Supset", "supset2" }, + { "Theta", "theta2" }, + { "Uparrow", "uparrow2" }, + { "Updownarrow", "updownarrow2" }, + { "Upsilon", "upsilon2" }, + { "Vdash", "vdash3" }, + { "Xi", "xi2" }, + { "nLeftarrow", "nleftarrow2" }, + { "nLeftrightarrow", "nleftrightarrow2" }, + { "nRightarrow", "nrightarrow2" }, + { "nVDash", "nvdash3" }, + { "nvDash", "nvdash2" }, + { "textrm \\AA", "textrm_AA"}, + { "textrm \\O", "textrm_O"}, + { "vDash", "vdash2" } +}; + +size_t const nr_sorted_png_map = sizeof(sorted_png_map) / sizeof(PngMap); + + +string const find_png(string const & name) +{ + PngMap const * const begin = sorted_png_map; + PngMap const * const end = begin + nr_sorted_png_map; + BOOST_ASSERT(sorted(begin, end)); + + PngMap const * const it = find_if(begin, end, CompareKey(name)); + + string png_name; + if (it != end) + png_name = it->value; + else { + png_name = subst(name, "_", "underscore"); + png_name = subst(png_name, ' ', '_'); + + // This way we can have "math-delim { }" on the toolbar. + png_name = subst(png_name, "(", "lparen"); + png_name = subst(png_name, ")", "rparen"); + png_name = subst(png_name, "[", "lbracket"); + png_name = subst(png_name, "]", "rbracket"); + png_name = subst(png_name, "{", "lbrace"); + png_name = subst(png_name, "}", "rbrace"); + png_name = subst(png_name, "|", "bars"); + png_name = subst(png_name, ",", "thinspace"); + png_name = subst(png_name, ":", "mediumspace"); + png_name = subst(png_name, ";", "thickspace"); + png_name = subst(png_name, "!", "negthinspace"); + } + + LYXERR(Debug::GUI, "find_png(" << name << ")\n" + << "Looking for math PNG called \"" << png_name << '"'); + return png_name; } +} // namespace anon -GuiLayoutBox::GuiLayoutBox(QToolBar * toolbar, GuiViewBase & owner) - : owner_(owner) + +/// return a icon for the given action +static QIcon getIcon(FuncRequest const & f, bool unknown) { - combo_ = new QComboBox; - combo_->setSizeAdjustPolicy(QComboBox::AdjustToContents); - combo_->setFocusPolicy(Qt::ClickFocus); - combo_->setMinimumWidth(combo_->sizeHint().width()); - combo_->setMaxVisibleItems(100); + initializeResources(); + QPixmap pm; + string name1; + string name2; + string path; + string fullname; + + switch (f.action) { + case LFUN_MATH_INSERT: + if (!f.argument().empty()) { + path = "math/"; + name1 = find_png(to_utf8(f.argument()).substr(1)); + } + break; + case LFUN_MATH_DELIM: + case LFUN_MATH_BIGDELIM: + path = "math/"; + name1 = find_png(to_utf8(f.argument())); + break; + case LFUN_CALL: + path = "commands/"; + name1 = to_utf8(f.argument()); + break; + default: + name2 = lyxaction.getActionName(f.action); + name1 = name2; + + if (!f.argument().empty()) + name1 = subst(name2 + ' ' + to_utf8(f.argument()), ' ', '_'); + } + + fullname = libFileSearch("images/" + path, name1, "png").absFilename(); + if (pm.load(toqstr(fullname))) + return pm; + + fullname = libFileSearch("images/" + path, name2, "png").absFilename(); + if (pm.load(toqstr(fullname))) + return pm; + + if (pm.load(":/images/" + toqstr(path + name1) + ".png")) + return pm; - QObject::connect(combo_, SIGNAL(activated(const QString &)), - this, SLOT(selected(const QString &))); + if (pm.load(":/images/" + toqstr(path + name2) + ".png")) + return pm; + + LYXERR(Debug::GUI, "Cannot find icon for command \"" + << lyxaction.getActionName(f.action) + << '(' << to_utf8(f.argument()) << ")\""); + if (unknown) + pm.load(":/images/unknown.png"); - toolbar->addWidget(combo_); + return pm; } -void GuiLayoutBox::set(docstring const & layout) +///////////////////////////////////////////////////////////////////// +// +// GuiLayoutBox +// +///////////////////////////////////////////////////////////////////// + +class FilterItemDelegate : public QAbstractItemDelegate { +public: + /// + explicit FilterItemDelegate(QObject * parent = 0) + : QAbstractItemDelegate(parent) {} + + /// + void paint(QPainter * painter, const QStyleOptionViewItem & option, + const QModelIndex &index) const { + QComboBox * combo = static_cast(parent()); + + // Draw using the menu item style (this is how QComboBox does it). + // But for the rich text drawing below we will call it with an + // empty string, and later then draw over it the real string. + painter->save(); + QStyleOptionMenuItem opt = getStyleOption(option, index); + QString text = underlineFilter(opt.text); + opt.text = QString(); + painter->eraseRect(option.rect); + combo->style()->drawControl(QStyle::CE_MenuItem, &opt, painter, combo->view()); + painter->restore(); + + // Draw the rich text. + painter->save(); + QColor col = opt.palette.text().color(); + if (opt.state & QStyle::State_Selected) + col = opt.palette.highlightedText().color(); + QAbstractTextDocumentLayout::PaintContext context; + context.palette.setColor(QPalette::Text, col); + + QTextDocument doc; + doc.setDefaultFont(opt.font); + doc.setHtml(text); + painter->translate(opt.rect.x() + 20, opt.rect.y()); + doc.documentLayout()->draw(painter, context); + painter->restore(); + } + + /// + QSize sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const { + QComboBox * combo = static_cast(parent()); + + QStyleOptionMenuItem opt = getStyleOption(option, index); + return combo->style()->sizeFromContents( + QStyle::CT_MenuItem, &opt, option.rect.size(), combo); + } + +private: + /// + QString underlineFilter(QString const & s) const + { + // get filter + GuiLayoutBox * p = static_cast(parent()); + QString const & f = p->filter(); + if (f.isEmpty()) + return s; + + // step through data item and put "(x)" for every matching character + QString r; + int lastp = -1; + p->filter(); + for (int i = 0; i < f.length(); ++i) { + int p = s.indexOf(f[i], lastp + 1, Qt::CaseInsensitive); + BOOST_ASSERT(p != -1); + if (lastp == p - 1 && lastp != -1) { + // remove ")" and append "x)" + r = r.left(r.length() - 4) + s[p] + ""; + } else { + // append "(x)" + r += s.mid(lastp + 1, p - lastp - 1); + r += QString("") + s[p] + ""; + } + lastp = p; + } + r += s.mid(lastp + 1); + return r; + } + + /// + QStyleOptionMenuItem getStyleOption(const QStyleOptionViewItem &option, + const QModelIndex &index) const + { + QComboBox * combo = static_cast(parent()); + + // create the options for a menu item + QStyleOptionMenuItem menuOption; + menuOption.palette = QApplication::palette("QMenu"); + menuOption.state = QStyle::State_Active | QStyle::State_Enabled; + if (option.state & QStyle::State_Selected) + menuOption.state |= QStyle::State_Selected; + menuOption.checkType = QStyleOptionMenuItem::NonExclusive; + menuOption.checked = combo->currentIndex() == index.row(); + menuOption.menuItemType = QStyleOptionMenuItem::Normal; + menuOption.text = index.model()->data(index, Qt::DisplayRole).toString() + .replace(QLatin1Char('&'), QLatin1String("&&")); + menuOption.tabWidth = 0; + menuOption.menuRect = option.rect; + menuOption.rect = option.rect; + menuOption.font = combo->font(); + menuOption.fontMetrics = QFontMetrics(menuOption.font); + return menuOption; + } +}; + + +class GuiFilterProxyModel : public QSortFilterProxyModel +{ +public: + /// + GuiFilterProxyModel(QObject * parent) + : QSortFilterProxyModel(parent) {} + + /// + void setCharFilter(QString const & f) + { + setFilterRegExp(charFilterRegExp(f)); + dataChanged(index(0, 0), index(rowCount() - 1, 1)); + } + +private: + /// + QString charFilterRegExp(QString const & filter) + { + QString re; + for (int i = 0; i < filter.length(); ++i) + re += ".*" + QRegExp::escape(filter[i]); + return re; + } +}; + + +GuiLayoutBox::GuiLayoutBox(GuiView & owner) + : owner_(owner), filterItemDelegate_(new FilterItemDelegate(this)) +{ + setSizeAdjustPolicy(QComboBox::AdjustToContents); + setFocusPolicy(Qt::ClickFocus); + setMinimumWidth(sizeHint().width()); + setMaxVisibleItems(100); + + // set the layout model with two columns + // 1st: translated layout names + // 2nd: raw layout names + model_ = new QStandardItemModel(0, 2, this); + filterModel_ = new GuiFilterProxyModel(this); + filterModel_->setSourceModel(model_); + filterModel_->setDynamicSortFilter(true); + filterModel_->setFilterCaseSensitivity(Qt::CaseInsensitive); + setModel(filterModel_); + + // for the filtering we have to intercept characters + view()->installEventFilter(this); + view()->setItemDelegateForColumn(0, filterItemDelegate_); + + QObject::connect(this, SIGNAL(activated(int)), + this, SLOT(selected(int))); + owner_.setLayoutDialog(this); + updateContents(true); +} + + +void GuiLayoutBox::setFilter(QString const & s) +{ + // remember old selection + int sel = currentIndex(); + if (sel != -1) + lastSel_ = filterModel_->mapToSource(filterModel_->index(sel, 0)).row(); + + filter_ = s; + filterModel_->setCharFilter(s); + + // restore old selection + if (lastSel_ != -1) { + QModelIndex i = filterModel_->mapFromSource(model_->index(lastSel_, 0)); + if (i.isValid()) + setCurrentIndex(i.row()); + } +} + + +void GuiLayoutBox::resetFilter() { - TextClass const & tc = textClass(owner_); + setFilter(QString()); +} - QString const & name = toqstr(translateIfPossible(tc[layout]->name())); - int i = 0; - for (; i < combo_->count(); ++i) { - if (name == combo_->itemText(i)) +bool GuiLayoutBox::eventFilter(QObject * o, QEvent * e) +{ + if (e->type() != QEvent::KeyPress) + return QComboBox::eventFilter(o, e); + + QKeyEvent * ke = static_cast(e); + bool modified = (ke->modifiers() == Qt::ControlModifier) + || (ke->modifiers() == Qt::AltModifier) + || (ke->modifiers() == Qt::MetaModifier); + + switch (ke->key()) { + case Qt::Key_Escape: + if (!modified && !filter_.isEmpty()) { + resetFilter(); + return true; + } + break; + case Qt::Key_Backspace: + if (!modified) { + // cut off one character + setFilter(filter_.left(filter_.length() - 1)); + } + break; + default: + if (modified || ke->text().isEmpty()) break; + // find chars for the filter string + QString s; + for (int i = 0; i < ke->text().length(); ++i) { + QChar c = ke->text()[i]; + if (c.isLetterOrNumber() + || c.isSymbol() + || c.isPunct() + || c.category() == QChar::Separator_Space) { + s += c; + } + } + if (!s.isEmpty()) { + // append new chars to the filter string + setFilter(filter_ + s); + return true; + } + break; } - if (i == combo_->count()) { + return QComboBox::eventFilter(o, e); +} + + +void GuiLayoutBox::set(docstring const & layout) +{ + resetFilter(); + + if (!text_class_) + return; + + QString const & name = toqstr((*text_class_)[layout]->name()); + if (name == currentText()) + return; + + QList r = model_->findItems(name, Qt::MatchExactly, 1); + if (r.empty()) { lyxerr << "Trying to select non existent layout type " << fromqstr(name) << endl; return; } - combo_->setCurrentIndex(i); + setCurrentIndex(filterModel_->mapFromSource(r.first()->index()).row()); } -void GuiLayoutBox::update() +void GuiLayoutBox::addItemSort(docstring const & item, bool sorted) { - TextClass const & tc = textClass(owner_); - - combo_->setUpdatesEnabled(false); + QString qitem = toqstr(item); + QString titem = toqstr(translateIfPossible(item)); - combo_->clear(); + QList row; + row.append(new QStandardItem(titem)); + row.append(new QStandardItem(qitem)); - TextClass::const_iterator it = tc.begin(); - TextClass::const_iterator const end = tc.end(); - for (; it != end; ++it) { - // ignore obsolete entries - if ((*it)->obsoleted_by().empty()) - combo_->addItem(toqstr(translateIfPossible((*it)->name()))); + // the simple unsorted case + int const end = model_->rowCount(); + if (!sorted || end < 2 || qitem[0].category() != QChar::Letter_Uppercase) { + model_->appendRow(row); + return; } - // needed to recalculate size hint - combo_->hide(); - combo_->setMinimumWidth(combo_->sizeHint().width()); - combo_->show(); + // find row to insert the item + int i = 1; // skip the Standard layout + QString is = model_->item(i, 0)->text(); + while (is.compare(titem) < 0) { + // e.g. --Separator-- + if (is[0].category() != QChar::Letter_Uppercase) + break; + ++i; + if (i == end) + break; + is = model_->item(i, 0)->text(); + } - combo_->setUpdatesEnabled(true); - combo_->update(); + model_->insertRow(i, row); } -void GuiLayoutBox::clear() +void GuiLayoutBox::updateContents(bool reset) { - combo_->clear(); -} + resetFilter(); + + Buffer const * buffer = owner_.buffer(); + if (!buffer) { + model_->clear(); + setEnabled(false); + text_class_ = 0; + inset_ = 0; + return; + } + // we'll only update the layout list if the text class has changed + // or we've moved from one inset to another + DocumentClass const * text_class = &buffer->params().documentClass(); + Inset const * inset = + owner_.view()->cursor().innerParagraph().inInset(); + if (!reset && text_class_ == text_class && inset_ == inset) { + set(owner_.view()->cursor().innerParagraph().layout()->name()); + return; + } -void GuiLayoutBox::open() -{ - combo_->showPopup(); -} + inset_ = inset; + text_class_ = text_class; + + model_->clear(); + for (size_t i = 0; i != text_class_->layoutCount(); ++i) { + Layout const & lt = *text_class_->layout(i); + docstring const & name = lt.name(); + // if this inset requires the empty layout, we skip the default + // layout + if (name == text_class_->defaultLayoutName() && inset && + (inset->forceEmptyLayout() || inset->useEmptyLayout())) + continue; + // if it doesn't require the empty layout, we skip it + if (name == text_class_->emptyLayoutName() && inset && + !inset->forceEmptyLayout() && !inset->useEmptyLayout()) + continue; + addItemSort(name, lyxrc.sort_layouts); + } + set(owner_.view()->cursor().innerParagraph().layout()->name()); -void GuiLayoutBox::setEnabled(bool enable) -{ - // Workaround for Qt bug where setEnabled(true) closes - // the popup - if (enable != combo_->isEnabled()) - combo_->setEnabled(enable); + // needed to recalculate size hint + hide(); + setMinimumWidth(sizeHint().width()); + setEnabled(!buffer->isReadonly()); + show(); } -void GuiLayoutBox::selected(const QString & str) +void GuiLayoutBox::selected(int index) { + // get selection + QModelIndex mindex = filterModel_->mapToSource(filterModel_->index(index, 1)); + docstring const name = qstring_to_ucs4(model_->itemFromIndex(mindex)->text()); + owner_.setFocus(); - layoutSelected(owner_, qstring_to_ucs4(str)); + if (!text_class_) { + updateContents(false); + resetFilter(); + return; + } + + // find corresponding text class + for (size_t i = 0; i != text_class_->layoutCount(); ++i) { + docstring const & itname = text_class_->layout(i)->name(); + if (itname == name) { + FuncRequest const func(LFUN_LAYOUT, itname, + FuncRequest::TOOLBAR); + theLyXFunc().setLyXView(&owner_); + lyx::dispatch(func); + updateContents(false); + resetFilter(); + return; + } + } + lyxerr << "ERROR (layoutSelected): layout not found!" << endl; } -GuiToolbar::GuiToolbar(ToolbarInfo const & tbinfo, GuiViewBase & owner) - : QToolBar(qt_(tbinfo.gui_name), &owner), command_buffer_(0), owner_(owner) + +///////////////////////////////////////////////////////////////////// +// +// GuiToolbar +// +///////////////////////////////////////////////////////////////////// + + +GuiToolbar::GuiToolbar(ToolbarInfo const & tbinfo, GuiView & owner) + : QToolBar(qt_(tbinfo.gui_name), &owner), owner_(owner), + layout_(0), command_buffer_(0) { // give visual separation between adjacent toolbars addSeparator(); @@ -168,10 +639,122 @@ GuiToolbar::GuiToolbar(ToolbarInfo const & tbinfo, GuiViewBase & owner) } -void GuiToolbar::focusCommandBuffer() +Action * GuiToolbar::addItem(ToolbarItem const & item) +{ + Action * act = new Action(owner_, + getIcon(item.func_, false), + toqstr(item.label_), item.func_, toqstr(item.label_)); + actions_.append(act); + return act; +} + +namespace { + +class PaletteButton : public QToolButton +{ +private: + GuiToolbar * bar_; + ToolbarItem const & tbitem_; + bool initialized_; +public: + PaletteButton(GuiToolbar * bar, ToolbarItem const & item) + : QToolButton(bar), bar_(bar), tbitem_(item), initialized_(false) + { + QString const label = qt_(to_ascii(tbitem_.label_)); + setToolTip(label); + setStatusTip(label); + setText(label); + connect(bar_, SIGNAL(iconSizeChanged(QSize)), + this, SLOT(setIconSize(QSize))); + setCheckable(true); + ToolbarInfo const * tbinfo = + toolbarbackend.getDefinedToolbarInfo(tbitem_.name_); + if (tbinfo) + // use the icon of first action for the toolbar button + setIcon(getIcon(tbinfo->items.begin()->func_, true)); + } + + void mousePressEvent(QMouseEvent * e) + { + if (initialized_) { + QToolButton::mousePressEvent(e); + return; + } + + initialized_ = true; + + ToolbarInfo const * tbinfo = + toolbarbackend.getDefinedToolbarInfo(tbitem_.name_); + if (!tbinfo) { + lyxerr << "Unknown toolbar " << tbitem_.name_ << endl; + return; + } + IconPalette * panel = new IconPalette(this); + QString const label = qt_(to_ascii(tbitem_.label_)); + panel->setWindowTitle(label); + connect(this, SIGNAL(clicked(bool)), panel, SLOT(setVisible(bool))); + connect(panel, SIGNAL(visible(bool)), this, SLOT(setChecked(bool))); + ToolbarInfo::item_iterator it = tbinfo->items.begin(); + ToolbarInfo::item_iterator const end = tbinfo->items.end(); + for (; it != end; ++it) + if (!getStatus(it->func_).unknown()) + panel->addButton(bar_->addItem(*it)); + + QToolButton::mousePressEvent(e); + } +}; + +class MenuButton : public QToolButton { - if (command_buffer_) - command_buffer_->setFocus(); +private: + GuiToolbar * bar_; + ToolbarItem const & tbitem_; + bool initialized_; +public: + MenuButton(GuiToolbar * bar, ToolbarItem const & item) + : QToolButton(bar), bar_(bar), tbitem_(item), initialized_(false) + { + setPopupMode(QToolButton::InstantPopup); + QString const label = qt_(to_ascii(tbitem_.label_)); + setToolTip(label); + setStatusTip(label); + setText(label); + setIcon(QPixmap(":images/math/" + toqstr(tbitem_.name_) + ".png")); + connect(bar, SIGNAL(iconSizeChanged(QSize)), + this, SLOT(setIconSize(QSize))); + } + + void mousePressEvent(QMouseEvent * e) + { + if (initialized_) { + QToolButton::mousePressEvent(e); + return; + } + + initialized_ = true; + + QString const label = qt_(to_ascii(tbitem_.label_)); + ButtonMenu * m = new ButtonMenu(label, this); + m->setWindowTitle(label); + m->setTearOffEnabled(true); + connect(bar_, SIGNAL(updated()), m, SLOT(updateParent())); + ToolbarInfo const * tbinfo = + toolbarbackend.getDefinedToolbarInfo(tbitem_.name_); + if (!tbinfo) { + lyxerr << "Unknown toolbar " << tbitem_.name_ << endl; + return; + } + ToolbarInfo::item_iterator it = tbinfo->items.begin(); + ToolbarInfo::item_iterator const end = tbinfo->items.end(); + for (; it != end; ++it) + if (!getStatus(it->func_).unknown()) + m->add(bar_->addItem(*it)); + setMenu(m); + + QToolButton::mousePressEvent(e); + } +}; + } @@ -182,7 +765,8 @@ void GuiToolbar::add(ToolbarItem const & item) addSeparator(); break; case ToolbarItem::LAYOUTS: - layout_.reset(new GuiLayoutBox(this, owner_)); + layout_ = new GuiLayoutBox(owner_); + addWidget(layout_); break; case ToolbarItem::MINIBUFFER: command_buffer_ = new GuiCommandBuffer(&owner_); @@ -193,10 +777,11 @@ void GuiToolbar::add(ToolbarItem const & item) case ToolbarItem::TABLEINSERT: { QToolButton * tb = new QToolButton; tb->setCheckable(true); - tb->setIcon(QPixmap(toqstr(getIcon(FuncRequest(LFUN_TABULAR_INSERT))))); - tb->setToolTip(qt_(to_ascii(item.label_))); - tb->setStatusTip(qt_(to_ascii(item.label_))); - tb->setText(qt_(to_ascii(item.label_))); + tb->setIcon(getIcon(FuncRequest(LFUN_TABULAR_INSERT), true)); + QString const label = qt_(to_ascii(item.label_)); + tb->setToolTip(label); + tb->setStatusTip(label); + tb->setText(label); InsertTableWidget * iv = new InsertTableWidget(owner_, tb); connect(tb, SIGNAL(clicked(bool)), iv, SLOT(show(bool))); connect(iv, SIGNAL(visible(bool)), tb, SLOT(setChecked(bool))); @@ -204,89 +789,17 @@ void GuiToolbar::add(ToolbarItem const & item) addWidget(tb); break; } - case ToolbarItem::ICONPALETTE: { - QToolButton * tb = new QToolButton(this); - tb->setToolTip(qt_(to_ascii(item.label_))); - tb->setStatusTip(qt_(to_ascii(item.label_))); - tb->setText(qt_(to_ascii(item.label_))); - connect(this, SIGNAL(iconSizeChanged(const QSize &)), - tb, SLOT(setIconSize(const QSize &))); - IconPalette * panel = new IconPalette(tb); - panel->setWindowTitle(qt_(to_ascii(item.label_))); - connect(this, SIGNAL(updated()), panel, SLOT(updateParent())); - ToolbarInfo const * tbinfo = toolbarbackend.getDefinedToolbarInfo(item.name_); - if (!tbinfo) { - lyxerr << "Unknown toolbar " << item.name_ << endl; - break; - } - ToolbarInfo::item_iterator it = tbinfo->items.begin(); - ToolbarInfo::item_iterator const end = tbinfo->items.end(); - for (; it != end; ++it) - if (!getStatus(it->func_).unknown()) { - Action * action = new Action(owner_, - getIcon(it->func_), - it->label_, - it->func_, - it->label_); - panel->addButton(action); - actions_.push_back(action); - // use the icon of first action for the toolbar button - if (it == tbinfo->items.begin()) - tb->setIcon(QPixmap(getIcon(it->func_).c_str())); - } - tb->setCheckable(true); - connect(tb, SIGNAL(clicked(bool)), panel, SLOT(setVisible(bool))); - connect(panel, SIGNAL(visible(bool)), tb, SLOT(setChecked(bool))); - addWidget(tb); + case ToolbarItem::ICONPALETTE: + addWidget(new PaletteButton(this, item)); break; - } + case ToolbarItem::POPUPMENU: { - QToolButton * tb = new QToolButton; - tb->setPopupMode(QToolButton::InstantPopup); - tb->setToolTip(qt_(to_ascii(item.label_))); - tb->setStatusTip(qt_(to_ascii(item.label_))); - tb->setText(qt_(to_ascii(item.label_))); - FileName icon_path = libFileSearch("images/math", item.name_, "png"); - tb->setIcon(QIcon(toqstr(icon_path.absFilename()))); - connect(this, SIGNAL(iconSizeChanged(const QSize &)), - tb, SLOT(setIconSize(const QSize &))); - - ButtonMenu * m = new ButtonMenu(qt_(to_ascii(item.label_)), tb); - m->setWindowTitle(qt_(to_ascii(item.label_))); - m->setTearOffEnabled(true); - connect(this, SIGNAL(updated()), m, SLOT(updateParent())); - ToolbarInfo const * tbinfo = toolbarbackend.getDefinedToolbarInfo(item.name_); - if (!tbinfo) { - lyxerr << "Unknown toolbar " << item.name_ << endl; - break; - } - ToolbarInfo::item_iterator it = tbinfo->items.begin(); - ToolbarInfo::item_iterator const end = tbinfo->items.end(); - for (; it != end; ++it) - if (!getStatus(it->func_).unknown()) { - Action * action = new Action(owner_, - getIcon(it->func_, false), - it->label_, - it->func_, - it->label_); - m->add(action); - actions_.push_back(action); - } - tb->setMenu(m); - addWidget(tb); + addWidget(new MenuButton(this, item)); break; } case ToolbarItem::COMMAND: { - if (getStatus(item.func_).unknown()) - break; - - Action * action = new Action(owner_, - getIcon(item.func_), - item.label_, - item.func_, - item.label_); - addAction(action); - actions_.push_back(action); + if (!getStatus(item.func_).unknown()) + addAction(addItem(item)); break; } default: @@ -295,24 +808,6 @@ void GuiToolbar::add(ToolbarItem const & item) } -void GuiToolbar::hide(bool) -{ - QToolBar::hide(); -} - - -void GuiToolbar::show(bool) -{ - QToolBar::show(); -} - - -bool GuiToolbar::isVisible() const -{ - return QToolBar::isVisible(); -} - - void GuiToolbar::saveInfo(ToolbarSection::ToolbarInfo & tbinfo) { // if tbinfo.state == auto *do not* set on/off @@ -344,64 +839,24 @@ void GuiToolbar::saveInfo(ToolbarSection::ToolbarInfo & tbinfo) } -void GuiToolbar::update() +void GuiToolbar::updateContents() { + // update visible toolbars only + if (!isVisible()) + return; // This is a speed bottleneck because this is called on every keypress // and update calls getStatus, which copies the cursor at least two times - for (size_t i = 0; i < actions_.size(); ++i) + for (int i = 0; i < actions_.size(); ++i) actions_[i]->update(); + if (layout_) + layout_->setEnabled(lyx::getStatus(FuncRequest(LFUN_LAYOUT)).enabled()); + // emit signal updated(); } -string const getIcon(FuncRequest const & f, bool unknown) -{ - using frontend::find_png; - - string fullname; - - switch (f.action) { - case LFUN_MATH_INSERT: - if (!f.argument().empty()) - fullname = find_png(to_utf8(f.argument()).substr(1)); - break; - case LFUN_MATH_DELIM: - case LFUN_MATH_BIGDELIM: - fullname = find_png(to_utf8(f.argument())); - break; - default: - string const name = lyxaction.getActionName(f.action); - string png_name = name; - - if (!f.argument().empty()) - png_name = subst(name + ' ' + to_utf8(f.argument()), ' ', '_'); - - fullname = libFileSearch("images", png_name, "png").absFilename(); - - if (fullname.empty()) { - // try without the argument - fullname = libFileSearch("images", name, "png").absFilename(); - } - } - - if (!fullname.empty()) { - LYXERR(Debug::GUI) << "Full icon name is `" - << fullname << '\'' << endl; - return fullname; - } - - LYXERR(Debug::GUI) << "Cannot find icon for command \"" - << lyxaction.getActionName(f.action) - << '(' << to_utf8(f.argument()) << ")\"" << endl; - if (unknown) - return libFileSearch("images", "unknown", "png").absFilename(); - else - return string(); -} - - } // namespace frontend } // namespace lyx