From 88593e0ec63f4aebf0ac62def810d1c3921e245e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Uwe=20St=C3=B6hr?= Date: Sat, 16 May 2015 03:22:37 +0200 Subject: [PATCH] support for colored boxes adds support to set colors for some box types (support for the LaTeX commands \fcolorbox and \colorbox) fileformat change --- development/FORMAT | 10 +- lib/lyx2lyx/LyX.py | 2 +- lib/lyx2lyx/lyx_2_2.py | 49 +++++++- src/frontends/qt4/GuiBox.cpp | 108 +++++++++++++++++ src/frontends/qt4/GuiBox.h | 7 +- src/frontends/qt4/ui/BoxUi.ui | 212 ++++++++++++++++++++++------------ src/insets/InsetBox.cpp | 82 ++++++++++--- src/insets/InsetBox.h | 4 + src/tex2lyx/TODO.txt | 18 +-- src/tex2lyx/text.cpp | 2 + src/version.h | 4 +- 11 files changed, 387 insertions(+), 111 deletions(-) diff --git a/development/FORMAT b/development/FORMAT index ae75380c5a..11b1ec117f 100644 --- a/development/FORMAT +++ b/development/FORMAT @@ -11,9 +11,17 @@ adjustments are made to tex2lyx and bugs are fixed in lyx2lyx. ----------------------- + +2015-05-16 Uwe Stöhr + * Format incremented to 492: support for \colorbox and \fcolorbox + in the box dialog. + New box parameters: + - framcolor + - backgroundcolor + 2015-05-14 Uwe Stöhr * Format incremented to 491: support for xcolor's default colors - No new parameter, the \\color parameter can nowhave these values: + No new parameter, the \color parameter can now also have these values: "brown", "darkgray", "gray", "lightgray", "lime", "olive", "orange", "pink", "purple", "teal", "violet" diff --git a/lib/lyx2lyx/LyX.py b/lib/lyx2lyx/LyX.py index 246ab33373..c7815f7fd4 100644 --- a/lib/lyx2lyx/LyX.py +++ b/lib/lyx2lyx/LyX.py @@ -85,7 +85,7 @@ format_relation = [("0_06", [200], minor_versions("0.6" , 4)), ("1_6", list(range(277,346)), minor_versions("1.6" , 10)), ("2_0", list(range(346,414)), minor_versions("2.0" , 8)), ("2_1", list(range(414,475)), minor_versions("2.1" , 0)), - ("2_2", list(range(475,492)), minor_versions("2.2" , 0)) + ("2_2", list(range(475,493)), minor_versions("2.2" , 0)) ] #################################################################### diff --git a/lib/lyx2lyx/lyx_2_2.py b/lib/lyx2lyx/lyx_2_2.py index 087e9c35e5..ec6213b90a 100644 --- a/lib/lyx2lyx/lyx_2_2.py +++ b/lib/lyx2lyx/lyx_2_2.py @@ -1077,6 +1077,51 @@ def revert_textcolor(document): i = i + 1 +def convert_colorbox(document): + " adds color settings for boxes " + + i = 0 + while True: + i = find_token(document.body, "height_special", i) + if i == -1: + return + document.body.insert(i + 2, 'framecolor "black"\nbackgroundcolor "white"') + i = i + 2 + + +def revert_colorbox(document): + " outputs color settings for boxes as TeX code " + + i = 0 + defaultframecolor = "black" + defaultbackcolor = "white" + while True: + i = find_token(document.body, "framecolor", i) + if i == -1: + return + # read out the values + beg = document.body[i].find('"'); + end = document.body[i].rfind('"'); + framecolor = document.body[i][beg+1:end]; + beg = document.body[i+1].find('"'); + end = document.body[i+1].rfind('"'); + backcolor = document.body[i+1][beg+1:end]; + # delete the specification + del document.body[i:i+2] + # output TeX code + # first output the closing brace + if framecolor != defaultframecolor or backcolor != defaultbackcolor: + document.body[i + 9 : i + 9] = put_cmd_in_ert("}") + # now output the box commands + if framecolor != defaultframecolor or backcolor != defaultbackcolor: + document.body[i - 14 : i - 14] = put_cmd_in_ert("{") + if framecolor != defaultframecolor: + document.body[i - 9 : i - 8] = ["\\backslash fboxcolor{" + framecolor + "}{" + backcolor + "}{"] + if backcolor != defaultbackcolor and framecolor == defaultframecolor: + document.body[i - 9 : i - 8] = ["\\backslash colorbox{" + backcolor + "}{"] + i = i + 11 + + ## # Conversion hub # @@ -1102,10 +1147,12 @@ convert = [ [488, [convert_newgloss]], [489, [convert_BoxFeatures]], [490, [convert_origin]], - [491, []] + [491, []], + [492, [convert_colorbox]] ] revert = [ + [491, [revert_colorbox]], [490, [revert_textcolor]], [489, [revert_origin]], [488, [revert_BoxFeatures]], diff --git a/src/frontends/qt4/GuiBox.cpp b/src/frontends/qt4/GuiBox.cpp index 2da69472aa..2deb45f9b0 100644 --- a/src/frontends/qt4/GuiBox.cpp +++ b/src/frontends/qt4/GuiBox.cpp @@ -15,6 +15,8 @@ #include "GuiBox.h" +#include "GuiApplication.h" +#include "ColorCache.h" #include "LengthCombo.h" #include "Length.h" #include "qt_helpers.h" @@ -26,6 +28,7 @@ #include "support/foreach.h" #include "support/lstrings.h" +#include #include #include @@ -73,6 +76,60 @@ static QStringList boxGuiSpecialLengthNames() } +static QList colorData() +{ + QList colors; + colors << ColorPair(qt_("none"), Color_none); + colors << ColorPair(qt_("black"), Color_black); + colors << ColorPair(qt_("white"), Color_white); + colors << ColorPair(qt_("blue"), Color_blue); + colors << ColorPair(qt_("brown"), Color_brown); + colors << ColorPair(qt_("cyan"), Color_cyan); + colors << ColorPair(qt_("darkgray"), Color_darkgray); + colors << ColorPair(qt_("gray"), Color_gray); + colors << ColorPair(qt_("green"), Color_green); + colors << ColorPair(qt_("lightgray"), Color_lightgray); + colors << ColorPair(qt_("lime"), Color_lime); + colors << ColorPair(qt_("magenta"), Color_magenta); + colors << ColorPair(qt_("olive"), Color_olive); + colors << ColorPair(qt_("orange"), Color_orange); + colors << ColorPair(qt_("pink"), Color_pink); + colors << ColorPair(qt_("purple"), Color_purple); + colors << ColorPair(qt_("red"), Color_red); + colors << ColorPair(qt_("teal"), Color_teal); + colors << ColorPair(qt_("violet"), Color_violet); + colors << ColorPair(qt_("yellow"), Color_yellow); + return colors; +} + + +template +void fillComboColor(QComboBox * combo, QList const & list, bool const is_none) +{ + QPixmap coloritem(32, 32); + QColor color; + // frameColorCO cannot be uncolored + if (is_none) + combo->addItem("none"); + typename QList::const_iterator cit = list.begin() + 1; + for (; cit != list.end(); ++cit) { + color = QColor(guiApp->colorCache().get(cit->second, false)); + coloritem.fill(color); + combo->addItem(QIcon(coloritem), cit->first); + } +} + + +template +static int findPos2nd(QList

const & vec, QString val) +{ + for (int i = 0; i != vec.size(); ++i) + if (vec[i].first == val) + return i; + return 0; +} + + GuiBox::GuiBox(QWidget * parent) : InsetParamsWidget(parent) { setupUi(this); @@ -108,6 +165,8 @@ GuiBox::GuiBox(QWidget * parent) : InsetParamsWidget(parent) connect(shadowsizeED, SIGNAL(textChanged(QString)), this, SIGNAL(changed())); connect(shadowsizeUnitsLC, SIGNAL(selectionChanged(lyx::Length::UNIT)), this, SIGNAL(changed())); + connect(frameColorCO, SIGNAL(highlighted(QString)), this, SIGNAL(changed())); + connect(backgroundColorCO, SIGNAL(highlighted(QString)), this, SIGNAL(changed())); heightED->setValidator(unsignedLengthValidator(heightED)); widthED->setValidator(unsignedLengthValidator(widthED)); @@ -122,6 +181,12 @@ GuiBox::GuiBox(QWidget * parent) : InsetParamsWidget(parent) addCheckedWidget(separationED, separationLA); addCheckedWidget(shadowsizeED, shadowsizeLA); + // initialize colors + color = colorData(); + // the background can be uncolored while the frame cannot + fillComboColor(frameColorCO, color, false); + fillComboColor(backgroundColorCO, color, true); + initDialog(); } @@ -163,6 +228,26 @@ void GuiBox::on_typeCO_activated(int index) widthCB->setChecked(itype != "none"); pagebreakCB->setChecked(false); } + // assure that the frame color is black for frameless boxes to + // provide the color "none" + if (frameless && frameColorCO->currentText() != qt_("black")) + frameColorCO->setCurrentIndex(0); + changed(); +} + + +void GuiBox::on_frameColorCO_currentIndexChanged(int /* index */) +{ + // if there is a special frme color the background canot be uncolored + if (frameColorCO->currentText() != qt_("black")) { + if (backgroundColorCO->currentText() == qt_("none")) + backgroundColorCO->setCurrentIndex(findPos2nd(color, qt_("white"))); + if (backgroundColorCO->itemText(0) == qt_("none")) + backgroundColorCO->removeItem(0); + } else { + if (backgroundColorCO->itemText(0) != qt_("none")) + backgroundColorCO->insertItem(0, qt_("none")); + } changed(); } @@ -185,6 +270,9 @@ void GuiBox::initDialog() // LaTeX's default for \shadowsize is 4 pt shadowsizeED->setText("4"); shadowsizeUnitsLC->setCurrentItem(Length::PT); + // the default color is black and none + frameColorCO->setCurrentIndex(findPos2nd(color, qt_("black")) - 1); + backgroundColorCO->setCurrentIndex(findPos2nd(color, qt_("none"))); } @@ -318,6 +406,12 @@ void GuiBox::paramsToDialog(Inset const * inset) shadowsizeUnitsLC->setEnabled(type == "Shadowbox"); lengthToWidgets(shadowsizeED, shadowsizeUnitsLC, (params.shadowsize).asString(), default_unit); + // set color + frameColorCO->setCurrentIndex(findPos2nd(color, qt_(params.framecolor)) - 1); + if (frameColorCO->currentText() != qt_("black")) + backgroundColorCO->setCurrentIndex(findPos2nd(color, qt_(params.backgroundcolor)) - 1); + else + backgroundColorCO->setCurrentIndex(findPos2nd(color, qt_(params.backgroundcolor))); } @@ -399,6 +493,17 @@ docstring GuiBox::dialogToParams() const params.shadowsize = Length(widgetsToLength(shadowsizeED, shadowsizeUnitsLC)); else params.shadowsize = Length("4pt"); + if (frameColorCO->isEnabled()) + params.framecolor = fromqstr( color[frameColorCO->currentIndex() + 1].first ); + else + params.framecolor = "black"; + if (backgroundColorCO->isEnabled()) { + if (frameColorCO->currentText() != qt_("black")) + params.backgroundcolor = fromqstr(color[backgroundColorCO->currentIndex() + 1].first); + else + params.backgroundcolor = fromqstr(color[backgroundColorCO->currentIndex()].first); + } else + params.backgroundcolor = "none"; return from_ascii(InsetBox::params2string(params)); } @@ -492,6 +597,9 @@ bool GuiBox::checkWidgets(bool readonly) const shadowsizeED->setText("4"); shadowsizeUnitsLC->setCurrentItem(Length::PT); } + // \fboxcolor and \colorbox cannot be used for fancybox boxes + frameColorCO->setEnabled(!pagebreakCB->isChecked() && outer == "Boxed"); + backgroundColorCO->setEnabled(!pagebreakCB->isChecked() && (frameColorCO->isEnabled() || outer == "Frameless")); } return InsetParamsWidget::checkWidgets(); diff --git a/src/frontends/qt4/GuiBox.h b/src/frontends/qt4/GuiBox.h index 4ff7ce9443..e40bcae4ef 100644 --- a/src/frontends/qt4/GuiBox.h +++ b/src/frontends/qt4/GuiBox.h @@ -15,11 +15,13 @@ #include "InsetParamsWidget.h" #include "ui_BoxUi.h" - +#include "Font.h" namespace lyx { namespace frontend { +typedef std::pair ColorPair; + class GuiBox : public InsetParamsWidget, public Ui::BoxUi { Q_OBJECT @@ -30,6 +32,7 @@ public: private Q_SLOTS: void on_innerBoxCO_activated(int); void on_typeCO_activated(int); + void on_frameColorCO_currentIndexChanged(int); void initDialog(); void on_widthCB_stateChanged(int state); void on_heightCB_stateChanged(int state); @@ -57,6 +60,8 @@ private: QStringList ids_spec_; /// QStringList gui_names_spec_; + /// + QList color; }; } // namespace frontend diff --git a/src/frontends/qt4/ui/BoxUi.ui b/src/frontends/qt4/ui/BoxUi.ui index 22ae57d4c9..01ffcdc354 100644 --- a/src/frontends/qt4/ui/BoxUi.ui +++ b/src/frontends/qt4/ui/BoxUi.ui @@ -6,8 +6,8 @@ 0 0 - 262 - 411 + 446 + 283 @@ -16,7 +16,7 @@ true - + @@ -26,6 +26,48 @@ true + + + + Width value + + + + + + + + + + + + + &Height: + + + false + + + false + + + + + + + + + + &Width: + + + false + + + false + + + @@ -88,52 +130,10 @@ - - - - - - - &Height: - - - false - - - false - - - - - - - - - - Width value - - - - - - - - - - &Width: - - - false - - - false - - - - + Alignment @@ -277,10 +277,23 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + - + Decoration @@ -289,13 +302,6 @@ true - - - - Line thickness: - - - @@ -303,19 +309,6 @@ - - - - - - - &Decoration: - - - typeCO - - - @@ -326,18 +319,21 @@ - - + + - Shadow size: + Line thickness: - - + + false + + Separation value + @@ -347,14 +343,31 @@ - - + + + + + + + &Decoration: + + + typeCO + + + + + + + Shadow size: + + + + + false - - Separation value - @@ -384,6 +397,51 @@ + + + + Color + + + true + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Background: + + + + + + + Frame: + + + + + + + + + + + + diff --git a/src/insets/InsetBox.cpp b/src/insets/InsetBox.cpp index cf3d791d41..c46848c816 100644 --- a/src/insets/InsetBox.cpp +++ b/src/insets/InsetBox.cpp @@ -195,15 +195,25 @@ bool InsetBox::forcePlainLayout(idx_type) const ColorCode InsetBox::backgroundColor(PainterInfo const &) const { - if (params_.type != "Shaded") + // we only support background color for 3 types + if (params_.type != "Shaded" && params_.type != "Frameless" && params_.type != "Boxed") return getLayout().bgcolor(); - // FIXME: This hardcoded color is a hack! - if (buffer().params().boxbgcolor == lyx::rgbFromHexName("#ff0000")) - return getLayout().bgcolor(); - ColorCode c = lcolor.getFromLyXName("boxbgcolor"); - if (c == Color_none) - return getLayout().bgcolor(); - return c; + if (params_.type == "Shaded") { + // FIXME: This hardcoded color is a hack! + if (buffer().params().boxbgcolor == lyx::rgbFromHexName("#ff0000")) + return getLayout().bgcolor(); + + ColorCode c = lcolor.getFromLyXName("boxbgcolor"); + if (c == Color_none) + return getLayout().bgcolor(); + return c; + } else { + if (params_.backgroundcolor == "none") + return getLayout().bgcolor(); + ColorCode boxbackground = lcolor.getFromLyXName(params_.backgroundcolor); + return boxbackground; + } + return getLayout().bgcolor(); } @@ -352,7 +362,11 @@ void InsetBox::latex(otexstream & os, OutputParams const & runparams) const && (thickness_string.find(defaultThick) != string::npos)) os << "{\\fboxsep " << from_ascii(separation_string); if (!params_.inner_box && !width_string.empty()) { - os << "\\framebox"; + if (params_.framecolor != "black" || params_.backgroundcolor != "none") { + os << "\\fcolorbox{" << params_.framecolor << "}{" << params_.backgroundcolor << "}{"; + os << "\\makebox"; + } else + os << "\\framebox"; // Special widths, see usrguide sec. 3.5 // FIXME UNICODE if (params_.special != "none") { @@ -364,8 +378,12 @@ void InsetBox::latex(otexstream & os, OutputParams const & runparams) const << ']'; if (params_.hor_pos != 'c') os << "[" << params_.hor_pos << "]"; - } else - os << "\\fbox"; + } else { + if (params_.framecolor != "black" || params_.backgroundcolor != "none") + os << "\\fcolorbox{" << params_.framecolor << "}{" << params_.backgroundcolor << "}"; + else + os << "\\fbox"; + } os << "{"; break; case ovalbox: @@ -420,11 +438,15 @@ void InsetBox::latex(otexstream & os, OutputParams const & runparams) const } if (params_.inner_box) { - if (params_.use_parbox) + if (params_.use_parbox) { + if (params_.backgroundcolor != "none" && btype == Frameless) + os << "\\colorbox{" << params_.backgroundcolor << "}{"; os << "\\parbox"; - else if (params_.use_makebox) { + } else if (params_.use_makebox) { if (!width_string.empty()) { - os << "\\makebox"; + if (params_.backgroundcolor != "none") + os << "\\colorbox{" << params_.backgroundcolor << "}{"; + os << "\\makebox{"; // FIXME UNICODE // output the width and horizontal position if (params_.special != "none") { @@ -436,12 +458,18 @@ void InsetBox::latex(otexstream & os, OutputParams const & runparams) const << ']'; if (params_.hor_pos != 'c') os << "[" << params_.hor_pos << "]"; - } else - os << "\\mbox"; - os << "{"; + } else { + if (params_.backgroundcolor != "none") + os << "\\colorbox{" << params_.backgroundcolor << "}{"; + else + os << "\\mbox{"; + } } - else + else { + if (params_.backgroundcolor != "none" && btype == Frameless) + os << "\\colorbox{" << params_.backgroundcolor << "}{"; os << "\\begin{minipage}"; + } // output parameters for parbox and minipage if (!params_.use_makebox) { @@ -505,6 +533,9 @@ void InsetBox::latex(otexstream & os, OutputParams const & runparams) const os << "%\n}"; else os << "%\n\\end{minipage}"; + if (params_.backgroundcolor != "none" && btype == Frameless + && !(params_.use_makebox && width_string.empty())) + os << "}"; } switch (btype) { @@ -518,6 +549,9 @@ void InsetBox::latex(otexstream & os, OutputParams const & runparams) const break; case Boxed: os << "}"; + if (!params_.inner_box && !width_string.empty() + && (params_.framecolor != "black" || params_.backgroundcolor != "none")) + os << "}"; if (!(separation_string.find(defaultSep) != string::npos) || !(thickness_string.find(defaultThick) != string::npos)) os << "}"; @@ -650,6 +684,8 @@ void InsetBox::validate(LaTeXFeatures & features) const BoxType btype = boxtranslator().find(params_.type); switch (btype) { case Frameless: + if (params_.backgroundcolor != "none") + features.require("xcolor"); break; case Framed: features.require("calc"); @@ -657,6 +693,8 @@ void InsetBox::validate(LaTeXFeatures & features) const break; case Boxed: features.require("calc"); + if (params_.framecolor != "black" || params_.backgroundcolor != "none") + features.require("xcolor"); break; case ovalbox: case Ovalbox: @@ -740,7 +778,9 @@ InsetBoxParams::InsetBoxParams(string const & label) height_special("totalheight"), // default is 1\\totalheight thickness(Length(defaultThick)), separation(Length(defaultSep)), - shadowsize(Length(defaultShadow)) + shadowsize(Length(defaultShadow)), + framecolor("black"), + backgroundcolor("none") {} @@ -760,6 +800,8 @@ void InsetBoxParams::write(ostream & os) const os << "thickness \"" << thickness.asString() << "\"\n"; os << "separation \"" << separation.asString() << "\"\n"; os << "shadowsize \"" << shadowsize.asString() << "\"\n"; + os << "framecolor \"" << framecolor << "\"\n"; + os << "backgroundcolor \"" << backgroundcolor << "\"\n"; } @@ -782,6 +824,8 @@ void InsetBoxParams::read(Lexer & lex) lex >> "thickness" >> thickness; lex >> "separation" >> separation; lex >> "shadowsize" >> shadowsize; + lex >> "framecolor" >> framecolor; + lex >> "backgroundcolor" >> backgroundcolor; } diff --git a/src/insets/InsetBox.h b/src/insets/InsetBox.h index e8ea613efd..877dd603f5 100644 --- a/src/insets/InsetBox.h +++ b/src/insets/InsetBox.h @@ -59,6 +59,10 @@ public: Length separation; /// Length shadowsize; + /// + std::string framecolor; + /// + std::string backgroundcolor; }; diff --git a/src/tex2lyx/TODO.txt b/src/tex2lyx/TODO.txt index 511927052b..f89ddc40c5 100644 --- a/src/tex2lyx/TODO.txt +++ b/src/tex2lyx/TODO.txt @@ -75,15 +75,15 @@ Format LaTeX feature LyX feature 459 beamer: \begin{frame}, \begin_layout Frame \begin{frame}[plain], \begin_layout PlainFrame \begin{frame}[fragile] \begin_layout FragileFrame -466 Powerdot updates: - \pause[] layout Pause - \onslide{}{} InsetFlex, InsetArgument - \onslide*{}{} InsetFlex, InsetArgument - \onslide+{}{} InsetFlex, InsetArgument - \twocolumn[]{}{} Layout Twocolumn, InsetArgument - \item[]<> InsetArgument - \begin{enumerate|itemize|...}[] InsetArgument - +466 Powerdot updates: + \pause[] layout Pause + \onslide{}{} InsetFlex, InsetArgument + \onslide*{}{} InsetFlex, InsetArgument + \onslide+{}{} InsetFlex, InsetArgument + \twocolumn[]{}{} Layout Twocolumn, InsetArgument + \item[]<> InsetArgument + \begin{enumerate|itemize|...}[] InsetArgument +492 commands \fcolorbox and \colorbox InsetBox General diff --git a/src/tex2lyx/text.cpp b/src/tex2lyx/text.cpp index c0845c172b..3cc3d31ade 100644 --- a/src/tex2lyx/text.cpp +++ b/src/tex2lyx/text.cpp @@ -850,6 +850,8 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags, string thickness = "0.4pt"; string separation = "3pt"; string shadowsize = "4pt"; + string framecolor = "black"; + string backgroundcolor = "none"; if (!inner_type.empty() && p.hasOpt()) { if (inner_type != "makebox") position = p.getArg('[', ']'); diff --git a/src/version.h b/src/version.h index 64ecd8f33c..06ddc33472 100644 --- a/src/version.h +++ b/src/version.h @@ -32,8 +32,8 @@ extern char const * const lyx_version_info; // Do not remove the comment below, so we get merge conflict in // independent branches. Instead add your own. -#define LYX_FORMAT_LYX 491 // uwestoehr: support more text colors -#define LYX_FORMAT_TEX2LYX 491 +#define LYX_FORMAT_LYX 492 // uwestoehr: support for colorboxes +#define LYX_FORMAT_TEX2LYX 492 #if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX #ifndef _MSC_VER -- 2.39.2