/** * \file GuiBox.cpp * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author Jürgen Vigna (Minipage stuff) * \author Martin Vermeer * \author Jürgen Spitzmüller * \author Uwe Stöhr * * Full author contact details are available in file CREDITS. */ #include #include "GuiBox.h" #include "GuiApplication.h" #include "ColorCache.h" #include "ColorSet.h" #include "LengthCombo.h" #include "Length.h" #include "qt_helpers.h" #include "Validator.h" #include "insets/InsetBox.h" #include "support/gettext.h" #include "support/foreach.h" #include "support/lstrings.h" #include #include #include #ifdef IN #undef IN #endif using namespace std; namespace lyx { namespace frontend { static QStringList boxGuiIds() { return QStringList() << "Frameless" << "Boxed" << "ovalbox" << "Ovalbox" << "Shadowbox" << "Shaded" << "Doublebox"; } static QStringList boxGuiNames() { return QStringList() << qt_("No frame") << qt_("Simple rectangular frame") << qt_("Oval frame, thin") << qt_("Oval frame, thick") << qt_("Drop shadow") << qt_("Shaded background") << qt_("Double rectangular frame"); } static QStringList boxGuiSpecialLengthIds() { return QStringList() << "height" << "depth" << "totalheight" << "width"; } static QStringList boxGuiSpecialLengthNames() { return QStringList() << qt_("Height") << qt_("Depth") << qt_("Total Height") << qt_("Width"); } 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(qt_("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); // fill the box type choice ids_ = boxGuiIds(); gui_names_ = boxGuiNames(); for (int i = 0; i != ids_.size(); ++i) typeCO->addItem(gui_names_[i], ids_[i]); // add the special units to the height choice // width needs different handling ids_spec_ = boxGuiSpecialLengthIds(); gui_names_spec_ = boxGuiSpecialLengthNames(); for (int i = 0; i != ids_spec_.size(); ++i) heightUnitsLC->addItem(gui_names_spec_[i], ids_spec_[i]); connect(widthED, SIGNAL(textChanged(QString)), this, SIGNAL(changed())); connect(widthUnitsLC, SIGNAL(selectionChanged(lyx::Length::UNIT)), this, SIGNAL(changed())); connect(valignCO, SIGNAL(highlighted(QString)), this, SIGNAL(changed())); connect(heightED, SIGNAL(textChanged(QString)), this, SIGNAL(changed())); connect(heightUnitsLC, SIGNAL(selectionChanged(lyx::Length::UNIT)), this, SIGNAL(changed())); connect(halignCO, SIGNAL(activated(int)), this, SIGNAL(changed())); connect(ialignCO, SIGNAL(activated(int)), this, SIGNAL(changed())); connect(thicknessED, SIGNAL(textChanged(QString)), this, SIGNAL(changed())); connect(thicknessUnitsLC, SIGNAL(selectionChanged(lyx::Length::UNIT)), this, SIGNAL(changed())); connect(separationED, SIGNAL(textChanged(QString)), this, SIGNAL(changed())); connect(separationUnitsLC, SIGNAL(selectionChanged(lyx::Length::UNIT)), this, SIGNAL(changed())); 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)); thicknessED->setValidator(unsignedLengthValidator(thicknessED)); separationED->setValidator(unsignedLengthValidator(separationED)); shadowsizeED->setValidator(unsignedLengthValidator(shadowsizeED)); // initialize the length validator addCheckedWidget(widthED, widthCB); addCheckedWidget(heightED, heightCB); addCheckedWidget(thicknessED, thicknessLA); 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(); } void GuiBox::on_innerBoxCO_activated(int /* index */) { QString itype = innerBoxCO->itemData(innerBoxCO->currentIndex()).toString(); // handle parbox and minipage the same way bool const ibox = (itype != "none" && itype != "makebox"); if (heightCB->isChecked() && !ibox) heightCB->setChecked(false); widthCB->setChecked(!widthED->text().isEmpty()); setSpecial(ibox); changed(); } void GuiBox::on_typeCO_activated(int index) { QString const type = typeCO->itemData(index).toString(); bool const frameless = (type == "Frameless"); QString itype = innerBoxCO->itemData(innerBoxCO->currentIndex()).toString(); setInnerType(frameless, itype); // refresh itype because it might have been changed in setInnerType itype = innerBoxCO->itemData(innerBoxCO->currentIndex()).toString(); // handle parbox and minipage the same way bool const ibox = (itype != "none" && itype != "makebox"); if (frameless && itype != "makebox") { if (heightCB->isChecked() && !ibox) heightCB->setChecked(false); setSpecial(ibox); } if (type != "Boxed") { if (type != "Frameless") 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->currentIndex() != 0) frameColorCO->setCurrentIndex(0); changed(); } void GuiBox::on_frameColorCO_currentIndexChanged(int /* index */) { // if there is a non-black frame color the background canot be uncolored // therefore remove the entry "none" in this case if (frameColorCO->currentIndex() != 0) { if (backgroundColorCO->count() == color.count()) { if (backgroundColorCO->currentIndex() == 0) backgroundColorCO->setCurrentIndex(findPos2nd(color, qt_("white"))); backgroundColorCO->removeItem(0); } } else { if (backgroundColorCO->count() == color.count() - 1) backgroundColorCO->insertItem(0, qt_("none")); } changed(); } void GuiBox::initDialog() { setInnerType(true, toqstr("minipage")); widthED->setText("100"); widthCB->setChecked(true); widthCB->setEnabled(false); widthUnitsLC->setCurrentItem(Length::PCW); heightED->setText("1"); heightUnitsLC->setCurrentItem("totalheight"); // LaTeX's default for \fboxrule is 0.4 pt thicknessED->setText("0.4"); thicknessUnitsLC->setCurrentItem(Length::PT); // LaTeX's default for \fboxsep is 3 pt separationED->setText("3"); separationUnitsLC->setCurrentItem(Length::PT); // LaTeX's default for \shadowsize is 4 pt shadowsizeED->setText("4"); shadowsizeUnitsLC->setCurrentItem(Length::PT); } void GuiBox::on_widthCB_stateChanged(int) { changed(); } void GuiBox::on_heightCB_stateChanged(int /*state*/) { changed(); } void GuiBox::on_pagebreakCB_stateChanged() { bool pbreak = (pagebreakCB->checkState() == Qt::Checked); if (pbreak) widthCB->setChecked(!pbreak); if (!pbreak) { on_typeCO_activated(typeCO->currentIndex()); return; } setSpecial(false); changed(); } void GuiBox::paramsToDialog(Inset const * inset) { InsetBox const * box = static_cast(inset); InsetBoxParams const & params = box->params(); QString type = toqstr(params.type); if (type == "Framed") { pagebreakCB->setChecked(true); type = "Boxed"; } else { pagebreakCB->setChecked(false); } typeCO->setCurrentIndex(typeCO->findData(type)); // default: minipage QString inner_type = "minipage"; if (!params.inner_box) inner_type = "none"; if (params.use_parbox) inner_type = "parbox"; if (params.use_makebox) inner_type = "makebox"; bool const frameless = (params.type == "Frameless"); setInnerType(frameless, inner_type); char c = params.pos; valignCO->setCurrentIndex(string("tcb").find(c, 0)); c = params.inner_pos; ialignCO->setCurrentIndex(string("tcbs").find(c, 0)); c = params.hor_pos; halignCO->setCurrentIndex(string("lcrs").find(c, 0)); bool ibox = (params.inner_box && !params.use_makebox); valignCO->setEnabled(ibox); ialignCO->setEnabled(ibox); setSpecial(ibox); // halign is only allowed if a width is used halignCO->setEnabled(widthCB->isChecked()); // add the entry "Stretch" if the box is \makebox or \framebox and if not already there if ((inner_type == "makebox" || (type == "Boxed" && inner_type == "none")) && halignCO->count() < 4) halignCO->addItem(toqstr("Stretch")); else if (inner_type != "makebox" && (type != "Boxed" && inner_type != "none")) halignCO->removeItem(3); // pagebreak is only allowed for Boxed without inner box pagebreakCB->setEnabled(!ibox && type == "Boxed"); Length::UNIT const default_unit = Length::defaultUnit(); // the width can only be selected for makebox or framebox widthCB->setEnabled(inner_type == "makebox" || (type == "Boxed" && !ibox && !pagebreakCB->isChecked())); if (params.width.empty()) { widthCB->setChecked(false); lengthToWidgets(widthED, widthUnitsLC, params.width, default_unit); } else { widthCB->setChecked(true); lengthToWidgets(widthED, widthUnitsLC, params.width, default_unit); QString const special = toqstr(params.special); if (!special.isEmpty() && special != "none") widthUnitsLC->setCurrentItem(special); } widthED->setEnabled(widthCB->isChecked()); widthUnitsLC->setEnabled(widthCB->isChecked()); lengthToWidgets(heightED, heightUnitsLC, (params.height).asString(), default_unit); QString const height_special = toqstr(params.height_special); if (!height_special.isEmpty() && height_special != "none") heightUnitsLC->setCurrentItem(height_special); // set no optional height if the value is the default "1\height" // (special units like \height are handled as "in", // FIXME: this is a very bad UI, this check box should be disabled in // this case, not forced to 'unchecked' state. if (height_special == "totalheight" && params.height == Length("1in")) heightCB->setCheckState(Qt::Unchecked); else heightCB->setCheckState(Qt::Checked); heightCB->setEnabled(ibox); // enable line thickness only for the rectangular frame types and drop shadow thicknessED->setEnabled(type == "Boxed" || type == "Doublebox" || type == "Shadowbox"); thicknessUnitsLC->setEnabled(type == "Boxed" || type == "Doublebox" || type == "Shadowbox"); lengthToWidgets(thicknessED, thicknessUnitsLC, (params.thickness).asString(), default_unit); // enable line separation for the allowed frame types separationED->setEnabled(type == "Boxed" || type == "ovalbox" || type == "Ovalbox" || type == "Doublebox" || type == "Shadowbox"); separationUnitsLC->setEnabled(type == "Boxed" || type == "ovalbox" || type == "Ovalbox" || type == "Doublebox" || type == "Shadowbox"); lengthToWidgets(separationED, separationUnitsLC, (params.separation).asString(), default_unit); // enable shadow size for drop shadow shadowsizeED->setEnabled(type == "Shadowbox"); shadowsizeUnitsLC->setEnabled(type == "Shadowbox"); lengthToWidgets(shadowsizeED, shadowsizeUnitsLC, (params.shadowsize).asString(), default_unit); // set color frameColorCO->setCurrentIndex(findPos2nd(color, qt_(params.framecolor)) - 1); // only if the framecolor is black the backgroundcolor has the entry "none" if (frameColorCO->currentIndex() != 0) backgroundColorCO->setCurrentIndex(findPos2nd(color, qt_(params.backgroundcolor)) - 1); else backgroundColorCO->setCurrentIndex(findPos2nd(color, qt_(params.backgroundcolor))); } docstring GuiBox::dialogToParams() const { bool const pagebreak = pagebreakCB->isEnabled() && pagebreakCB->isChecked(); string box_type; if (pagebreak) box_type = "Framed"; else box_type = fromqstr(typeCO->itemData( typeCO->currentIndex()).toString()); InsetBoxParams params(box_type); params.inner_box = (!pagebreak && innerBoxCO->currentText() != qt_("None")); params.use_parbox = (!pagebreak && innerBoxCO->currentText() == qt_("Parbox")); params.use_makebox = (!pagebreak && innerBoxCO->currentText() == qt_("Makebox")); params.pos = "tcb"[valignCO->currentIndex()]; params.inner_pos = "tcbs"[ialignCO->currentIndex()]; params.hor_pos = "lcrs"[halignCO->currentIndex()]; QString unit = widthUnitsLC->itemData(widthUnitsLC->currentIndex()).toString(); QString value = widthED->text(); if (widthED->isEnabled()) { if (ids_spec_.contains(unit) && !isValidLength(fromqstr(value))) { params.special = fromqstr(unit); // Note: the unit is simply ignored in this case params.width = Length(value.toDouble(), Length::IN); } else { params.special = "none"; // we must specify a valid length in this case if (value.isEmpty()) widthED->setText("0"); params.width = Length(widgetsToLength(widthED, widthUnitsLC)); } } else { params.special = "none"; params.width = Length(); } // the height parameter is omitted if the value // is "1in" and "Total Height" is used as unit. // 1in + "Total Height" means "1\height" which is the LaTeX default // if no height is given if (heightCB->checkState() == Qt::Unchecked) { params.height = Length("1in"); params.height_special = "totalheight"; } else { unit = heightUnitsLC->itemData(heightUnitsLC->currentIndex()).toString(); value = heightED->text(); if (ids_spec_.contains(unit) && !isValidLength(fromqstr(value))) { params.height_special = fromqstr(unit); // Note: the unit is simply ignored in this case params.height = Length(value.toDouble(), Length::IN); } else { params.height_special = "none"; params.height = Length(widgetsToLength(heightED, heightUnitsLC)); } } // handle the line thickness, line separation and shadow size if (thicknessED->isEnabled()) params.thickness = Length(widgetsToLength(thicknessED, thicknessUnitsLC)); else params.thickness = Length("0.4pt"); if (separationED->isEnabled()) params.separation = Length(widgetsToLength(separationED, separationUnitsLC)); else params.separation = Length("3pt"); if (separationED->isEnabled()) params.shadowsize = Length(widgetsToLength(shadowsizeED, shadowsizeUnitsLC)); else params.shadowsize = Length("4pt"); if (frameColorCO->isEnabled()) params.framecolor = lcolor.getLaTeXName(color[frameColorCO->currentIndex() + 1].second); else params.framecolor = "black"; if (backgroundColorCO->isEnabled()) { // only if the framecolor is black the backgroundcolor has the entry "none" if (frameColorCO->currentIndex() != 0) params.backgroundcolor = lcolor.getLaTeXName(color[backgroundColorCO->currentIndex() + 1].second); else params.backgroundcolor = lcolor.getLaTeXName(color[backgroundColorCO->currentIndex()].second); } else params.backgroundcolor = "none"; return from_ascii(InsetBox::params2string(params)); } bool GuiBox::checkWidgets(bool readonly) const { typeCO->setEnabled(!readonly); if (readonly) { pagebreakCB->setEnabled(false); innerBoxCO->setEnabled(false); valignCO->setEnabled(false); ialignCO->setEnabled(false); halignCO->setEnabled(false); widthCB->setEnabled(false); widthED->setEnabled(false); widthUnitsLC->setEnabled(false); heightED->setEnabled(false); heightUnitsLC->setEnabled(false); heightCB->setEnabled(false); thicknessED->setEnabled(false); thicknessUnitsLC->setEnabled(false); separationED->setEnabled(false); separationUnitsLC->setEnabled(false); shadowsizeED->setEnabled(false); shadowsizeUnitsLC->setEnabled(false); } else { QString const outer = typeCO->itemData(typeCO->currentIndex()).toString(); QString const itype = innerBoxCO->itemData(innerBoxCO->currentIndex()).toString(); bool const ibox = (itype != "none" && itype != "makebox"); valignCO->setEnabled(ibox); ialignCO->setEnabled(ibox); if (heightCB->isChecked() && !ibox) heightCB->setChecked(false); heightCB->setEnabled(ibox); // the width can only be selected for makebox or framebox widthCB->setEnabled(itype == "makebox" || (outer == "Boxed" && itype == "none" && !pagebreakCB->isChecked())); // except for Frameless and Boxed, the width cannot be specified if // there is no inner box bool const width_enabled = ibox || outer == "Frameless" || (outer == "Boxed" && !pagebreakCB->isChecked()); // enable if width_enabled widthED->setEnabled(width_enabled); widthUnitsLC->setEnabled(width_enabled); if (!widthCB->isChecked() && widthCB->isEnabled()) { widthED->setEnabled(false); widthUnitsLC->setEnabled(false); } // halign is only allowed if a width is used halignCO->setEnabled(widthCB->isChecked()); // add the entry "Stretch" if the box is \makebox or \framebox and if not already there if ((itype == "makebox" || (outer == "Boxed" && itype == "none")) && halignCO->count() < 4) halignCO->addItem(toqstr("Stretch")); else if (itype != "makebox" && (outer != "Boxed" && itype != "none")) halignCO->removeItem(3); // pagebreak is only allowed for Boxed without inner box pagebreakCB->setEnabled(!ibox && outer == "Boxed"); heightED->setEnabled(itype != "none" && heightCB->isChecked()); heightUnitsLC->setEnabled(itype != "none" && heightCB->isChecked()); heightCB->setEnabled(ibox); // enable line thickness for the rectangular frame types and drop shadow thicknessED->setEnabled(outer == "Boxed" || outer == "Doublebox" || outer == "Shadowbox"); thicknessUnitsLC->setEnabled(outer == "Boxed" || outer == "Doublebox" || outer == "Shadowbox"); // set default values if empty if (thicknessED->text().isEmpty() && thicknessED->isEnabled()) { thicknessED->setText("0.4"); thicknessUnitsLC->setCurrentItem(Length::PT); } // enable line separation for the allowed frame types separationED->setEnabled(outer == "Boxed" || outer == "ovalbox" || outer == "Ovalbox" || outer == "Doublebox" || outer == "Shadowbox"); separationUnitsLC->setEnabled(outer == "Boxed" || outer == "ovalbox" || outer == "Ovalbox" || outer == "Doublebox" || outer == "Shadowbox"); // set default values if empty if (separationED->text().isEmpty() && separationED->isEnabled()) { separationED->setText("3"); separationUnitsLC->setCurrentItem(Length::PT); } // enable shadow size for drop shadow shadowsizeED->setEnabled(outer == "Shadowbox"); shadowsizeUnitsLC->setEnabled(outer == "Shadowbox"); // set default values if empty if (shadowsizeED->text().isEmpty() && shadowsizeED->isEnabled()) { 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(); } void GuiBox::setSpecial(bool ibox) { QString const last_item = widthUnitsLC->itemData(heightUnitsLC->currentIndex()).toString(); // check if the widget contains the special units bool const has_special = (widthUnitsLC->findData("totalheight") != -1); // insert 'em if needed... if (!ibox && !has_special) { for (int i = 1; i < ids_spec_.size(); ++i) widthUnitsLC->addItem(gui_names_spec_[i], ids_spec_[i]); // ... or remove 'em if needed } else if (ibox && has_special) { for (int i = 1; i < ids_spec_.size(); ++i) { int n = widthUnitsLC->findData(ids_spec_[i]); if (n != -1) widthUnitsLC->removeItem(n); } } // restore selected text, if possible widthUnitsLC->setCurrentItem(last_item); } void GuiBox::setInnerType(bool frameless, QString const & type) { // with "frameless" boxes, inner box is mandatory // (i.e. is the actual box) // we have to remove "none" then and adjust the combo innerBoxCO->clear(); if (!frameless) innerBoxCO->addItem(qt_("None"), toqstr("none")); else innerBoxCO->addItem(qt_("Makebox"), toqstr("makebox")); innerBoxCO->addItem(qt_("Parbox"), toqstr("parbox")); innerBoxCO->addItem(qt_("Minipage"), toqstr("minipage")); int i = (innerBoxCO->findData(type) != -1) ? innerBoxCO->findData(type) : 0; innerBoxCO->setCurrentIndex(i); } } // namespace frontend } // namespace lyx #include "moc_GuiBox.cpp"