commit 154b4fdecec9ba8e5f5c1d1210b07992e68e7d15
Author: Juergen Spitzmueller <[email protected]>
Date:   Wed Dec 26 15:46:14 2018 +0100

    Give textstyle-apply a history
    
    The last 20 applications are saved now and accessible both via the lfun
    (textstyle-apply n) and the toolbar (via button menu)
    
    Fixes: #7133
    
    This also changes the default icon and toolbar position of the action,
    as requested in #11427
---
 lib/Makefile.am                          |    3 +
 lib/images/classic/dynamic-freefonts.png |  Bin 0 -> 247 bytes
 lib/images/dynamic-freefonts.svgz        |  Bin 0 -> 4001 bytes
 lib/images/oxygen/dynamic-freefonts.svgz |  Bin 0 -> 51218 bytes
 lib/images/textstyle-apply.svgz          |  Bin 2358 -> 4001 bytes
 lib/ui/stdtoolbars.inc                   |    2 +-
 src/Font.cpp                             |   41 +++++++++---------
 src/Font.h                               |    2 +-
 src/LyXAction.cpp                        |    6 ++-
 src/Text.h                               |    2 +
 src/Text3.cpp                            |   58 ++++++++++++++++++++-----
 src/frontends/qt4/GuiFontLoader.cpp      |    3 +-
 src/frontends/qt4/GuiToolbar.cpp         |   68 +++++++++++++++++++++---------
 src/frontends/qt4/GuiToolbar.h           |    1 +
 14 files changed, 130 insertions(+), 56 deletions(-)

diff --git a/lib/Makefile.am b/lib/Makefile.am
index 3816d5c..032103e 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -473,6 +473,7 @@ dist_images_DATA1X = \
        images/down.svgz \
        images/dynamic-char-styles.svgz \
        images/dynamic-custom-insets.svgz \
+       images/dynamic-freefonts.svgz \
        images/editclear.svgz \
        images/emblem-readonly.svgz \
        images/emblem-shellescape.svgz \
@@ -1759,6 +1760,7 @@ dist_imagesoxygen_DATA1X = \
        images/oxygen/dialog-toggle_toc.svgz \
        images/oxygen/dynamic-char-styles.svgz \
        images/oxygen/dynamic-custom-insets.svgz \
+       images/oxygen/dynamic-freefonts.svgz \
        images/oxygen/down.svgz \
        images/oxygen/editclear.svgz \
        images/oxygen/ert-insert.svgz \
@@ -1965,6 +1967,7 @@ dist_imagesclassic_DATA = \
        images/classic/dialog-show_vclog.png  \
        images/classic/dialog-toggle_findreplaceadv.png  \
        images/classic/dialog-toggle_toc.png  \
+       images/classic/dynamic-freefonts.png \
        images/classic/down.png  \
        images/classic/ert-insert.png  \
        images/classic/file-open.png  \
diff --git a/lib/images/classic/dynamic-freefonts.png 
b/lib/images/classic/dynamic-freefonts.png
new file mode 100644
index 0000000..a63ad1d
Binary files /dev/null and b/lib/images/classic/dynamic-freefonts.png differ
diff --git a/lib/images/dynamic-freefonts.svgz 
b/lib/images/dynamic-freefonts.svgz
new file mode 100644
index 0000000..5830eef
Binary files /dev/null and b/lib/images/dynamic-freefonts.svgz differ
diff --git a/lib/images/oxygen/dynamic-freefonts.svgz 
b/lib/images/oxygen/dynamic-freefonts.svgz
new file mode 100644
index 0000000..7a7ffd0
Binary files /dev/null and b/lib/images/oxygen/dynamic-freefonts.svgz differ
diff --git a/lib/images/textstyle-apply.svgz b/lib/images/textstyle-apply.svgz
index 1a037d4..5830eef 100644
Binary files a/lib/images/textstyle-apply.svgz and 
b/lib/images/textstyle-apply.svgz differ
diff --git a/lib/ui/stdtoolbars.inc b/lib/ui/stdtoolbars.inc
index e0666f0..412d564 100644
--- a/lib/ui/stdtoolbars.inc
+++ b/lib/ui/stdtoolbars.inc
@@ -87,7 +87,6 @@ ToolbarSet
                Item "Toggle emphasis" "font-emph"
                Item "Toggle noun" "font-noun"
                DynamicMenu "dynamic-char-styles" "Custom text styles"
-               Item "Apply last text properties" "textstyle-apply"
                Separator
                Item "Insert math" "math-mode on"
                Item "Insert graphics" "dialog-show-new-inset graphics"
@@ -139,6 +138,7 @@ ToolbarSet
                Item "Include file" "dialog-show-new-inset include"
                Separator
                Item "Text properties" "dialog-show character"
+               DynamicMenu "dynamic-freefonts" "Apply recent text properties"
                Item "Paragraph settings" "layout-paragraph"
                Item "Thesaurus" "thesaurus-entry"
        End
diff --git a/src/Font.cpp b/src/Font.cpp
index ea31f40..bb0b548 100644
--- a/src/Font.cpp
+++ b/src/Font.cpp
@@ -143,41 +143,41 @@ void Font::update(Font const & newfont,
 }
 
 
-docstring const stateText(FontInfo const & f)
+docstring const stateText(FontInfo const & f, bool const terse)
 {
        odocstringstream os;
-       if (f.family() != INHERIT_FAMILY)
+       if (f.family() != INHERIT_FAMILY && (!terse || f.family() != 
IGNORE_FAMILY))
                os << _(GUIFamilyNames[f.family()]) << ", ";
-       if (f.series() != INHERIT_SERIES)
+       if (f.series() != INHERIT_SERIES && (!terse || f.series() != 
IGNORE_SERIES))
                os << _(GUISeriesNames[f.series()]) << ", ";
-       if (f.shape() != INHERIT_SHAPE)
+       if (f.shape() != INHERIT_SHAPE && (!terse || f.shape() != IGNORE_SHAPE))
                os << _(GUIShapeNames[f.shape()]) << ", ";
-       if (f.size() != FONT_SIZE_INHERIT)
+       if (f.size() != FONT_SIZE_INHERIT && (!terse || f.size() != 
FONT_SIZE_IGNORE))
                os << _(GUISizeNames[f.size()]) << ", ";
-       if (f.color() != Color_inherit)
+       if (f.color() != Color_inherit && (!terse || f.color() != Color_ignore))
                os << lcolor.getGUIName(f.color()) << ", ";
        // FIXME: uncomment this when we support background.
        //if (f.background() != Color_inherit)
        //      os << lcolor.getGUIName(f.background()) << ", ";
-       if (f.emph() != FONT_INHERIT)
+       if (f.emph() != FONT_INHERIT && (!terse || f.emph() != FONT_IGNORE))
                os << bformat(_("Emphasis %1$s, "),
                              _(GUIMiscNames[f.emph()]));
-       if (f.underbar() != FONT_INHERIT)
+       if (f.underbar() != FONT_INHERIT && (!terse || f.underbar() == FONT_ON))
                os << bformat(_("Underline %1$s, "),
                              _(GUIMiscNames[f.underbar()]));
-       if (f.strikeout() != FONT_INHERIT)
-               os << bformat(_("Strike out %1$s, "),
-                             _(GUIMiscNames[f.strikeout()]));
-       if (f.xout() != FONT_INHERIT)
-               os << bformat(_("Cross out %1$s, "),
-                             _(GUIMiscNames[f.xout()]));
-       if (f.uuline() != FONT_INHERIT)
+       if (f.uuline() != FONT_INHERIT && (!terse || f.uuline() == FONT_ON))
                os << bformat(_("Double underline %1$s, "),
                              _(GUIMiscNames[f.uuline()]));
-       if (f.uwave() != FONT_INHERIT)
+       if (f.uwave() != FONT_INHERIT && (!terse || f.uwave() == FONT_ON))
                os << bformat(_("Wavy underline %1$s, "),
                              _(GUIMiscNames[f.uwave()]));
-       if (f.noun() != FONT_INHERIT)
+       if (f.strikeout() != FONT_INHERIT && (!terse || f.strikeout() == 
FONT_ON))
+               os << bformat(_("Strike out %1$s, "),
+                             _(GUIMiscNames[f.strikeout()]));
+       if (f.xout() != FONT_INHERIT && (!terse || f.strikeout() == FONT_ON))
+               os << bformat(_("Cross out %1$s, "),
+                             _(GUIMiscNames[f.xout()]));
+       if (f.noun() != FONT_INHERIT && (!terse || f.noun() != FONT_IGNORE))
                os << bformat(_("Noun %1$s, "),
                              _(GUIMiscNames[f.noun()]));
        if (f == inherit_font)
@@ -187,11 +187,12 @@ docstring const stateText(FontInfo const & f)
 }
 
 
-docstring const Font::stateText(BufferParams * params) const
+docstring const Font::stateText(BufferParams * params, bool const terse) const
 {
        odocstringstream os;
-       os << lyx::stateText(bits_);
-       if (!params || (language() != params->language)) {
+       os << lyx::stateText(bits_, terse);
+       if ((!params || (language() != params->language))
+           && (!terse || language() != ignore_language)) {
                // reset_language is a null pointer!
                os << bformat(_("Language: %1$s, "),
                              (language() == reset_language) ? _("Default")
diff --git a/src/Font.h b/src/Font.h
index 9ec853d..226ad26 100644
--- a/src/Font.h
+++ b/src/Font.h
@@ -90,7 +90,7 @@ public:
 
 
        /// Build GUI description of font state
-       docstring const stateText(BufferParams * params) const;
+       docstring const stateText(BufferParams * params, bool const terse = 
false) const;
 
        ///
        void validate(LaTeXFeatures & features) const;
diff --git a/src/LyXAction.cpp b/src/LyXAction.cpp
index f5ed3f8..6d31752 100644
--- a/src/LyXAction.cpp
+++ b/src/LyXAction.cpp
@@ -3830,8 +3830,10 @@ void LyXAction::init()
 
 /*!
  * \var lyx::FuncCode lyx::LFUN_TEXTSTYLE_APPLY
- * \li Action: Toggle user-defined (=last-time used) text style.
- * \li Notion: This style is set via #LFUN_TEXTSTYLE_UPDATE, which is
+ * \li Action: Apply last used text properties.
+ * \li Syntax: textstyle-apply [<NUM>]
+ * \li Params: <NUM>: number of the selection in the internal freefonts stack 
to be applied.
+ * \li Notion: These properties are stored via #LFUN_TEXTSTYLE_UPDATE, which is
                automatically triggered when using Text Style dialog.
  * \li Syntax: textstyle-apply
  * \li Origin: leeming, 12 Mar 2003
diff --git a/src/Text.h b/src/Text.h
index b54277b..054b6f6 100644
--- a/src/Text.h
+++ b/src/Text.h
@@ -109,6 +109,8 @@ public:
 
        ///
        void toggleFree(Cursor & cur, Font const &, bool toggleall = false);
+       /// Stack to save recent text propterty applications
+       std::vector<docstring> getFreeFonts() const;
 
        /// ???
        /// FIXME: replace Cursor with DocIterator.
diff --git a/src/Text3.cpp b/src/Text3.cpp
index 45eb815..9dce236 100644
--- a/src/Text3.cpp
+++ b/src/Text3.cpp
@@ -72,6 +72,7 @@
 #include "support/debug.h"
 #include "support/gettext.h"
 #include "support/lassert.h"
+#include "support/limited_stack.h"
 #include "support/lstrings.h"
 #include "support/lyxalgo.h"
 #include "support/lyxtime.h"
@@ -105,7 +106,8 @@ using cap::pasteSimpleText;
 using frontend::Clipboard;
 
 // globals...
-static Font freefont(ignore_font, ignore_language);
+typedef limited_stack<pair<docstring, Font>> FontStack;
+static FontStack freeFonts(15);
 static bool toggleall = false;
 
 static void toggleAndShow(Cursor & cur, Text * text,
@@ -2364,10 +2366,26 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                break;
        }
 
-       case LFUN_TEXTSTYLE_APPLY:
-               toggleAndShow(cur, this, freefont, toggleall);
-               cur.message(_("Character set"));
+       case LFUN_TEXTSTYLE_APPLY: {
+               unsigned int num = 0;
+               string const arg = to_utf8(cmd.argument());
+               // Argument?
+               if (!arg.empty()) {
+                       if (isStrUnsignedInt(arg)) {
+                               num = convert<uint>(arg);
+                               if (num >= freeFonts.size()) {
+                                       cur.message(_("Invalid argument (number 
exceeds stack size)!"));
+                                       break;
+                               }
+                       } else {
+                               cur.message(_("Invalid argument (must be a 
positive number)!"));
+                               break;
+                       }
+               }
+               toggleAndShow(cur, this, freeFonts[num].second, toggleall);
+               cur.message(bformat(_("Text properties applied: %1$s"), 
freeFonts[num].first));
                break;
+       }
 
        // Set the freefont using the contents of \param data dispatched from
        // the frontends and apply it at the current cursor location.
@@ -2375,17 +2393,17 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                Font font;
                bool toggle;
                if (font.fromString(to_utf8(cmd.argument()), toggle)) {
-                       freefont = font;
+                       docstring const props = 
font.stateText(&bv->buffer().params(), true);
+                       freeFonts.push(make_pair(props, font));
                        toggleall = toggle;
-                       toggleAndShow(cur, this, freefont, toggleall);
+                       toggleAndShow(cur, this, font, toggleall);
                        // We need a buffer update if we change the language
                        // of an info inset
                        if (cur.insetInSelection(INFO_CODE))
                                cur.forceBufferUpdate();
-                       cur.message(_("Character set"));
-               } else {
-                       lyxerr << "Argument not ok";
-               }
+                       cur.message(bformat(_("Text properties applied: %1$s"), 
props));
+               } else
+                       LYXERR0("Invalid argument of textstyle-update");
                break;
        }
 
@@ -3378,11 +3396,14 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & 
cmd,
        case LFUN_FONT_CROSSOUT:
        case LFUN_FONT_UNDERUNDERLINE:
        case LFUN_FONT_UNDERWAVE:
-       case LFUN_TEXTSTYLE_APPLY:
        case LFUN_TEXTSTYLE_UPDATE:
                enable = !cur.paragraph().isPassThru();
                break;
 
+       case LFUN_TEXTSTYLE_APPLY:
+               enable = !freeFonts.empty();
+               break;
+
        case LFUN_WORD_DELETE_FORWARD:
        case LFUN_WORD_DELETE_BACKWARD:
        case LFUN_LINE_DELETE_FORWARD:
@@ -3506,4 +3527,19 @@ bool Text::inDescriptionItem(Cursor & cur) const
                    && (pos == 0 || par.getChar(pos - 1) != ' ')));
 }
 
+
+std::vector<docstring> Text::getFreeFonts() const
+{
+       vector<docstring> ffList;
+
+       FontStack::const_iterator cit = freeFonts.begin();
+       FontStack::const_iterator end = freeFonts.end();
+       for (; cit != end; ++cit)
+               // we do not use cit-> here because gcc 2.9x does not
+               // like it (JMarc)
+               ffList.push_back((*cit).first);
+
+       return ffList;
+}
+
 } // namespace lyx
diff --git a/src/frontends/qt4/GuiFontLoader.cpp 
b/src/frontends/qt4/GuiFontLoader.cpp
index 4f70533..4839ad3 100644
--- a/src/frontends/qt4/GuiFontLoader.cpp
+++ b/src/frontends/qt4/GuiFontLoader.cpp
@@ -42,7 +42,8 @@ int const num_math_fonts = sizeof(math_fonts) / 
sizeof(*math_fonts);
 
 namespace lyx {
 
-extern docstring const stateText(FontInfo const & f);
+extern docstring const stateText(FontInfo const & f,
+                                bool const terse = false);
 
 namespace frontend {
 
diff --git a/src/frontends/qt4/GuiToolbar.cpp b/src/frontends/qt4/GuiToolbar.cpp
index ce1b2b7..ea2683b 100644
--- a/src/frontends/qt4/GuiToolbar.cpp
+++ b/src/frontends/qt4/GuiToolbar.cpp
@@ -41,6 +41,7 @@
 
 #include "insets/InsetText.h"
 
+#include "support/convert.h"
 #include "support/debug.h"
 #include "support/gettext.h"
 #include "support/lstrings.h"
@@ -318,8 +319,9 @@ void DynamicMenuButton::initialize()
 
 bool DynamicMenuButton::isMenuType(string const & s)
 {
-       return s == "dynamic-custom-insets" ||
-              s == "dynamic-char-styles";
+       return s == "dynamic-custom-insets"
+               || s == "dynamic-char-styles"
+               || s == "dynamic-freefonts";
 }
 
 
@@ -336,28 +338,54 @@ void DynamicMenuButton::updateTriggered()
                setEnabled(false);
                setMinimumWidth(sizeHint().width());
                d->text_class_.reset();
-               d->inset_ = 0;
+               d->inset_ = nullptr;
                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();
+       string const & menutype = tbitem_.name_;
+       if (menutype == "dynamic-custom-insets" || menutype == 
"dynamic-char-styles") {
+               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));
+       } else if (menutype == "dynamic-freefonts") {
+               m->clear();
+               vector<docstring> ffList = 
bv->cursor().innerText()->getFreeFonts();
+               unsigned int i = 0;
+               Action * default_act = nullptr;
+               for (auto const & f : ffList) {
+                       FuncRequest func(LFUN_TEXTSTYLE_APPLY, 
convert<docstring>(i),
+                                        FuncRequest::TOOLBAR);
+                       docstring const lb = char_type('&') + 
convert<docstring>(i)
+                               + from_ascii(". ") + f ;
+                       Action * act = new Action(func, QIcon(), toqstr(lb), 
toqstr(f), this);
+                       m->addAction(act);
+                       // The most recent one is the default
+                       if (i == 0)
+                               default_act = act;
+                       ++i;
+               }
+               if (default_act) {
+                       QToolButton::setDefaultAction(default_act);
+                       
QToolButton::setIcon(getIcon(FuncRequest(LFUN_TEXTSTYLE_APPLY), false));
+               }
+               setPopupMode(QToolButton::DelayedPopup);
+               
setEnabled(lyx::getStatus(FuncRequest(LFUN_TEXTSTYLE_APPLY)).enabled());
        }
-       // 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));
 }
 
 
diff --git a/src/frontends/qt4/GuiToolbar.h b/src/frontends/qt4/GuiToolbar.h
index c821347..da09bbf 100644
--- a/src/frontends/qt4/GuiToolbar.h
+++ b/src/frontends/qt4/GuiToolbar.h
@@ -86,6 +86,7 @@ protected Q_SLOTS:
 /// (stdtoolbars.inc, usually) and must be one of:
 ///            dynamic-custom-insets
 ///            dynamic-char-styles
+///            dynamic-freefonts
 /// 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().

Reply via email to