diff --git a/lib/Makefile.am b/lib/Makefile.am index f86b6de39a..2b3ea9a0d9 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -469,6 +469,8 @@ dist_images_DATA1X = \ images/dialog-toggle_findreplaceadv.svgz \ images/dialog-toggle_toc.svgz \ images/down.svgz \ + images/dynamic-char-styles.svgz \ + images/dynamic-custom-insets.svgz \ images/editclear.svgz \ images/emblem-readonly.svgz \ images/emblem-shellescape.svgz \ @@ -1746,6 +1748,8 @@ dist_imagesoxygen_DATA1X = \ images/oxygen/dialog-show_vclog.svgz \ images/oxygen/dialog-toggle_findreplaceadv.svgz \ images/oxygen/dialog-toggle_toc.svgz \ + images/oxygen/dynamic-char-styles.svgz \ + images/oxygen/dynamic-custom-insets.svgz \ images/oxygen/down.svgz \ images/oxygen/editclear.svgz \ images/oxygen/ert-insert.svgz \ diff --git a/lib/images/dynamic-char-styles.svgz b/lib/images/dynamic-char-styles.svgz new file mode 100644 index 0000000000..715b5f3076 Binary files /dev/null and b/lib/images/dynamic-char-styles.svgz differ diff --git a/lib/images/dynamic-custom-insets.svgz b/lib/images/dynamic-custom-insets.svgz new file mode 100644 index 0000000000..715b5f3076 Binary files /dev/null and b/lib/images/dynamic-custom-insets.svgz differ diff --git a/lib/images/oxygen/dynamic-char-styles.svgz b/lib/images/oxygen/dynamic-char-styles.svgz new file mode 100644 index 0000000000..486a3730c4 Binary files /dev/null and b/lib/images/oxygen/dynamic-char-styles.svgz differ diff --git a/lib/images/oxygen/dynamic-custom-insets.svgz b/lib/images/oxygen/dynamic-custom-insets.svgz new file mode 100644 index 0000000000..486a3730c4 Binary files /dev/null and b/lib/images/oxygen/dynamic-custom-insets.svgz differ diff --git a/lib/ui/stdtoolbars.inc b/lib/ui/stdtoolbars.inc index 3cabda3fff..54bea8abc9 100644 --- a/lib/ui/stdtoolbars.inc +++ b/lib/ui/stdtoolbars.inc @@ -96,6 +96,9 @@ ToolbarSet Item "Toggle math toolbar" "toolbar-toggle math" Item "Toggle table toolbar" "toolbar-toggle table" Item "Toggle review toolbar" "toolbar-toggle review" + Separator + DynamicMenu "dynamic-custom-insets" "Custom Insets" + DynamicMenu "dynamic-char-styles" "Character Styles" End Toolbar "view/update" "View/Update" diff --git a/src/frontends/qt4/GuiToolbar.cpp b/src/frontends/qt4/GuiToolbar.cpp index db96e0e526..ce1b2b7e7b 100644 --- a/src/frontends/qt4/GuiToolbar.cpp +++ b/src/frontends/qt4/GuiToolbar.cpp @@ -18,21 +18,28 @@ #include "GuiToolbar.h" #include "Action.h" +#include "Buffer.h" +#include "BufferParams.h" +#include "BufferView.h" +#include "Cursor.h" +#include "FuncRequest.h" +#include "FuncStatus.h" #include "GuiApplication.h" #include "GuiCommandBuffer.h" #include "GuiView.h" #include "IconPalette.h" #include "InsertTableWidget.h" -#include "LayoutBox.h" -#include "qt_helpers.h" -#include "Toolbars.h" - -#include "FuncRequest.h" -#include "FuncStatus.h" #include "KeyMap.h" +#include "LayoutBox.h" #include "LyX.h" #include "LyXRC.h" +#include "qt_helpers.h" #include "Session.h" +#include "Text.h" +#include "TextClass.h" +#include "Toolbars.h" + +#include "insets/InsetText.h" #include "support/debug.h" #include "support/gettext.h" @@ -181,7 +188,7 @@ public: } // namespace -MenuButton::MenuButton(GuiToolbar * bar, ToolbarItem const & item, bool const sticky) +MenuButtonBase::MenuButtonBase(GuiToolbar * bar, ToolbarItem const & item) : QToolButton(bar), bar_(bar), tbitem_(item) { setPopupMode(QToolButton::InstantPopup); @@ -201,6 +208,20 @@ MenuButton::MenuButton(GuiToolbar * bar, ToolbarItem const & item, bool const st break; } } +} + + +void MenuButtonBase::actionTriggered(QAction * action) +{ + QToolButton::setDefaultAction(action); + setPopupMode(QToolButton::DelayedPopup); +} + + +StaticMenuButton::StaticMenuButton( + GuiToolbar * bar, ToolbarItem const & item, bool const sticky) + : MenuButtonBase(bar, item) +{ if (sticky) connect(this, SIGNAL(triggered(QAction *)), this, SLOT(actionTriggered(QAction *))); @@ -210,7 +231,7 @@ MenuButton::MenuButton(GuiToolbar * bar, ToolbarItem const & item, bool const st } -void MenuButton::initialize() +void StaticMenuButton::initialize() { QString const label = qt_(to_ascii(tbitem_.label_)); ButtonMenu * m = new ButtonMenu(label, this); @@ -232,14 +253,7 @@ void MenuButton::initialize() } -void MenuButton::actionTriggered(QAction * action) -{ - QToolButton::setDefaultAction(action); - setPopupMode(QToolButton::DelayedPopup); -} - - -void MenuButton::updateTriggered() +void StaticMenuButton::updateTriggered() { if (!menu()) return; @@ -261,6 +275,126 @@ void MenuButton::updateTriggered() } +class DynamicMenuButton::Private +{ + /// noncopyable + Private(Private const &); + void operator=(Private const &); +public: + Private() : inset_(0) {} + /// + DocumentClassConstPtr text_class_; + /// + InsetText const * inset_; +}; + + +DynamicMenuButton::DynamicMenuButton(GuiToolbar * bar, ToolbarItem const & item) + : MenuButtonBase(bar, item), d(new Private()) +{ + initialize(); +} + + +DynamicMenuButton::~DynamicMenuButton() +{ + delete d; +} + + +void DynamicMenuButton::initialize() +{ + 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())); + connect(bar_, SIGNAL(updated()), this, SLOT(updateTriggered())); + connect(bar_, SIGNAL(iconSizeChanged(QSize)), + this, SLOT(setIconSize(QSize))); + setMenu(m); +} + + +bool DynamicMenuButton::isMenuType(string const & s) +{ + return s == "dynamic-custom-insets" || + s == "dynamic-char-styles"; +} + + +void DynamicMenuButton::updateTriggered() +{ + QMenu * m = menu(); + // the menu should exist by this point + // if not, we can at least avoid crashing in release mode + LASSERT(m, return); + GuiView const & owner = bar_->owner(); + BufferView const * bv = owner.currentBufferView(); + if (!bv) { + m->clear(); + setEnabled(false); + setMinimumWidth(sizeHint().width()); + d->text_class_.reset(); + d->inset_ = 0; + return; + } + + DocumentClassConstPtr text_class = + bv->buffer().params().documentClassPtr(); + InsetText const * inset = &(bv->cursor().innerText()->inset()); + // if the text class has changed, then we need to reload the menu + if (d->text_class_ != text_class) { + d->text_class_ = text_class; + // at the moment, we can just call loadFlexInsets, and it will + // handle both types. if there were more types of menus, then we + // might need to have other options. + loadFlexInsets(); + } + // remember where we are + d->inset_ = inset; + // note that enabling here might need to be more subtle if there + // were other kinds of menus. + setEnabled(!bv->buffer().isReadonly() && + !m->isEmpty() && + inset->insetAllowed(FLEX_CODE)); +} + + +void DynamicMenuButton::loadFlexInsets() +{ + QMenu * m = menu(); + m->clear(); + string const & menutype = tbitem_.name_; + InsetLayout::InsetLyXType ftype; + if (menutype == "dynamic-custom-insets") + ftype = InsetLayout::CUSTOM; + else if (menutype == "dynamic-char-styles") + ftype = InsetLayout::CHARSTYLE; + else { + // this should have been taken care of earlier + LASSERT(false, return); + } + + TextClass::InsetLayouts const & inset_layouts = + d->text_class_->insetLayouts(); + for (auto const & iit : inset_layouts) { + InsetLayout const & il = iit.second; + if (il.lyxtype() != ftype) + continue; + docstring const name = iit.first; + QString const loc_item = toqstr(translateIfPossible( + prefixIs(name, from_ascii("Flex:")) ? + name.substr(5) : name)); + FuncRequest func(LFUN_FLEX_INSERT, + from_ascii("\"") + name + from_ascii("\""), FuncRequest::TOOLBAR); + Action * act = + new Action(func, getIcon(func, false), loc_item, loc_item, this); + m->addAction(act); + } +} + + void GuiToolbar::add(ToolbarItem const & item) { switch (item.type_) { @@ -299,15 +433,22 @@ void GuiToolbar::add(ToolbarItem const & item) case ToolbarItem::ICONPALETTE: addWidget(new PaletteButton(this, item)); break; - case ToolbarItem::POPUPMENU: { - addWidget(new MenuButton(this, item, false)); + addWidget(new StaticMenuButton(this, item, false)); break; } case ToolbarItem::STICKYPOPUPMENU: { - addWidget(new MenuButton(this, item, true)); + addWidget(new StaticMenuButton(this, item, true)); break; } + case ToolbarItem::DYNAMICMENU: { + // we only handle certain things + if (DynamicMenuButton::isMenuType(item.name_)) + addWidget(new DynamicMenuButton(this, item)); + else + LYXERR0("Unknown dynamic menu type: " << item.name_); + break; + } case ToolbarItem::COMMAND: { if (!getStatus(*item.func_).unknown()) addAction(addItem(item)); diff --git a/src/frontends/qt4/GuiToolbar.h b/src/frontends/qt4/GuiToolbar.h index badd59930a..c82134790b 100644 --- a/src/frontends/qt4/GuiToolbar.h +++ b/src/frontends/qt4/GuiToolbar.h @@ -20,6 +20,8 @@ #include #include +#include "support/strfwd.h" + class QSettings; namespace lyx { @@ -38,30 +40,78 @@ class LayoutBox; class ToolbarInfo; class ToolbarItem; -class MenuButton : public QToolButton +class MenuButtonBase : public QToolButton { Q_OBJECT public: /// - MenuButton(GuiToolbar * bar, ToolbarItem const & item, - bool const sticky = false); + MenuButtonBase(GuiToolbar * bar, ToolbarItem const & item); -private: +protected: /// - void initialize(); + virtual void initialize() = 0; /// GuiToolbar * bar_; /// ToolbarItem const & tbitem_; -private Q_SLOTS: +protected Q_SLOTS: /// void actionTriggered(QAction * action); + /// + virtual void updateTriggered() = 0; +}; + + +class StaticMenuButton : public MenuButtonBase +{ + Q_OBJECT +public: + /// + StaticMenuButton(GuiToolbar * bar, ToolbarItem const & item, + bool const sticky = false); + +protected: + /// + void initialize(); + +protected Q_SLOTS: /// void updateTriggered(); }; +/// A menu which can be populated on the fly. +/// The 'type' of menu must be given in the toolbar file +/// (stdtoolbars.inc, usually) and must be one of: +/// dynamic-custom-insets +/// dynamic-char-styles +/// To add a new one of these, you must add a routine, like +/// loadFlexInsets, that will populate the menu, and call it from +/// updateTriggered. Make sure to add the new type to isMenuType(). +class DynamicMenuButton : public MenuButtonBase +{ + Q_OBJECT +public: + /// + DynamicMenuButton(GuiToolbar * bar, ToolbarItem const & item); + /// + ~DynamicMenuButton(); + /// + static bool isMenuType(std::string const & s); +protected: + /// + void initialize(); + /// + void loadFlexInsets(); + /// pimpl so we don't have to include big files + class Private; + Private * d; +protected Q_SLOTS: + /// + void updateTriggered(); +}; + class GuiToolbar : public QToolBar { @@ -107,6 +157,8 @@ public: /// Action * addItem(ToolbarItem const & item); + /// + GuiView const & owner() { return owner_; } Q_SIGNALS: /// diff --git a/src/frontends/qt4/Toolbars.cpp b/src/frontends/qt4/Toolbars.cpp index a2f8a0bec7..bf4617cd5b 100644 --- a/src/frontends/qt4/Toolbars.cpp +++ b/src/frontends/qt4/Toolbars.cpp @@ -74,10 +74,12 @@ ToolbarInfo & ToolbarInfo::read(Lexer & lex) TO_EXPORTFORMATS, TO_IMPORTFORMATS, TO_UPDATEFORMATS, - TO_VIEWFORMATS + TO_VIEWFORMATS, + TO_DYNAMICMENU }; struct LexerKeyword toolTags[] = { + { "dynamicmenu", TO_DYNAMICMENU}, { "end", TO_ENDTOOLBAR }, { "exportformats", TO_EXPORTFORMATS }, { "iconpalette", TO_ICONPALETTE }, @@ -155,6 +157,16 @@ ToolbarInfo & ToolbarInfo::read(Lexer & lex) } break; + case TO_DYNAMICMENU: { + if (lex.next(true)) { + string const name = lex.getString(); + lex.next(true); + docstring const label = lex.getDocString(); + add(ToolbarItem(ToolbarItem::DYNAMICMENU, name, label)); + } + break; + } + case TO_STICKYPOPUPMENU: if (lex.next(true)) { string const pname = lex.getString(); diff --git a/src/frontends/qt4/Toolbars.h b/src/frontends/qt4/Toolbars.h index a65d05ebec..a4acb419cb 100644 --- a/src/frontends/qt4/Toolbars.h +++ b/src/frontends/qt4/Toolbars.h @@ -44,7 +44,9 @@ public: /// a button that expands a menu but remembers the last choice STICKYPOPUPMENU, /// - ICONPALETTE + ICONPALETTE, + /// + DYNAMICMENU }; ToolbarItem(Type type,