diff --git a/libs/ui/utils/kis_document_aware_spin_box_unit_manager.cpp b/libs/ui/utils/kis_document_aware_spin_box_unit_manager.cpp index 23858a32ae..5c0a86bceb 100644 --- a/libs/ui/utils/kis_document_aware_spin_box_unit_manager.cpp +++ b/libs/ui/utils/kis_document_aware_spin_box_unit_manager.cpp @@ -1,163 +1,183 @@ /* * Copyright (c) 2017 Laurent Valentin Jospin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_document_aware_spin_box_unit_manager.h" #include "KisPart.h" #include "KisMainWindow.h" #include "KisView.h" #include "KisDocument.h" #include "kis_types.h" #include "kis_image.h" #include "kis_image_animation_interface.h" #include "kis_time_range.h" KisSpinBoxUnitManager* KisDocumentAwareSpinBoxUnitManagerBuilder::buildUnitManager(QObject* parent) { return new KisDocumentAwareSpinBoxUnitManager(parent); } void KisDocumentAwareSpinBoxUnitManager::setDocumentAwarnessToExistingUnitSpinBox(KisDoubleParseUnitSpinBox* spinBox, bool setUnitFromOutsideToggle) { KisDocumentAwareSpinBoxUnitManager* manager = new KisDocumentAwareSpinBoxUnitManager(spinBox); spinBox->setUnitManager(manager); spinBox->setUnitChangeFromOutsideBehavior(setUnitFromOutsideToggle); } KisDoubleParseUnitSpinBox* KisDocumentAwareSpinBoxUnitManager::createUnitSpinBoxWithDocumentAwarness(QWidget* parent) { KisDoubleParseUnitSpinBox* spinBox = new KisDoubleParseUnitSpinBox(parent); setDocumentAwarnessToExistingUnitSpinBox(spinBox); return spinBox; } KisDocumentAwareSpinBoxUnitManager::KisDocumentAwareSpinBoxUnitManager(QObject *parent, int pPixDir): KisSpinBoxUnitManager(parent) { if (pPixDir == PIX_DIR_Y) { pixDir = PIX_DIR_Y; } else { pixDir = PIX_DIR_X; } grantDocumentRelativeUnits(); //the purpose of this class is to manage document relative units. } -qreal KisDocumentAwareSpinBoxUnitManager::getConversionFactor(int dim, QString symbol) const +qreal KisDocumentAwareSpinBoxUnitManager::getConversionFactor(int dim, QString psymbol) const { + QString symbol = psymbol; + + if (symbol == "%") { //percent can be seen as vw or vh depending of the reference side in the image. + if (pixDir == PIX_DIR_X) { + symbol = "vw"; + } else { + symbol = "vh"; + } + } + qreal factor = KisSpinBoxUnitManager::getConversionFactor(dim, symbol); if (factor > 0) { //no errors occured at a lower level, so the conversion factor has been get. return factor; } factor = 1; //fall back to something natural in case document is unreachable (1 px = 1 pt = 1vw = 1vh). So a virtual document of 100x100 with a resolution of 1. KisView* view = KisPart::instance()->currentMainwindow()->activeView(); if (view == nullptr) { return factor; } KisDocument* doc = view->document(); if (doc == nullptr) { return factor; } KisImage* img = doc->image().data(); if (img == nullptr) { return factor; } qreal resX = img->xRes(); qreal resY = img->yRes(); qreal sizeX = img->width(); qreal sizeY = img->height(); switch (dim) { case LENGTH: if (symbol == "px") { if (pixDir == PIX_DIR_X) { factor = resX; } else { factor = resY; } } else if (symbol == "vw") { qreal docWidth = sizeX/resX; factor = 100.0/docWidth; //1 vw is 1% of document width, 1 vw in point is docWidth/100 so 1 point in vw is the inverse. } else if (symbol == "vh") { qreal docHeight = sizeY/resY; factor = 100.0/docHeight; } break; case IMLENGTH: if (symbol == "vw") { factor = 100.0/sizeX; //1 vw is 1% of document width, 1 vw in pixel is sizeX/100 so 1 pixel in vw is the inverse. } else if (symbol == "vh") { factor = 100.0/sizeY; } break; case TIME: { if (symbol == "s") { qreal fps = img->animationInterface()->framerate(); factor = 1/fps; } else if (symbol == "%") { const KisTimeRange & time_range = img->animationInterface()->fullClipRange(); qreal n_frame = time_range.end() - time_range.start(); factor = 100/n_frame; } } break; default: break; } return factor; } qreal KisDocumentAwareSpinBoxUnitManager::getConversionConstant(int dim, QString symbol) const { if (dim == TIME && symbol == "%") { KisImage* img = KisPart::instance()->currentMainwindow()->activeView()->document()->image().data(); const KisTimeRange & time_range = img->animationInterface()->fullClipRange(); qreal n_frame = time_range.end() - time_range.start(); return -time_range.start()*100.0/n_frame; } return KisSpinBoxUnitManager::getConversionConstant(dim, symbol); } + + +bool KisDocumentAwareSpinBoxUnitManager::hasPercent(int unitDim) const { + + if (unitDim == IMLENGTH || unitDim == LENGTH) { + return true; + } + + return KisSpinBoxUnitManager::hasPercent(unitDim); +} diff --git a/libs/ui/utils/kis_document_aware_spin_box_unit_manager.h b/libs/ui/utils/kis_document_aware_spin_box_unit_manager.h index 9add3a50a7..f7c08ca809 100644 --- a/libs/ui/utils/kis_document_aware_spin_box_unit_manager.h +++ b/libs/ui/utils/kis_document_aware_spin_box_unit_manager.h @@ -1,67 +1,70 @@ /* * Copyright (c) 2017 Laurent Valentin Jospin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISDOCUMENTAWARESPINBOXUNITMANAGER_H #define KISDOCUMENTAWARESPINBOXUNITMANAGER_H #include "kis_spin_box_unit_manager.h" #include "kis_double_parse_unit_spin_box.h" #include "kritaui_export.h" class KisDocumentAwareSpinBoxUnitManagerBuilder : public KisSpinBoxUnitManagerBuilder { public: KisSpinBoxUnitManager* buildUnitManager(QObject* parent) override; }; /*! * \brief The KisDocumentAwareSpinBoxUnitManager class is a KisSpinBoxUnitManager that is able to connect to the current document to compute transformation for document relative units (the ones that depend of the resolution, or the size in pixels of the image). * \see KisSpinBoxUnitManager */ class KRITAUI_EXPORT KisDocumentAwareSpinBoxUnitManager : public KisSpinBoxUnitManager { Q_OBJECT public: enum PixDir { PIX_DIR_X, PIX_DIR_Y }; //in case the image has not the same x and y resolution, indicate on which direction get the resolution. //! \brief configure a KisDocumentAwareSpinBoxUnitManager for the given spinbox (make the manager a child of the spinbox and attach it to the spinbox). static void setDocumentAwarnessToExistingUnitSpinBox(KisDoubleParseUnitSpinBox* spinBox, bool setUnitFromOutsideToggle = false); //! \brief create a unitSpinBox that is already document aware. static KisDoubleParseUnitSpinBox* createUnitSpinBoxWithDocumentAwarness(QWidget* parent = 0); KisDocumentAwareSpinBoxUnitManager(QObject *parent = 0, int pPixDir = PIX_DIR_X); //! \reimp \see KisSpinBoxUnitManager - qreal getConversionFactor(int dim, QString symbol) const override; + qreal getConversionFactor(int dim, QString psymbol) const override; //! \reimp \see KisSpinBoxUnitManager qreal getConversionConstant(int dim, QString symbol) const override; -private: +protected: + + //! \reimp \see KisSpinBoxUnitManager + virtual bool hasPercent(int unitDim) const; PixDir pixDir; }; #endif // KISDOCUMENTAWARESPINBOXUNITMANAGER_H diff --git a/libs/widgets/kis_double_parse_unit_spin_box.cpp b/libs/widgets/kis_double_parse_unit_spin_box.cpp index 21d05ad526..ef08bb33bc 100644 --- a/libs/widgets/kis_double_parse_unit_spin_box.cpp +++ b/libs/widgets/kis_double_parse_unit_spin_box.cpp @@ -1,422 +1,439 @@ /* * Copyright (c) 2016 Laurent Valentin Jospin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_double_parse_unit_spin_box.h" #include "kis_spin_box_unit_manager.h" #include class Q_DECL_HIDDEN KisDoubleParseUnitSpinBox::Private { public: Private(double low, double up, double step, KisSpinBoxUnitManager* unitManager) : lowerInPoints(low), upperInPoints(up), stepInPoints(step), unit(KoUnit(KoUnit::Point)), outPutSymbol(""), unitManager(unitManager), defaultUnitManager(unitManager), isDeleting(false), unitHasBeenChangedFromOutSideOnce(false), letUnitBeChangedFromOutsideMoreThanOnce(true), - displayUnit(true) + displayUnit(true), + allowResetDecimals(true) { } double lowerInPoints; ///< lowest value in points double upperInPoints; ///< highest value in points double stepInPoints; ///< step in points KoUnit unit; double previousValueInPoint; ///< allow to store the previous value in point, usefull in some cases, even if, usually, we prefere to refer to the actual value (in selected unit) and convert it, since this is not alway updated. QString previousSymbol; QString outPutSymbol; KisSpinBoxUnitManager* unitManager; //manage more units than permitted by KoUnit. KisSpinBoxUnitManager* defaultUnitManager; //the default unit manager is the one the spinbox rely on and go back to if a connected unit manager is destroyed before the spinbox. bool isDeleting; bool unitHasBeenChangedFromOutSideOnce; //in some part of the code the unit is reset. We want to prevent this overriding the unit defined by the user. We use this switch to do so. bool letUnitBeChangedFromOutsideMoreThanOnce; bool displayUnit; + bool allowResetDecimals; + }; KisDoubleParseUnitSpinBox::KisDoubleParseUnitSpinBox(QWidget *parent) : KisDoubleParseSpinBox(parent), d(new Private(-9999, 9999, 1, KisSpinBoxUnitManagerFactory::buildDefaultUnitManager(this))) { setUnit( KoUnit(KoUnit::Point) ); setAlignment( Qt::AlignRight ); connect(this, SIGNAL(valueChanged( double )), this, SLOT(privateValueChanged())); connect(lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(detectUnitChanges()) ); connect(d->unitManager, (void (KisSpinBoxUnitManager::*)()) &KisSpinBoxUnitManager::unitAboutToChange, this, (void (KisDoubleParseUnitSpinBox::*)()) &KisDoubleParseUnitSpinBox::prepareUnitChange); connect(d->unitManager, (void (KisSpinBoxUnitManager::*)( QString )) &KisSpinBoxUnitManager::unitChanged, this, (void (KisDoubleParseUnitSpinBox::*)( QString const& )) &KisDoubleParseUnitSpinBox::internalUnitChange); + setDecimals(d->unitManager->getApparentUnitRecommandedDecimals()); + } KisDoubleParseUnitSpinBox::~KisDoubleParseUnitSpinBox() { d->isDeleting = true; delete d->defaultUnitManager; delete d; } void KisDoubleParseUnitSpinBox::setUnitManager(KisSpinBoxUnitManager* unitManager) { qreal oldVal = d->unitManager->getReferenceValue(KisDoubleParseSpinBox::value()); QString oldSymbol = d->unitManager->getApparentUnitSymbol(); qreal newVal; double newMin; double newMax; double newStep; if (oldSymbol == unitManager->getApparentUnitSymbol() && d->unitManager->getUnitDimensionType() == unitManager->getUnitDimensionType()) { d->unitManager = unitManager; //set the new unitmanager anyway, since it may be a subclass, so change the behavior anyway. goto connect_signals; } if (d->unitManager->getUnitDimensionType() == unitManager->getUnitDimensionType()) { //dimension is the same, calculate the new value newVal = unitManager->getApparentValue(oldVal); } else { newVal = unitManager->getApparentValue(d->lowerInPoints); } newMin = unitManager->getApparentValue(d->lowerInPoints); newMax = unitManager->getApparentValue(d->upperInPoints); newStep = unitManager->getApparentValue(d->stepInPoints); if (unitManager->getApparentUnitSymbol() == KoUnit(KoUnit::Pixel).symbol()) { // limit the pixel step by 1.0 newStep = qMax(qreal(1.0), newStep); } KisDoubleParseSpinBox::setMinimum(newMin); KisDoubleParseSpinBox::setMaximum(newMax); KisDoubleParseSpinBox::setSingleStep(newStep); connect_signals: if (d->unitManager != d->defaultUnitManager) { disconnect(d->unitManager, &QObject::destroyed, this, &KisDoubleParseUnitSpinBox::disconnectExternalUnitManager); //there's no dependance anymore. } disconnect(d->unitManager, (void (KisSpinBoxUnitManager::*)()) &KisSpinBoxUnitManager::unitAboutToChange, this, (void (KisDoubleParseUnitSpinBox::*)()) &KisDoubleParseUnitSpinBox::prepareUnitChange); disconnect(d->unitManager, (void (KisSpinBoxUnitManager::*)( QString )) &KisSpinBoxUnitManager::unitChanged, this, (void (KisDoubleParseUnitSpinBox::*)( QString const& )) &KisDoubleParseUnitSpinBox::internalUnitChange); d->unitManager = unitManager; connect(d->unitManager, &QObject::destroyed, this, &KisDoubleParseUnitSpinBox::disconnectExternalUnitManager); connect(d->unitManager, (void (KisSpinBoxUnitManager::*)()) &KisSpinBoxUnitManager::unitAboutToChange, this, (void (KisDoubleParseUnitSpinBox::*)()) &KisDoubleParseUnitSpinBox::prepareUnitChange); connect(d->unitManager, (void (KisSpinBoxUnitManager::*)( QString )) &KisSpinBoxUnitManager::unitChanged, this, (void (KisDoubleParseUnitSpinBox::*)( QString const& )) &KisDoubleParseUnitSpinBox::internalUnitChange); KisDoubleParseSpinBox::setValue(newVal); + + if (d->allowResetDecimals) { //if the user has not fixed the number of decimals. + setDecimals(d->unitManager->getApparentUnitRecommandedDecimals()); + } } void KisDoubleParseUnitSpinBox::changeValue( double newValue ) { double apparentValue; double fact; double cons; if (d->outPutSymbol.isEmpty()) { apparentValue = d->unitManager->getApparentValue(newValue); } else { fact = d->unitManager->getConversionFactor(d->unitManager->getUnitDimensionType(), d->outPutSymbol); cons = d->unitManager->getConversionConstant(d->unitManager->getUnitDimensionType(), d->outPutSymbol); apparentValue = fact*newValue + cons; } if (apparentValue == KisDoubleParseSpinBox::value()) { return; } if (d->outPutSymbol.isEmpty()) { KisDoubleParseSpinBox::setValue( d->unitManager->getApparentValue(newValue) ); } else { KisDoubleParseSpinBox::setValue( d->unitManager->getApparentValue((newValue - cons)/fact) ); } } void KisDoubleParseUnitSpinBox::setUnit( const KoUnit & unit) { if (d->unitHasBeenChangedFromOutSideOnce && !d->letUnitBeChangedFromOutsideMoreThanOnce) { return; } if (d->unitManager->getUnitDimensionType() != KisSpinBoxUnitManager::LENGTH) { d->unitManager->setUnitDimension(KisSpinBoxUnitManager::LENGTH); //setting the unit using a KoUnit mean you want to use a length. } setUnit(unit.symbol()); d->unit = unit; } void KisDoubleParseUnitSpinBox::setUnit(const QString &symbol) { d->unitManager->setApparentUnitFromSymbol(symbol); //via signals and slots, the correct functions should be called. } void KisDoubleParseUnitSpinBox::setReturnUnit(const QString & symbol) { d->outPutSymbol = symbol; } void KisDoubleParseUnitSpinBox::prepareUnitChange() { d->previousValueInPoint = d->unitManager->getReferenceValue(KisDoubleParseSpinBox::value()); d->previousSymbol = d->unitManager->getApparentUnitSymbol(); } void KisDoubleParseUnitSpinBox::internalUnitChange(const QString &symbol) { //d->unitManager->setApparentUnitFromSymbol(symbol); if (d->unitManager->getApparentUnitSymbol() == d->previousSymbol) { //the setApparentUnitFromSymbol is a bit clever, for example in regard of Casesensitivity. So better check like this. return; } KisDoubleParseSpinBox::setMinimum( d->unitManager->getApparentValue( d->lowerInPoints ) ); KisDoubleParseSpinBox::setMaximum( d->unitManager->getApparentValue( d->upperInPoints ) ); qreal step = d->unitManager->getApparentValue( d->stepInPoints ); if (symbol == KoUnit(KoUnit::Pixel).symbol()) { // limit the pixel step by 1.0 step = qMax(qreal(1.0), step); } KisDoubleParseSpinBox::setSingleStep( step ); KisDoubleParseSpinBox::setValue( d->unitManager->getApparentValue( d->previousValueInPoint ) ); + if (d->allowResetDecimals) { + setDecimals(d->unitManager->getApparentUnitRecommandedDecimals()); + } + d->unitHasBeenChangedFromOutSideOnce = true; } void KisDoubleParseUnitSpinBox::setDimensionType(int dim) { if (!KisSpinBoxUnitManager::isUnitId(dim)) { return; } d->unitManager->setUnitDimension((KisSpinBoxUnitManager::UnitDimension) dim); } double KisDoubleParseUnitSpinBox::value( ) const { if (d->outPutSymbol.isEmpty()) { return d->unitManager->getReferenceValue( KisDoubleParseSpinBox::value() ); } double ref = d->unitManager->getReferenceValue( KisDoubleParseSpinBox::value() ); double fact = d->unitManager->getConversionFactor(d->unitManager->getUnitDimensionType(), d->outPutSymbol); double cons = d->unitManager->getConversionConstant(d->unitManager->getUnitDimensionType(), d->outPutSymbol); return fact*ref + cons; } void KisDoubleParseUnitSpinBox::setMinimum(double min) { d->lowerInPoints = min; KisDoubleParseSpinBox::setMinimum( d->unitManager->getApparentValue( min ) ); } void KisDoubleParseUnitSpinBox::setMaximum(double max) { d->upperInPoints = max; KisDoubleParseSpinBox::setMaximum( d->unitManager->getApparentValue( max ) ); } void KisDoubleParseUnitSpinBox::setLineStep(double step) { d->stepInPoints = d->unitManager->getReferenceValue(step); KisDoubleParseSpinBox::setSingleStep( step ); } void KisDoubleParseUnitSpinBox::setLineStepPt(double step) { d->stepInPoints = step; KisDoubleParseSpinBox::setSingleStep( d->unitManager->getApparentValue( step ) ); } void KisDoubleParseUnitSpinBox::setMinMaxStep( double min, double max, double step ) { setMinimum( min ); setMaximum( max ); setLineStepPt( step ); } QValidator::State KisDoubleParseUnitSpinBox::validate(QString &input, int &pos) const { Q_UNUSED(pos); QRegExp regexp ("([ a-zA-Z]+)$"); // Letters or spaces at end const int res = input.indexOf( regexp ); /*if ( res == -1 ) { // Nothing like an unit? The user is probably editing the unit return QValidator::Intermediate; }*/ QString expr ( (res > 0) ? input.left( res ) : input ); const QString unitName ( (res > 0) ? regexp.cap( 1 ).trimmed().toLower() : "" ); bool ok = true; bool interm = false; QValidator::State exprState = KisDoubleParseSpinBox::validate(expr, pos); if (res < 0) { return exprState; } if (exprState == QValidator::Invalid) { return exprState; } else if (exprState == QValidator::Intermediate) { interm = true; } //check if we can parse the unit. QStringList listOfSymbol = d->unitManager->getsUnitSymbolList(); ok = listOfSymbol.contains(unitName); if (!ok || interm) { return QValidator::Intermediate; } return QValidator::Acceptable; } QString KisDoubleParseUnitSpinBox::textFromValue( double value ) const { QString txt = KisDoubleParseSpinBox::textFromValue(value); if (d->displayUnit) { if (!txt.endsWith(d->unitManager->getApparentUnitSymbol())) { txt += " " + d->unitManager->getApparentUnitSymbol(); } } return txt; } QString KisDoubleParseUnitSpinBox::veryCleanText() const { return makeTextClean(cleanText()); } double KisDoubleParseUnitSpinBox::valueFromText( const QString& str ) const { QString txt = makeTextClean(str); return KisDoubleParseSpinBox::valueFromText(txt); //this function will take care of prefix (and don't mind if suffix has been removed. } void KisDoubleParseUnitSpinBox::setUnitChangeFromOutsideBehavior(bool toggle) { d->letUnitBeChangedFromOutsideMoreThanOnce = toggle; } void KisDoubleParseUnitSpinBox::setDisplayUnit(bool toggle) { d->displayUnit = toggle; } +void KisDoubleParseUnitSpinBox::preventDecimalsChangeFromUnitManager(bool prevent) { + d->allowResetDecimals = !prevent; +} + void KisDoubleParseUnitSpinBox::privateValueChanged() { emit valueChangedPt( value() ); } QString KisDoubleParseUnitSpinBox::detectUnit() { QString str = veryCleanText().trimmed(); //text with the new unit but not the old one. QRegExp regexp ("([ ]*[a-zA-Z]+[ ]*)$"); // Letters or spaces at end int res = str.indexOf( regexp ); if (res > -1) { QString expr ( str.right( str.size() - res ) ); expr = expr.trimmed(); return expr; } return ""; } void KisDoubleParseUnitSpinBox::detectUnitChanges() { QString unitSymb = detectUnit(); if (unitSymb.isEmpty()) { return; } QString oldUnitSymb = d->unitManager->getApparentUnitSymbol(); setUnit(unitSymb); setValue(valueFromText(cleanText())); //change value keep the old value, but converted to new unit... which is different from the value the user entered in the new unit. So we need to set the new value. if (oldUnitSymb != d->unitManager->getApparentUnitSymbol()) { // the user has changed the unit, so we block changes from outside. setUnitChangeFromOutsideBehavior(false); } } QString KisDoubleParseUnitSpinBox::makeTextClean(QString const& txt) const { QString expr = txt; QString symbol = d->unitManager->getApparentUnitSymbol(); if ( expr.endsWith(suffix()) ) { expr.remove(expr.size()-suffix().size(), suffix().size()); } expr = expr.trimmed(); if ( expr.endsWith(symbol) ) { expr.remove(expr.size()-symbol.size(), symbol.size()); } return expr.trimmed(); } void KisDoubleParseUnitSpinBox::disconnectExternalUnitManager() { if (!d->isDeleting) { setUnitManager(d->defaultUnitManager); //go back to default unit manager. } } diff --git a/libs/widgets/kis_double_parse_unit_spin_box.h b/libs/widgets/kis_double_parse_unit_spin_box.h index 8ece97710c..c47bfc9e0e 100644 --- a/libs/widgets/kis_double_parse_unit_spin_box.h +++ b/libs/widgets/kis_double_parse_unit_spin_box.h @@ -1,137 +1,139 @@ /* * Copyright (c) 2016 Laurent Valentin Jospin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_DOUBLEPARSEUNITSPINBOX_H #define KIS_DOUBLEPARSEUNITSPINBOX_H #include #include "kis_double_parse_spin_box.h" #include "kritawidgets_export.h" class KisSpinBoxUnitManager; /*! * \brief The KisDoubleParseUnitSpinBox class is an evolution of the \see KoUnitDoubleSpinBox, but inherit from \see KisDoubleParseSpinBox to be able to parse math expressions. * * This class store the */ class KRITAWIDGETS_EXPORT KisDoubleParseUnitSpinBox : public KisDoubleParseSpinBox { Q_OBJECT public: KisDoubleParseUnitSpinBox(QWidget* parent = 0); ~KisDoubleParseUnitSpinBox() override; void setUnitManager(KisSpinBoxUnitManager* unitManager); /** * Set the new value in points (or other reference unit) which will then be converted to the current unit for display * @param newValue the new value * @see value() */ virtual void changeValue( double newValue ); /** * This spinbox shows the internal value after a conversion to the unit set here. */ virtual void setUnit(const KoUnit &unit); virtual void setUnit(const QString & symbol); /*! * \brief setReturnUnit set a unit, such that the spinbox now return values in this unit instead of the reference unit for the current dimension. * \param symbol the symbol of the new unit. */ void setReturnUnit(const QString & symbol); /** * @brief setDimensionType set the dimension (for example length or angle) of the units the spinbox manage * @param dim the dimension id. (if not an id in KisSpinBoxUnitManager::UnitDimension, then the function does nothing). */ virtual void setDimensionType(int dim); /// @return the current value, converted in points double value( ) const; /// Set minimum value in points. void setMinimum(double min); /// Set maximum value in points. void setMaximum(double max); /// Set step size in the current unit. void setLineStep(double step); /// Set step size in points. void setLineStepPt(double step); /// Set minimum, maximum value and the step size (all in points) void setMinMaxStep( double min, double max, double step ); /// reimplemented from superclass, will forward to KoUnitDoubleValidator QValidator::State validate(QString &input, int &pos) const override; /** * Transform the double in a nice text, using locale symbols * @param value the number as double * @return the resulting string */ QString textFromValue( double value ) const override; //! \brief get the text in the spinbox without prefix or suffix, and remove unit symbol if present. QString veryCleanText() const override; /** * Transfrom a string into a double, while taking care of locale specific symbols. * @param str the string to transform into a number * @return the value as double */ double valueFromText( const QString& str ) const override; void setUnitChangeFromOutsideBehavior(bool toggle); //if set to false, setting the unit using KoUnit won't have any effect. //! \brief display the unit symbol in the spinbox or not. For example if the unit is displayed in a combobox connected to the unit manager. void setDisplayUnit(bool toggle); + void preventDecimalsChangeFromUnitManager(bool prevent); + Q_SIGNALS: /// emitted like valueChanged in the parent, but this one emits the point value, or converted to another reference unit. void valueChangedPt( qreal ); private: class Private; Private * const d; QString detectUnit(); QString makeTextClean(QString const& txt) const; //thoses functions are usefull to sync the spinbox with it's unitmanager. //! \brief change the unit, reset the spin box everytime. From the outside it's alway set unit that should be called. void internalUnitChange(QString const& symbol); void prepareUnitChange(); private Q_SLOTS: // exists to do emits for valueChangedPt void privateValueChanged(); void detectUnitChanges(); void disconnectExternalUnitManager(); }; #endif // KIS_DOUBLEPARSEUNITSPINBOX_H diff --git a/libs/widgetutils/kis_spin_box_unit_manager.cpp b/libs/widgetutils/kis_spin_box_unit_manager.cpp index acae8ac490..39ab9da463 100644 --- a/libs/widgetutils/kis_spin_box_unit_manager.cpp +++ b/libs/widgetutils/kis_spin_box_unit_manager.cpp @@ -1,524 +1,629 @@ /* * Copyright (c) 2017 Laurent Valentin Jospin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_spin_box_unit_manager.h" #include "KoUnit.h" #include #include KisSpinBoxUnitManagerBuilder* KisSpinBoxUnitManagerFactory::builder = nullptr; KisSpinBoxUnitManager* KisSpinBoxUnitManagerFactory::buildDefaultUnitManager(QObject* parent) { if (builder == nullptr) { return new KisSpinBoxUnitManager(parent); } return builder->buildUnitManager(parent); } void KisSpinBoxUnitManagerFactory::setDefaultUnitManagerBuilder(KisSpinBoxUnitManagerBuilder* pBuilder) { if (builder != nullptr) { delete builder; //The factory took over the lifecycle of the builder, so it delete it when replaced. } builder = pBuilder; } void KisSpinBoxUnitManagerFactory::clearUnitManagerBuilder() { if (builder != nullptr) { delete builder; //The factory took over the lifecycle of the builder, so it delete it when replaced. } builder = nullptr; } const QStringList KisSpinBoxUnitManager::referenceUnitSymbols = {"pt", "px", "°", "frame"}; const QStringList KisSpinBoxUnitManager::documentRelativeLengthUnitSymbols = {"px", "vw", "vh"}; //px are relative to the resolution, vw and vh to the width and height. const QStringList KisSpinBoxUnitManager::documentRelativeTimeUnitSymbols = {"s", "%"}; //secondes are relative to the framerate, % to the sequence length. class Q_DECL_HIDDEN KisSpinBoxUnitManager::Private { public: Private(KisSpinBoxUnitManager::UnitDimension pDim = KisSpinBoxUnitManager::LENGTH, QString pUnitSymbol = "pt", double pConv = 1.0): dim(pDim), unitSymbol(pUnitSymbol), conversionFactor(pConv), conversionFactorIsFixed(true), conversionConstant(0), conversionConstantIsFixed(true), constrains(0), unitListCached(false), + unitListWithNameCached(false), hasHundredPercent(false), canAccessDocument(false) { } KisSpinBoxUnitManager::UnitDimension dim; QString unitSymbol; mutable double conversionFactor; bool conversionFactorIsFixed; //tell if it's possible to trust the conversion factor stored or if it's needed to recompute it. mutable double conversionConstant; bool conversionConstantIsFixed; //tell if it's possible to trust the conversion constant stored or if it's needed to recompute it. KisSpinBoxUnitManager::Constrains constrains; mutable QStringList unitList; mutable bool unitListCached; mutable QStringList unitListWithName; mutable bool unitListWithNameCached; //it's possible to store a reference for the % unit, for lenght. bool hasHundredPercent; qreal hundredPercent; bool canAccessDocument; + + QVector connectedUnitManagers; }; KisSpinBoxUnitManager::KisSpinBoxUnitManager(QObject *parent) : QAbstractListModel(parent) { d = new Private(); connect(this, (void (KisSpinBoxUnitManager::*)( QString )) &KisSpinBoxUnitManager::unitChanged, this, &KisSpinBoxUnitManager::newUnitSymbolToUnitIndex); } KisSpinBoxUnitManager::~KisSpinBoxUnitManager() { delete d; } int KisSpinBoxUnitManager::getUnitDimensionType() const { return d->dim; } QString KisSpinBoxUnitManager::getReferenceUnitSymbol() const { return referenceUnitSymbols[d->dim]; } QString KisSpinBoxUnitManager::getApparentUnitSymbol() const { return d->unitSymbol; } int KisSpinBoxUnitManager::getApparentUnitId() const { QStringList list = getsUnitSymbolList(); return list.indexOf(d->unitSymbol); } +int KisSpinBoxUnitManager::getApparentUnitRecommandedDecimals() const { + + switch (d->dim) { + + case LENGTH: + if (d->unitSymbol == "px") { + return 0; + } else { + return 2; + } + case IMLENGTH: + if (d->unitSymbol == "px") { + return 0; + } else { + return 2; + } + default: //by default return 2. + break; + } + + return 2; + +} + QStringList KisSpinBoxUnitManager::getsUnitSymbolList(bool withName) const{ QStringList list; if (withName) { if (d->unitListWithNameCached) { return d->unitListWithName; } } else { if (d->unitListCached) { return d->unitList; } } switch (d->dim) { case LENGTH: for (int i = 0; i < KoUnit::TypeCount; i++) { if (KoUnit::Type(i) == KoUnit::Pixel) { continue; //skip pixel, which is a document relative unit, in the base classe. } if (withName) { list << KoUnit::unitDescription(KoUnit::Type(i)); } else { list << KoUnit(KoUnit::Type(i)).symbol(); } } + if (hasPercent(LENGTH)) { + + if (withName) { + list << i18n("percent (%)"); + } else { + list << "%"; + } + + } + if (d->canAccessDocument) { // ad document relative units if (withName) { - list << KoUnit::unitDescription(KoUnit::Pixel) << i18n("view width (vw)") << i18n("view height (vh)"); + list << KoUnit::unitDescription(KoUnit::Pixel) << i18n("percent of view width (vw)") << i18n("percent of view height (vh)"); } else { list << documentRelativeLengthUnitSymbols; } } break; case IMLENGTH: if (withName) { list << KoUnit::unitDescription(KoUnit::Pixel); } else { list << "px"; } + if (hasPercent(IMLENGTH)) { + + if (withName) { + list << i18n("percent (%)"); + } else { + list << "%"; + } + + } + if (d->canAccessDocument) { // ad document relative units if (withName) { - list << i18n("view width (vw)") << i18n("view height (vh)"); + list << i18n("percent of view width (vw)") << i18n("percent of view height (vh)"); } else { list << "vw" << "vh"; } } break; case ANGLE: if (withName) { list << i18n("degrees (°)") << i18n("radians (rad)") << i18n("gons (gon)") << i18n("percent of circle (%)"); } else { list << "°" << "rad" << "gon" << "%"; } break; case TIME: if (withName) { list << i18n("frames (f)"); } else { list << "f"; } if (d->canAccessDocument) { if (withName) { list << i18n("seconds (s)") << i18n("percent of animation (%)"); } else { list << documentRelativeTimeUnitSymbols; } } break; } if (withName) { d->unitListWithName = list; d->unitListWithNameCached = true; } else { d->unitList = list; d->unitListCached = true; } return list; } qreal KisSpinBoxUnitManager::getConversionConstant(int dim, QString symbol) const { Q_UNUSED(dim); Q_UNUSED(symbol); return 0; // all units managed here are transform via a linear function, so this wll alway be 0 in this class. } qreal KisSpinBoxUnitManager::getReferenceValue(double apparentValue) const { if (!d->conversionFactorIsFixed) { recomputeConversionFactor(); } if(!d->conversionConstantIsFixed) { recomputeConvesrionConstant(); } qreal v = (apparentValue - d->conversionConstant)/d->conversionFactor; if (d->constrains &= REFISINT) { v = qFloor(v); } return v; } int KisSpinBoxUnitManager::rowCount(const QModelIndex &parent) const { if (parent == QModelIndex()) { return getsUnitSymbolList().size(); } return 0; } QVariant KisSpinBoxUnitManager::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole) { + return getsUnitSymbolList(false).at(index.row()); + + } else if (role == Qt::ToolTipRole) { + + return getsUnitSymbolList(true).at(index.row()); + } return QVariant(); } qreal KisSpinBoxUnitManager::getApparentValue(double refValue) const { if (!d->conversionFactorIsFixed) { recomputeConversionFactor(); } if(!d->conversionConstantIsFixed) { recomputeConvesrionConstant(); } qreal v = refValue*d->conversionFactor + d->conversionConstant; if (d->constrains &= VALISINT) { v = qFloor(v); } return v; } qreal KisSpinBoxUnitManager::getConversionFactor(int dim, QString symbol) const { qreal factor = -1; switch (dim) { case LENGTH: do { if (symbol == "px") { break; } bool ok; KoUnit unit = KoUnit::fromSymbol(symbol, &ok); if (! ok) { break; } factor = unit.toUserValue(1.0); } while (0) ; break; case IMLENGTH: if (symbol == "px") { factor = 1; } break; case ANGLE: if (symbol == "°") { factor = 1.0; break; } if (symbol == "rad") { factor = acos(-1)/90.0; break; } if (symbol == "gon") { factor = 10.0/9.0; break; } if (symbol == "%") { factor = 2.5/9.0; //(25% of circle is 90°) break; } break; case TIME: if (symbol != "f") { //we have only frames for the moment. break; } factor = 1.0; break; default: break; } return factor; } void KisSpinBoxUnitManager::setUnitDimension(UnitDimension dimension) { if (dimension == d->dim) { return; } d->dim = dimension; d->unitSymbol = referenceUnitSymbols[d->dim]; //Active dim is reference dim when just changed. d->conversionFactor = 1.0; emit unitDimensionChanged(d->dim); } void KisSpinBoxUnitManager::setApparentUnitFromSymbol(QString pSymbol) { QString symbol = pSymbol.trimmed(); if (symbol == d->unitSymbol) { return; } emit unitAboutToChange(); QString newSymb = ""; switch (d->dim) { case ANGLE: if (symbol.toLower() == "deg") { newSymb = "°"; break; } goto default_indentifier; //alway do default after handling possible special cases. default_indentifier: default: QStringList list = getsUnitSymbolList(); if (list.contains(symbol, Qt::CaseInsensitive)) { for (QString str : list) { if (str.toLower() == symbol.toLower()) { newSymb = str; //official symbol may contain capitals letters, so better take the official version. break; } } break; } } if(newSymb.isEmpty()) { return; //abort if it was impossible to locate the correct symbol. } if (d->canAccessDocument) { //manage document relative units. QStringList speUnits; switch (d->dim) { case LENGTH: speUnits = documentRelativeLengthUnitSymbols; goto default_identifier_conv_fact; case IMLENGTH: speUnits << "vw" << "vh"; goto default_identifier_conv_fact; case TIME: speUnits = documentRelativeTimeUnitSymbols; goto default_identifier_conv_fact; default_identifier_conv_fact: default: if (speUnits.isEmpty()) { d->conversionFactorIsFixed = true; break; } if (speUnits.contains(newSymb)) { d->conversionFactorIsFixed = false; break; } d->conversionFactorIsFixed = true; break; } if (d->dim == TIME) { if (newSymb == "%") { d->conversionConstantIsFixed = false; } } else { d->conversionConstantIsFixed = true; } } qreal conversFact = getConversionFactor(d->dim, newSymb); qreal oldConversFact = d->conversionFactor; d->conversionFactor = conversFact; emit conversionFactorChanged(d->conversionFactor, oldConversFact); d->unitSymbol = newSymb; emit unitChanged(newSymb); } void KisSpinBoxUnitManager::selectApparentUnitFromIndex(int index) { if (index >= 0 && index < rowCount()) { setApparentUnitFromSymbol(getsUnitSymbolList().at(index)); } } + +void KisSpinBoxUnitManager::syncWithOtherUnitManager(KisSpinBoxUnitManager* other) { + + if (d->connectedUnitManagers.indexOf(other) >= 0) { + return; + } + + if (other->getUnitDimensionType() == getUnitDimensionType()) { //sync only unitmanager of the same type. + if (other->getsUnitSymbolList() == getsUnitSymbolList()) { //and if we have identical units available. + + connect(this, SIGNAL(unitChanged(int)), other, SLOT(selectApparentUnitFromIndex(int))); //sync units. + connect(other, SIGNAL(unitChanged(int)), this, SLOT(selectApparentUnitFromIndex(int))); //sync units. + + d->connectedUnitManagers.append(other); + + } + } + +} + +void KisSpinBoxUnitManager::clearSyncWithOtherUnitManager(KisSpinBoxUnitManager* other) { + + int id = d->connectedUnitManagers.indexOf(other); + + if (id < 0) { + return; + } + + disconnect(this, SIGNAL(unitChanged(int)), other, SLOT(selectApparentUnitFromIndex(int))); //unsync units. + disconnect(other, SIGNAL(unitChanged(int)), this, SLOT(selectApparentUnitFromIndex(int))); //unsync units. + + d->connectedUnitManagers.removeAt(id); + +} + void KisSpinBoxUnitManager::newUnitSymbolToUnitIndex(QString symbol) { int id = getsUnitSymbolList().indexOf(symbol); if (id >= 0) { emit unitChanged(id); } } +bool KisSpinBoxUnitManager::hasPercent(int unitDim) const { + + if (unitDim == IMLENGTH || unitDim == LENGTH) { + return false; + } + + if (unitDim == TIME) { + return d->canAccessDocument; + } + + if (unitDim == ANGLE) { + return true; //percent is fixed when considering angles. + } + + return false; +} + void KisSpinBoxUnitManager::recomputeConversionFactor() const { if (d->conversionFactorIsFixed) { return; } qreal oldConversionFactor = d->conversionFactor; d->conversionFactor = getConversionFactor(d->dim, d->unitSymbol); if (oldConversionFactor != d->conversionFactor) { emit conversionFactorChanged(d->conversionFactor, oldConversionFactor); } } void KisSpinBoxUnitManager::recomputeConvesrionConstant() const { if (d->conversionConstantIsFixed) { return; } qreal oldConversionConstant = d->conversionConstant; d->conversionConstant = getConversionConstant(d->dim, d->unitSymbol); if (oldConversionConstant != d->conversionConstant) { emit conversionConstantChanged(d->conversionConstant, oldConversionConstant); } } void KisSpinBoxUnitManager::grantDocumentRelativeUnits() { d->canAccessDocument = true; } diff --git a/libs/widgetutils/kis_spin_box_unit_manager.h b/libs/widgetutils/kis_spin_box_unit_manager.h index 486578f5e8..7e3f308576 100644 --- a/libs/widgetutils/kis_spin_box_unit_manager.h +++ b/libs/widgetutils/kis_spin_box_unit_manager.h @@ -1,164 +1,173 @@ /* * Copyright (c) 2017 Laurent Valentin Jospin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISSPINBOXUNITMANAGER_H #define KISSPINBOXUNITMANAGER_H #include #include #include #include "kritawidgetutils_export.h" class KisSpinBoxUnitManager; class KisSpinBoxUnitManagerBuilder; class KisSpinBoxUnitManagerFactory; /*! * \brief The KisSpinBoxUnitManagerFactory class is a factory that is used to build a default KisSpinBoxUnitManager. * \see KisSpinBoxUnitManagerBuilder */ class KRITAWIDGETUTILS_EXPORT KisSpinBoxUnitManagerFactory { public: static KisSpinBoxUnitManager* buildDefaultUnitManager(QObject* parent); //! \brief set a builder the factory can use. The factory should take on the lifecycle of the builder, so to delete it call clearUnitManagerBuilder(); static void setDefaultUnitManagerBuilder(KisSpinBoxUnitManagerBuilder* pBuilder); static void clearUnitManagerBuilder(); private: static KisSpinBoxUnitManagerBuilder* builder; }; /*! * \brief The KisSpinBoxUnitManagerBuilder class is the base class, used in the strategy pattern of KisSpinBoxUnitManagerFactory. * \see KisSpinBoxUnitManagerFactory. */ class KRITAWIDGETUTILS_EXPORT KisSpinBoxUnitManagerBuilder { public: virtual ~KisSpinBoxUnitManagerBuilder() {} virtual KisSpinBoxUnitManager* buildUnitManager(QObject* parent) = 0; //this pure virtual function is used to build a unitmanager, it will be used by the unitManagerFactory. }; /** * @brief The KisSpinBoxUnitManager class is an abstract interface for the unitspinboxes classes to manage different type of units. * * The class make a difference between unit dimension (distance, angle, time). * * The class allow to convert values between reference unit and apparent unit, but also to get other informations like possible units symbols. * * This class don't allow to use relative units (units which conversion factor is dependant of the context), even if its private data are prepared to manage it. * The reason for this is that from the library of this class it is very hard to acess easily the informations needed. So all will be managed by subclasses in other libs. * * The class is a subclass of QAbstractListModel, so that available list of units is easily acessed by other Qt standard components, like QComboBoxes. * */ class KRITAWIDGETUTILS_EXPORT KisSpinBoxUnitManager : public QAbstractListModel { Q_OBJECT public: enum UnitDimension{ LENGTH = 0, //length, print size, reference is point IMLENGTH = 1, //length, image size, reference is pixel. This dimension is used when the printing units must be avoided ANGLE = 2, TIME = 3 }; static inline bool isUnitId(int code) { return (code == LENGTH || code == ANGLE || code == TIME); } //! \brief this list hold the symbols of the referenc unit per dimension. The index is equal to the value in UnitDimension so that the dimension name can be used to index the list. static const QStringList referenceUnitSymbols; enum Constrain{ NOCONSTR = 0, REFISINT = 1, VALISINT = 2 }; Q_DECLARE_FLAGS(Constrains, Constrain) explicit KisSpinBoxUnitManager(QObject *parent = 0); ~KisSpinBoxUnitManager() override; int getUnitDimensionType() const; QString getReferenceUnitSymbol() const; QString getApparentUnitSymbol() const; //! \brief get the position of the apparent unit in the list of units. It is usefull if we want to build a model for combo-box based unit management. int getApparentUnitId() const; + //! \brief get a hint of how many decimals the spinbox need to display. + int getApparentUnitRecommandedDecimals() const; + virtual QStringList getsUnitSymbolList(bool withName = false) const; qreal getReferenceValue(double apparentValue) const; qreal getApparentValue(double refValue) const; //! \brief gets the conversion factor of a managed unit, or -1 in case of error. This method is the one that need to be overridden to extend the ability of the KisSpinBoxUnitManager. virtual qreal getConversionFactor(int dim, QString symbol) const; //! \brief some units conversions are done via an affine transform, not just a linear transform. This function gives the constant of this affine transform (usually 0). virtual qreal getConversionConstant(int dim, QString symbol) const; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Q_SIGNALS: void unitDimensionChanged(int dimCode); void unitAboutToChange(); void unitChanged(QString symbol); void unitChanged(int index); void conversionFactorChanged(qreal newConversionFactor, qreal oldConversionFactor) const; void conversionConstantChanged(qreal newConversionFactor, qreal oldConversionFactor) const; void unitListChanged(); public Q_SLOTS: void setUnitDimension(UnitDimension dimension); void setApparentUnitFromSymbol(QString pSymbol); void selectApparentUnitFromIndex(int index); + void syncWithOtherUnitManager(KisSpinBoxUnitManager* other); + void clearSyncWithOtherUnitManager(KisSpinBoxUnitManager* other); + protected: class Private; Private * d; //! \brief convert a unitChanged signal with a QString to one with an index. void newUnitSymbolToUnitIndex(QString symbol); + //! \brief indicate if the unit manager has some kind of way of using a percent unit, used by the main class to add percent when necessary. + virtual bool hasPercent(int unitDim) const; + //unit's that may be used only if acess to the document informations exists. static const QStringList documentRelativeLengthUnitSymbols; static const QStringList documentRelativeTimeUnitSymbols; void recomputeConversionFactor() const; void recomputeConvesrionConstant() const; //! \brief calling this method give acess to document relative units. Only subclasses that manage thoses units should call it. void grantDocumentRelativeUnits(); }; #endif // KISSPINBOXUNITMANAGER_H diff --git a/plugins/extensions/imagesize/dlg_canvassize.cc b/plugins/extensions/imagesize/dlg_canvassize.cc index b394a041d9..1715fa0669 100644 --- a/plugins/extensions/imagesize/dlg_canvassize.cc +++ b/plugins/extensions/imagesize/dlg_canvassize.cc @@ -1,469 +1,499 @@ /* * * Copyright (c) 2009 Edward Apap * Copyright (c) 2013 Juan Palacios * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dlg_canvassize.h" #include "kcanvaspreview.h" +#include #include #include #include #include #include #include #include // used to extend KoUnit in comboboxes static const QString percentStr(i18n("Percent (%)")); +const QString DlgCanvasSize::PARAM_PREFIX = "canvasizedlg"; +const QString DlgCanvasSize::PARAM_WIDTH_UNIT = DlgCanvasSize::PARAM_PREFIX + "_widthunit"; +const QString DlgCanvasSize::PARAM_HEIGTH_UNIT = DlgCanvasSize::PARAM_PREFIX + "_heightunit"; +const QString DlgCanvasSize::PARAM_XOFFSET_UNIT = DlgCanvasSize::PARAM_PREFIX + "_xoffsetunit"; +const QString DlgCanvasSize::PARAM_YOFFSET_UNIT = DlgCanvasSize::PARAM_PREFIX + "_yoffsetunit"; + DlgCanvasSize::DlgCanvasSize(QWidget *parent, int width, int height, double resolution) : KoDialog(parent) , m_keepAspect(true) , m_aspectRatio((double)width / height) , m_resolution(resolution) , m_originalWidth(width) , m_originalHeight(height) , m_newWidth(width) , m_newHeight(height) , m_xOffset(0) , m_yOffset(0) { setCaption(i18n("Resize Canvas")); setButtons(Ok | Cancel); setDefaultButton(Ok); m_page = new WdgCanvasSize(this); Q_CHECK_PTR(m_page); m_page->layout()->setMargin(0); m_page->setObjectName("canvas_size"); _widthUnitManager = new KisDocumentAwareSpinBoxUnitManager(this); _heightUnitManager = new KisDocumentAwareSpinBoxUnitManager(this, KisDocumentAwareSpinBoxUnitManager::PIX_DIR_Y); + KisConfig cfg; + _widthUnitManager->setApparentUnitFromSymbol("px"); _heightUnitManager->setApparentUnitFromSymbol("px"); m_page->newWidthDouble->setUnitManager(_widthUnitManager); m_page->newHeightDouble->setUnitManager(_heightUnitManager); m_page->newWidthDouble->setDecimals(2); m_page->newHeightDouble->setDecimals(2); m_page->newWidthDouble->setDisplayUnit(false); m_page->newHeightDouble->setDisplayUnit(false); m_page->newWidthDouble->setValue(width); m_page->newWidthDouble->setFocus(); m_page->newHeightDouble->setValue(height); m_page->widthUnit->setModel(_widthUnitManager); m_page->heightUnit->setModel(_heightUnitManager); - const int pixelUnitIndex = _widthUnitManager->getsUnitSymbolList().indexOf("px"); //TODO: have a better way to identify units. - m_page->widthUnit->setCurrentIndex(pixelUnitIndex); - m_page->heightUnit->setCurrentIndex(pixelUnitIndex); + QString unitw = cfg.readEntry(PARAM_WIDTH_UNIT, "px"); + QString unith = cfg.readEntry(PARAM_HEIGTH_UNIT, "px"); + + _widthUnitManager->setApparentUnitFromSymbol(unitw); + _heightUnitManager->setApparentUnitFromSymbol(unith); + + const int wUnitIndex = _widthUnitManager->getsUnitSymbolList().indexOf(unitw); + const int hUnitIndex = _widthUnitManager->getsUnitSymbolList().indexOf(unith); + + m_page->widthUnit->setCurrentIndex(wUnitIndex); + m_page->heightUnit->setCurrentIndex(hUnitIndex); _xOffsetUnitManager = new KisDocumentAwareSpinBoxUnitManager(this); _yOffsetUnitManager = new KisDocumentAwareSpinBoxUnitManager(this, KisDocumentAwareSpinBoxUnitManager::PIX_DIR_Y); _xOffsetUnitManager->setApparentUnitFromSymbol("px"); _yOffsetUnitManager->setApparentUnitFromSymbol("px"); m_page->xOffsetDouble->setUnitManager(_xOffsetUnitManager); m_page->yOffsetDouble->setUnitManager(_yOffsetUnitManager); m_page->xOffsetDouble->setDecimals(2); m_page->yOffsetDouble->setDecimals(2); m_page->xOffsetDouble->setDisplayUnit(false); m_page->yOffsetDouble->setDisplayUnit(false); m_page->xOffUnit->setModel(_xOffsetUnitManager); m_page->yOffUnit->setModel(_yOffsetUnitManager); - m_page->xOffUnit->setCurrentIndex(pixelUnitIndex); - m_page->yOffUnit->setCurrentIndex(pixelUnitIndex); + m_page->xOffsetDouble->changeValue(m_xOffset); + m_page->yOffsetDouble->changeValue(m_yOffset); + + QString unitx = cfg.readEntry(PARAM_XOFFSET_UNIT, "px"); + QString unity = cfg.readEntry(PARAM_YOFFSET_UNIT, "px"); + + _xOffsetUnitManager->setApparentUnitFromSymbol(unitx); + _yOffsetUnitManager->setApparentUnitFromSymbol(unity); + + const int xUnitIndex = _xOffsetUnitManager->getsUnitSymbolList().indexOf(unitx); + const int yUnitIndex = _yOffsetUnitManager->getsUnitSymbolList().indexOf(unity); + + m_page->xOffUnit->setCurrentIndex(xUnitIndex); + m_page->yOffUnit->setCurrentIndex(yUnitIndex); m_page->canvasPreview->setImageSize(m_originalWidth, m_originalHeight); m_page->canvasPreview->setCanvasSize(m_originalWidth, m_originalHeight); m_page->canvasPreview->setImageOffset(m_xOffset, m_yOffset); - m_page->xOffsetDouble->changeValue(m_xOffset); - m_page->yOffsetDouble->changeValue(m_yOffset); - - KisConfig cfg; - m_page->aspectRatioBtn->setKeepAspectRatio(cfg.readEntry("CanvasSize/KeepAspectRatio", false)); m_page->constrainProportionsCkb->setChecked(cfg.readEntry("CanvasSize/ConstrainProportions", false)); m_keepAspect = cfg.readEntry("CanvasSize/KeepAspectRatio", false); m_group = new QButtonGroup(m_page); m_group->addButton(m_page->topLeft, NORTH_WEST); m_group->addButton(m_page->topCenter, NORTH); m_group->addButton(m_page->topRight, NORTH_EAST); m_group->addButton(m_page->middleLeft, WEST); m_group->addButton(m_page->middleCenter, CENTER); m_group->addButton(m_page->middleRight, EAST); m_group->addButton(m_page->bottomLeft, SOUTH_WEST); m_group->addButton(m_page->bottomCenter, SOUTH); m_group->addButton(m_page->bottomRight, SOUTH_EAST); loadAnchorIcons(); m_group->button(CENTER)->setChecked(true); updateAnchorIcons(CENTER); KisSizeGroup *labelsGroup = new KisSizeGroup(this); labelsGroup->addWidget(m_page->lblNewWidth); labelsGroup->addWidget(m_page->lblNewHeight); labelsGroup->addWidget(m_page->lblXOff); labelsGroup->addWidget(m_page->lblYOff); labelsGroup->addWidget(m_page->lblAnchor); KisSizeGroup *spinboxesGroup = new KisSizeGroup(this); spinboxesGroup->addWidget(m_page->newWidthDouble); spinboxesGroup->addWidget(m_page->newHeightDouble); spinboxesGroup->addWidget(m_page->xOffsetDouble); spinboxesGroup->addWidget(m_page->yOffsetDouble); KisSizeGroup *comboboxesGroup = new KisSizeGroup(this); comboboxesGroup->addWidget(m_page->widthUnit); comboboxesGroup->addWidget(m_page->heightUnit); comboboxesGroup->addWidget(m_page->xOffUnit); comboboxesGroup->addWidget(m_page->yOffUnit); setMainWidget(m_page); connect(this, SIGNAL(okClicked()), this, SLOT(accept())); connect(m_page->newWidthDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotWidthChanged(double))); connect(m_page->newHeightDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotHeightChanged(double))); connect(m_page->widthUnit, SIGNAL(currentIndexChanged(int)), _widthUnitManager, SLOT(selectApparentUnitFromIndex(int))); connect(m_page->heightUnit, SIGNAL(currentIndexChanged(int)), _heightUnitManager, SLOT(selectApparentUnitFromIndex(int))); connect(_widthUnitManager, SIGNAL(unitChanged(int)), m_page->widthUnit, SLOT(setCurrentIndex(int))); connect(_heightUnitManager, SIGNAL(unitChanged(int)), m_page->heightUnit, SLOT(setCurrentIndex(int))); connect(m_page->xOffsetDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotXOffsetChanged(double))); connect(m_page->yOffsetDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotYOffsetChanged(double))); connect(m_page->xOffUnit, SIGNAL(currentIndexChanged(int)), _xOffsetUnitManager, SLOT(selectApparentUnitFromIndex(int))); connect(m_page->yOffUnit, SIGNAL(currentIndexChanged(int)), _yOffsetUnitManager, SLOT(selectApparentUnitFromIndex(int))); connect(_xOffsetUnitManager, SIGNAL(unitChanged(int)), m_page->xOffUnit, SLOT(setCurrentIndex(int))); connect(_yOffsetUnitManager, SIGNAL(unitChanged(int)), m_page->yOffUnit, SLOT(setCurrentIndex(int))); connect(m_page->constrainProportionsCkb, SIGNAL(toggled(bool)), this, SLOT(slotAspectChanged(bool))); connect(m_page->aspectRatioBtn, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(slotAspectChanged(bool))); connect(m_group, SIGNAL(buttonClicked(int)), SLOT(slotAnchorButtonClicked(int))); connect(m_page->canvasPreview, SIGNAL(sigModifiedXOffset(int)), this, SLOT(slotCanvasPreviewXOffsetChanged(int))); connect(m_page->canvasPreview, SIGNAL(sigModifiedYOffset(int)), this, SLOT(slotCanvasPreviewYOffsetChanged(int))); } DlgCanvasSize::~DlgCanvasSize() { KisConfig cfg; cfg.writeEntry("CanvasSize/KeepAspectRatio", m_page->aspectRatioBtn->keepAspectRatio()); cfg.writeEntry("CanvasSize/ConstrainProportions", m_page->constrainProportionsCkb->isChecked()); + cfg.writeEntry(PARAM_WIDTH_UNIT, _widthUnitManager->getApparentUnitSymbol()); + cfg.writeEntry(PARAM_HEIGTH_UNIT, _heightUnitManager->getApparentUnitSymbol()); + + cfg.writeEntry(PARAM_XOFFSET_UNIT, _xOffsetUnitManager->getApparentUnitSymbol()); + cfg.writeEntry(PARAM_YOFFSET_UNIT, _yOffsetUnitManager->getApparentUnitSymbol()); + delete m_page; } qint32 DlgCanvasSize::width() { return (qint32) m_newWidth; } qint32 DlgCanvasSize::height() { return (qint32) m_newHeight; } qint32 DlgCanvasSize::xOffset() { return (qint32) m_page->xOffsetDouble->value(); } qint32 DlgCanvasSize::yOffset() { return (qint32) m_page->yOffsetDouble->value(); } void DlgCanvasSize::slotAspectChanged(bool keep) { m_page->aspectRatioBtn->blockSignals(true); m_page->constrainProportionsCkb->blockSignals(true); m_page->aspectRatioBtn->setKeepAspectRatio(keep); m_page->constrainProportionsCkb->setChecked(keep); m_page->aspectRatioBtn->blockSignals(false); m_page->constrainProportionsCkb->blockSignals(false); m_keepAspect = keep; if (keep) { // size values may be out of sync, so we need to reset it to defaults m_newWidth = m_originalWidth; m_newHeight = m_originalHeight; m_xOffset = 0; m_yOffset = 0; m_page->canvasPreview->blockSignals(true); m_page->canvasPreview->setCanvasSize(m_newWidth, m_newHeight); m_page->canvasPreview->setImageOffset(m_xOffset, m_yOffset); m_page->canvasPreview->blockSignals(false); updateOffset(CENTER); updateButtons(CENTER); } } void DlgCanvasSize::slotAnchorButtonClicked(int id) { updateOffset(id); updateButtons(id); } void DlgCanvasSize::slotWidthChanged(double v) { //this slot receiv values in pt from the unitspinbox, so just use the resolution. const double resValue = v*_widthUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); m_newWidth = qRound(resValue); if (m_keepAspect) { m_newHeight = qRound(m_newWidth / m_aspectRatio); m_page->newHeightDouble->blockSignals(true); m_page->newHeightDouble->changeValue(v / m_aspectRatio); m_page->newHeightDouble->blockSignals(false); } int savedId = m_group->checkedId(); m_page->canvasPreview->blockSignals(true); m_page->canvasPreview->setCanvasSize(m_newWidth, m_newHeight); m_page->canvasPreview->blockSignals(false); updateOffset(savedId); updateButtons(savedId); } void DlgCanvasSize::slotHeightChanged(double v) { //this slot receiv values in pt from the unitspinbox, so just use the resolution. const double resValue = v*_heightUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); m_newHeight = qRound(resValue); if (m_keepAspect) { m_newWidth = qRound(m_newHeight * m_aspectRatio); m_page->newWidthDouble->blockSignals(true); m_page->newWidthDouble->changeValue(v * m_aspectRatio); m_page->newWidthDouble->blockSignals(false); } int savedId = m_group->checkedId(); m_page->canvasPreview->blockSignals(true); m_page->canvasPreview->setCanvasSize(m_newWidth, m_newHeight); m_page->canvasPreview->blockSignals(false); updateOffset(savedId); updateButtons(savedId); } void DlgCanvasSize::slotXOffsetChanged(double v) { //this slot receiv values in pt from the unitspinbox, so just use the resolution. const double resValue = v*_xOffsetUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); m_xOffset = qRound(resValue); m_page->canvasPreview->blockSignals(true); m_page->canvasPreview->setImageOffset(m_xOffset, m_yOffset); m_page->canvasPreview->blockSignals(false); updateButtons(-1); } void DlgCanvasSize::slotYOffsetChanged(double v) { //this slot receiv values in pt from the unitspinbox, so just use the resolution. const double resValue = v*_xOffsetUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); m_yOffset = qRound(resValue); m_page->canvasPreview->blockSignals(true); m_page->canvasPreview->setImageOffset(m_xOffset, m_yOffset); m_page->canvasPreview->blockSignals(false); updateButtons(-1); } void DlgCanvasSize::slotCanvasPreviewXOffsetChanged(int v) { double newVal = v / _xOffsetUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); m_page->xOffsetDouble->changeValue(newVal); } void DlgCanvasSize::slotCanvasPreviewYOffsetChanged(int v) { double newVal = v / _yOffsetUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); m_page->yOffsetDouble->changeValue(newVal); } void DlgCanvasSize::loadAnchorIcons() { m_anchorIcons[NORTH_WEST] = KisIconUtils::loadIcon("arrow-topleft"); m_anchorIcons[NORTH] = KisIconUtils::loadIcon("arrow-up"); m_anchorIcons[NORTH_EAST] = KisIconUtils::loadIcon("arrow-topright"); m_anchorIcons[EAST] = KisIconUtils::loadIcon("arrow-right"); m_anchorIcons[CENTER] = KisIconUtils::loadIcon("arrow_center"); m_anchorIcons[WEST] = KisIconUtils::loadIcon("arrow-left"); m_anchorIcons[SOUTH_WEST] = KisIconUtils::loadIcon("arrow-downleft"); m_anchorIcons[SOUTH] = KisIconUtils::loadIcon("arrow-down"); m_anchorIcons[SOUTH_EAST] = KisIconUtils::loadIcon("arrow-downright"); } void DlgCanvasSize::updateAnchorIcons(int id) { anchor iconLayout[10][9] = { {NONE, EAST, NONE, SOUTH, SOUTH_EAST, NONE, NONE, NONE, NONE}, {WEST, NONE, EAST, SOUTH_WEST, SOUTH, SOUTH_EAST, NONE, NONE, NONE}, {NONE, WEST, NONE, NONE, SOUTH_WEST, SOUTH, NONE, NONE, NONE}, {NORTH, NORTH_EAST, NONE, NONE, EAST, NONE, SOUTH, SOUTH_EAST, NONE}, {NORTH_WEST, NORTH, NORTH_EAST, WEST, NONE, EAST, SOUTH_WEST, SOUTH, SOUTH_EAST}, {NONE, NORTH_WEST, NORTH, NONE, WEST, NONE, NONE, SOUTH_WEST, SOUTH}, {NONE, NONE, NONE, NORTH, NORTH_EAST, NONE, NONE, EAST, NONE}, {NONE, NONE, NONE, NORTH_WEST, NORTH, NORTH_EAST, WEST, NONE, EAST}, {NONE, NONE, NONE, NONE, NORTH_WEST, NORTH, NONE, WEST, NONE}, {NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE} }; if (id == -1) { id = SOUTH_EAST + 1; } // we are going to swap arrows direction based on width and height shrinking bool shrinkWidth = (m_newWidth < m_originalWidth) ? true : false; bool shrinkHeight = (m_newHeight < m_originalHeight) ? true : false; for (int i = NORTH_WEST; i <= SOUTH_EAST; i++) { anchor iconId = iconLayout[id][i]; // all corner arrows represents shrinking in some direction if (shrinkWidth || shrinkHeight) { switch (iconId) { case NORTH_WEST: iconId = SOUTH_EAST; break; case NORTH_EAST: iconId = SOUTH_WEST; break; case SOUTH_WEST: iconId = NORTH_EAST; break; case SOUTH_EAST: iconId = NORTH_WEST; break; default: break; } } if (shrinkWidth) { switch (iconId) { case WEST: iconId = EAST; break; case EAST: iconId = WEST; break; default: break; } } if (shrinkHeight) { switch (iconId) { case NORTH: iconId = SOUTH; break; case SOUTH: iconId = NORTH; break; default: break; } } QAbstractButton *button = m_group->button(i); if (iconId == NONE) { button->setIcon(QIcon()); } else { button->setIcon(m_anchorIcons[iconId]); } } } void DlgCanvasSize::updateButtons(int forceId) { int id = m_group->checkedId(); if (forceId != -1) { m_group->setExclusive(true); m_group->button(forceId)->setChecked(true); updateAnchorIcons(forceId); } else if (id != -1) { double xOffset, yOffset; expectedOffset(id, xOffset, yOffset); // convert values to internal unit int internalXOffset = 0; int internalYOffset = 0; if (m_page->xOffUnit->currentText() == percentStr) { internalXOffset = qRound((xOffset * m_newWidth) / 100.0); internalYOffset = qRound((yOffset * m_newHeight) / 100.0); } else { const KoUnit xOffsetUnit = KoUnit::fromListForUi(m_page->xOffUnit->currentIndex()); internalXOffset = qRound(xOffsetUnit.fromUserValue(xOffset)); const KoUnit yOffsetUnit = KoUnit::fromListForUi(m_page->yOffUnit->currentIndex()); internalYOffset = qRound(yOffsetUnit.fromUserValue(yOffset)); } bool offsetAsExpected = internalXOffset == m_xOffset && internalYOffset == m_yOffset; if (offsetAsExpected) { m_group->setExclusive(true); } else { m_group->setExclusive(false); m_group->button(id)->setChecked(false); id = -1; } updateAnchorIcons(id); } else { updateAnchorIcons(id); } } void DlgCanvasSize::updateOffset(int id) { if (id == -1) return; double xOffset; double yOffset; expectedOffset(id, xOffset, yOffset); m_page->xOffsetDouble->changeValue(xOffset); m_page->yOffsetDouble->changeValue(yOffset); } void DlgCanvasSize::expectedOffset(int id, double &xOffset, double &yOffset) { const double xCoeff = (id % 3) * 0.5; const double yCoeff = (id / 3) * 0.5; const int xDiff = m_newWidth - m_originalWidth; const int yDiff = m_newHeight - m_originalHeight; //convert to unitmanager default unit. xOffset = xDiff * xCoeff / _xOffsetUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); yOffset = yDiff * yCoeff / _yOffsetUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); } diff --git a/plugins/extensions/imagesize/dlg_canvassize.h b/plugins/extensions/imagesize/dlg_canvassize.h index 1c8ec8f4f6..ba34e1466d 100644 --- a/plugins/extensions/imagesize/dlg_canvassize.h +++ b/plugins/extensions/imagesize/dlg_canvassize.h @@ -1,100 +1,106 @@ /* * * Copyright (c) 2009 Edward Apap * Copyright (c) 2013 Juan Palacios * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DLG_CANVASSIZE #define DLG_CANVASSIZE #include #include #include "ui_wdg_canvassize.h" class KisDocumentAwareSpinBoxUnitManager; class WdgCanvasSize : public QWidget, public Ui::WdgCanvasSize { Q_OBJECT public: WdgCanvasSize(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class DlgCanvasSize: public KoDialog { Q_OBJECT public: enum anchor { NORTH_WEST = 0, NORTH, NORTH_EAST, WEST, CENTER, EAST, SOUTH_WEST, SOUTH, SOUTH_EAST, NONE}; + static const QString PARAM_PREFIX; + static const QString PARAM_WIDTH_UNIT; + static const QString PARAM_HEIGTH_UNIT; + static const QString PARAM_XOFFSET_UNIT; + static const QString PARAM_YOFFSET_UNIT; + DlgCanvasSize(QWidget * parent, int width, int height, double resolution); ~DlgCanvasSize() override; qint32 width(); qint32 height(); qint32 xOffset(); qint32 yOffset(); private Q_SLOTS: void slotAspectChanged(bool keep); void slotAnchorButtonClicked(int id); void slotWidthChanged(double v); void slotHeightChanged(double v); void slotXOffsetChanged(double v); void slotYOffsetChanged(double v); void slotCanvasPreviewXOffsetChanged(int v); void slotCanvasPreviewYOffsetChanged(int v); private: void loadAnchorIcons(); void updateAnchorIcons(int id); void updateButtons(int forceId); void updateOffset(int id); void expectedOffset(int id, double &xOffset, double &yOffset); bool m_keepAspect; const double m_aspectRatio; const double m_resolution; const int m_originalWidth, m_originalHeight; int m_newWidth, m_newHeight; int m_xOffset, m_yOffset; WdgCanvasSize * m_page; QIcon m_anchorIcons[9]; QButtonGroup *m_group; KisDocumentAwareSpinBoxUnitManager* _widthUnitManager; KisDocumentAwareSpinBoxUnitManager* _heightUnitManager; KisDocumentAwareSpinBoxUnitManager* _xOffsetUnitManager; KisDocumentAwareSpinBoxUnitManager* _yOffsetUnitManager; }; #endif // DLG_CANVASSIZE diff --git a/plugins/extensions/imagesize/dlg_imagesize.cc b/plugins/extensions/imagesize/dlg_imagesize.cc index 75f111c5ce..7c2a4d55c7 100644 --- a/plugins/extensions/imagesize/dlg_imagesize.cc +++ b/plugins/extensions/imagesize/dlg_imagesize.cc @@ -1,431 +1,423 @@ /* * dlg_imagesize.cc - part of KimageShop^WKrayon^WKrita * * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2009 C. Boemann * Copyright (c) 2013 Juan Palacios * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dlg_imagesize.h" #include +#include #include #include #include #include #include "kis_aspect_ratio_locker.h" #include "kis_acyclic_signal_connector.h" #include "kis_signals_blocker.h" #include "kis_double_parse_unit_spin_box.h" #include "kis_document_aware_spin_box_unit_manager.h" static const int maxImagePixelSize = 10000; static const QString pixelStr(KoUnit::unitDescription(KoUnit::Pixel)); static const QString percentStr(i18n("Percent (%)")); static const QString pixelsInchStr(i18n("Pixels/Inch")); static const QString pixelsCentimeterStr(i18n("Pixels/Centimeter")); +const QString DlgImageSize::PARAM_PREFIX = "imagesizedlg"; +const QString DlgImageSize::PARAM_IMSIZE_UNIT = DlgImageSize::PARAM_PREFIX + "_imsizeunit"; +const QString DlgImageSize::PARAM_SIZE_UNIT = DlgImageSize::PARAM_PREFIX + "_sizeunit"; +const QString DlgImageSize::PARAM_RES_UNIT = DlgImageSize::PARAM_PREFIX + "_resunit"; +const QString DlgImageSize::PARAM_RATIO_LOCK = DlgImageSize::PARAM_PREFIX + "_ratioLock"; +const QString DlgImageSize::PARAM_PRINT_SIZE_SEPARATE = DlgImageSize::PARAM_PREFIX + "_printSizeSeparatly"; + DlgImageSize::DlgImageSize(QWidget *parent, int width, int height, double resolution) : KoDialog(parent) { setCaption(i18n("Scale To New Size")); setButtons(Ok | Cancel); setDefaultButton(Ok); m_page = new WdgImageSize(this); Q_CHECK_PTR(m_page); m_page->layout()->setMargin(0); m_page->setObjectName("image_size"); m_page->pixelFilterCmb->setIDList(KisFilterStrategyRegistry::instance()->listKeys()); m_page->pixelFilterCmb->setToolTip(KisFilterStrategyRegistry::instance()->formattedDescriptions()); m_page->pixelFilterCmb->setCurrent("Bicubic"); - /** * Initialize Pixel Width and Height fields */ m_widthUnitManager = new KisDocumentAwareSpinBoxUnitManager(this); m_heightUnitManager = new KisDocumentAwareSpinBoxUnitManager(this, KisDocumentAwareSpinBoxUnitManager::PIX_DIR_Y); + KisConfig cfg; + /// configure the unit to image length, default unit is pixel and printing units are forbiden. m_widthUnitManager->setUnitDimension(KisSpinBoxUnitManager::IMLENGTH); m_heightUnitManager->setUnitDimension(KisSpinBoxUnitManager::IMLENGTH); + m_widthUnitManager->syncWithOtherUnitManager(m_heightUnitManager); //sync the two managers, so that the units will be the same, but each manager will know a different reference for percents. + + m_widthUnitManager->setApparentUnitFromSymbol("px"); //set unit to pixel. + m_page->pixelWidthDouble->setUnitManager(m_widthUnitManager); m_page->pixelHeightDouble->setUnitManager(m_heightUnitManager); m_page->pixelWidthDouble->changeValue(width); m_page->pixelHeightDouble->changeValue(height); m_page->pixelWidthDouble->setDisplayUnit(false); m_page->pixelHeightDouble->setDisplayUnit(false); /// add custom units - m_page->pixelSizeUnit->addItem(pixelStr); - m_page->pixelSizeUnit->addItem(percentStr); - m_page->pixelSizeUnit->setCurrentText(pixelStr); + + int unitId = m_widthUnitManager->getApparentUnitId(); + + m_page->pixelSizeUnit->setModel(m_widthUnitManager); + m_page->pixelSizeUnit->setCurrentIndex(unitId); + + /** + * Connect Pixel Unit switching controls + */ + + KisAcyclicSignalConnector *pixelUnitConnector = new KisAcyclicSignalConnector(this); + pixelUnitConnector->connectForwardInt(m_page->pixelSizeUnit, SIGNAL(currentIndexChanged(int)), + m_widthUnitManager, SLOT(selectApparentUnitFromIndex(int))); + pixelUnitConnector->connectBackwardInt(m_widthUnitManager, SIGNAL(unitChanged(int)), + m_page->pixelSizeUnit, SLOT(setCurrentIndex(int))); + + QString imSizeUnit = cfg.readEntry(PARAM_IMSIZE_UNIT, "px"); + + m_widthUnitManager->setApparentUnitFromSymbol(imSizeUnit); /** * Initialize Print Width, Height and Resolution fields */ m_printSizeUnitManager = new KisSpinBoxUnitManager(this); m_page->printWidth->setUnitManager(m_printSizeUnitManager); m_page->printHeight->setUnitManager(m_printSizeUnitManager); m_page->printWidth->setDecimals(2); m_page->printHeight->setDecimals(2); m_page->printWidth->setDisplayUnit(false); m_page->printHeight->setDisplayUnit(false); m_page->printResolution->setDecimals(2); m_page->printResolution->setAlignment(Qt::AlignRight); m_page->printWidthUnit->setModel(m_printSizeUnitManager); //TODO: create a resolution dimension in the unit manager. m_page->printResolutionUnit->addItem(pixelsInchStr); m_page->printResolutionUnit->addItem(pixelsCentimeterStr); /** * Initialize labels and layout */ KisSizeGroup *labelsGroup = new KisSizeGroup(this); labelsGroup->addWidget(m_page->lblPixelWidth); labelsGroup->addWidget(m_page->lblPixelHeight); labelsGroup->addWidget(m_page->lblPixelFilter); labelsGroup->addWidget(m_page->lblPrintWidth); labelsGroup->addWidget(m_page->lblPrintHeight); labelsGroup->addWidget(m_page->lblResolution); KisSizeGroup *spinboxesGroup = new KisSizeGroup(this); spinboxesGroup->addWidget(m_page->pixelWidthDouble); spinboxesGroup->addWidget(m_page->pixelHeightDouble); spinboxesGroup->addWidget(m_page->printWidth); spinboxesGroup->addWidget(m_page->printHeight); spinboxesGroup->addWidget(m_page->printResolution); KisSizeGroup *comboboxesGroup = new KisSizeGroup(this); comboboxesGroup->addWidget(m_page->pixelSizeUnit); comboboxesGroup->addWidget(m_page->printWidthUnit); comboboxesGroup->addWidget(m_page->printResolutionUnit); connect(this, SIGNAL(okClicked()), this, SLOT(accept())); /** * Initialize aspect ratio buttons and lockers */ m_page->pixelAspectRatioBtn->setKeepAspectRatio(true); m_page->printAspectRatioBtn->setKeepAspectRatio(true); m_page->constrainProportionsCkb->setChecked(true); m_pixelSizeLocker = new KisAspectRatioLocker(this); m_pixelSizeLocker->connectSpinBoxes(m_page->pixelWidthDouble, m_page->pixelHeightDouble, m_page->pixelAspectRatioBtn); m_printSizeLocker = new KisAspectRatioLocker(this); m_printSizeLocker->connectSpinBoxes(m_page->printWidth, m_page->printHeight, m_page->printAspectRatioBtn); /** * Connect Keep Aspect Lock buttons */ KisAcyclicSignalConnector *constrainsConnector = new KisAcyclicSignalConnector(this); constrainsConnector->connectBackwardBool( m_page->constrainProportionsCkb, SIGNAL(toggled(bool)), this, SLOT(slotLockAllRatioSwitched(bool))); constrainsConnector->connectForwardBool( m_pixelSizeLocker, SIGNAL(aspectButtonToggled(bool)), this, SLOT(slotLockPixelRatioSwitched(bool))); constrainsConnector->createCoordinatedConnector()->connectBackwardBool( m_printSizeLocker, SIGNAL(aspectButtonToggled(bool)), this, SLOT(slotLockPrintRatioSwitched(bool))); constrainsConnector->createCoordinatedConnector()->connectBackwardBool( m_page->adjustPrintSizeSeparatelyCkb, SIGNAL(toggled(bool)), this, SLOT(slotAdjustSeparatelySwitched(bool))); - /** - * Connect Pixel Unit switching controls - */ - - KisAcyclicSignalConnector *pixelUnitConnector = new KisAcyclicSignalConnector(this); - pixelUnitConnector->connectForwardInt( - m_page->pixelSizeUnit, SIGNAL(currentIndexChanged(int)), - this, SLOT(slotPixelUnitBoxChanged())); - - pixelUnitConnector->connectBackwardInt( - m_widthUnitManager, SIGNAL(unitChanged(int)), - this, SLOT(slotPixelWidthUnitSuffixChanged())); - - pixelUnitConnector->createCoordinatedConnector()->connectBackwardInt( - m_heightUnitManager, SIGNAL(unitChanged(int)), - this, SLOT(slotPixelHeightUnitSuffixChanged())); - /** * Connect Print Unit switching controls */ KisAcyclicSignalConnector *printUnitConnector = new KisAcyclicSignalConnector(this); printUnitConnector->connectForwardInt( m_page->printWidthUnit, SIGNAL(currentIndexChanged(int)), m_printSizeUnitManager, SLOT(selectApparentUnitFromIndex(int))); printUnitConnector->connectBackwardInt( m_printSizeUnitManager, SIGNAL(unitChanged(int)), m_page->printWidthUnit, SLOT(setCurrentIndex(int))); /// connect resolution connect(m_page->printResolutionUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPrintResolutionUnitChanged())); - /** * Create syncing connections between Pixel and Print values */ KisAcyclicSignalConnector *syncConnector = new KisAcyclicSignalConnector(this); syncConnector->connectForwardVoid( m_pixelSizeLocker, SIGNAL(sliderValueChanged()), this, SLOT(slotSyncPixelToPrintSize())); syncConnector->connectBackwardVoid( m_printSizeLocker, SIGNAL(sliderValueChanged()), this, SLOT(slotSyncPrintToPixelSize())); syncConnector->createCoordinatedConnector()->connectBackwardVoid( m_page->printResolution, SIGNAL(valueChanged(double)), this, SLOT(slotPrintResolutionChanged())); /** * Initialize printing values from the predefined image values */ + QString printSizeUnit; + if (QLocale().measurementSystem() == QLocale::MetricSystem) { - m_page->printWidthUnit->setCurrentText("cm"); + printSizeUnit = "cm"; } else { // Imperial - m_page->printWidthUnit->setCurrentText("in"); + printSizeUnit = "in"; } + printSizeUnit = cfg.readEntry(PARAM_SIZE_UNIT, printSizeUnit); + + m_printSizeUnitManager->setApparentUnitFromSymbol(printSizeUnit); + setCurrentResilutionPPI(resolution); slotSyncPixelToPrintSize(); - slotPixelUnitBoxChanged(); /** * Initialize aspect ratio lockers with the current proportion. - * Print locker gets the values only after the first call to slotSyncPixelToPrintSize(). */ m_pixelSizeLocker->updateAspect(); m_printSizeLocker->updateAspect(); + QString printResUnit = cfg.readEntry(PARAM_RES_UNIT, ""); + m_page->printResolutionUnit->setCurrentText(printResUnit); + + m_page->constrainProportionsCkb->setChecked(cfg.readEntry(PARAM_RATIO_LOCK, true)); + m_page->adjustPrintSizeSeparatelyCkb->setChecked(cfg.readEntry(PARAM_PRINT_SIZE_SEPARATE, false)); + setMainWidget(m_page); } DlgImageSize::~DlgImageSize() { + KisConfig cfg; + cfg.writeEntry(PARAM_PRINT_SIZE_SEPARATE, m_page->adjustPrintSizeSeparatelyCkb->isChecked()); + cfg.writeEntry(PARAM_RATIO_LOCK, m_page->constrainProportionsCkb->isChecked()); + + cfg.writeEntry(PARAM_IMSIZE_UNIT, m_widthUnitManager->getApparentUnitSymbol()); + cfg.writeEntry(PARAM_SIZE_UNIT, m_printSizeUnitManager->getApparentUnitSymbol()); + cfg.writeEntry(PARAM_RES_UNIT, m_page->printResolutionUnit->currentText()); + delete m_page; } qint32 DlgImageSize::width() { return int(m_page->pixelWidthDouble->value()); } qint32 DlgImageSize::height() { return int(m_page->pixelHeightDouble->value()); } double DlgImageSize::resolution() { return currentResolutionPPI(); } KisFilterStrategy *DlgImageSize::filterType() { KoID filterID = m_page->pixelFilterCmb->currentItem(); KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value(filterID.id()); return filter; } void DlgImageSize::slotSyncPrintToPixelSize() { const bool printIsSeparate = m_page->adjustPrintSizeSeparatelyCkb->isChecked(); if (!printIsSeparate) { KisSignalsBlocker b(m_page->pixelWidthDouble, m_page->pixelHeightDouble); m_page->pixelWidthDouble->changeValue(m_page->printWidth->value() * currentResolutionPPI()); m_page->pixelHeightDouble->changeValue(m_page->printHeight->value() * currentResolutionPPI()); } else if (m_page->pixelWidthDouble->value() != 0.0) { setCurrentResilutionPPI(m_page->pixelWidthDouble->value() / m_page->printWidth->value()); } } void DlgImageSize::slotSyncPixelToPrintSize() { const qreal resolution = currentResolutionPPI(); if (resolution != 0.0) { KisSignalsBlocker b(m_page->printWidth, m_page->printHeight); m_page->printWidth->changeValue(m_page->pixelWidthDouble->value() / resolution); m_page->printHeight->changeValue(m_page->pixelHeightDouble->value() / resolution); } } void DlgImageSize::slotPrintResolutionChanged() { const bool printIsSeparate = m_page->adjustPrintSizeSeparatelyCkb->isChecked(); if (printIsSeparate) { slotSyncPixelToPrintSize(); } else { slotSyncPrintToPixelSize(); } updatePrintSizeMaximum(); } void DlgImageSize::slotPrintResolutionUnitChanged() { qreal resolution = m_page->printResolution->value(); if (m_page->printResolutionUnit->currentText() == pixelsInchStr) { resolution = KoUnit::convertFromUnitToUnit(resolution, KoUnit(KoUnit::Inch), KoUnit(KoUnit::Centimeter)); } else { resolution = KoUnit::convertFromUnitToUnit(resolution, KoUnit(KoUnit::Centimeter), KoUnit(KoUnit::Inch)); } { KisSignalsBlocker b(m_page->printResolution); m_page->printResolution->setValue(resolution); } } void DlgImageSize::slotLockPixelRatioSwitched(bool value) { const bool printIsSeparate = m_page->adjustPrintSizeSeparatelyCkb->isChecked(); if (!printIsSeparate) { m_page->printAspectRatioBtn->setKeepAspectRatio(value); } m_page->constrainProportionsCkb->setChecked(value); } void DlgImageSize::slotLockPrintRatioSwitched(bool value) { m_page->pixelAspectRatioBtn->setKeepAspectRatio(value); m_page->constrainProportionsCkb->setChecked(value); } void DlgImageSize::slotLockAllRatioSwitched(bool value) { const bool printIsSeparate = m_page->adjustPrintSizeSeparatelyCkb->isChecked(); m_page->pixelAspectRatioBtn->setKeepAspectRatio(value); if (!printIsSeparate) { m_page->printAspectRatioBtn->setKeepAspectRatio(value); } } void DlgImageSize::slotAdjustSeparatelySwitched(bool value) { m_page->printAspectRatioBtn->setEnabled(!value); m_page->printAspectRatioBtn->setKeepAspectRatio(!value ? m_page->constrainProportionsCkb->isChecked() : true); } -void DlgImageSize::slotPixelUnitBoxChanged() -{ - { - KisSignalsBlocker b(m_page->pixelWidthDouble, m_page->pixelHeightDouble); - - if (m_page->pixelSizeUnit->currentText() == pixelStr) { - // TODO: adjust single step as well - - m_page->pixelWidthDouble->setDecimals(0); - m_page->pixelHeightDouble->setDecimals(0); - m_page->pixelWidthDouble->setSingleStep(1); - m_page->pixelHeightDouble->setSingleStep(1); - m_widthUnitManager->setApparentUnitFromSymbol("px"); - m_heightUnitManager->setApparentUnitFromSymbol("px"); - } else { - m_page->pixelWidthDouble->setDecimals(2); - m_page->pixelHeightDouble->setDecimals(2); - m_page->pixelWidthDouble->setSingleStep(0.01); - m_page->pixelHeightDouble->setSingleStep(0.01); - m_widthUnitManager->setApparentUnitFromSymbol("vw"); - m_heightUnitManager->setApparentUnitFromSymbol("vh"); - } - } -} - -void DlgImageSize::slotPixelWidthUnitSuffixChanged() -{ - m_page->pixelSizeUnit->setCurrentIndex(m_widthUnitManager->getApparentUnitSymbol() != "px"); - slotPixelUnitBoxChanged(); -} - -void DlgImageSize::slotPixelHeightUnitSuffixChanged() -{ - m_page->pixelSizeUnit->setCurrentIndex(m_heightUnitManager->getApparentUnitSymbol() != "px"); - slotPixelUnitBoxChanged(); -} - qreal DlgImageSize::currentResolutionPPI() const { qreal resolution = m_page->printResolution->value(); if (m_page->printResolutionUnit->currentText() == pixelsInchStr) { resolution = KoUnit::convertFromUnitToUnit(resolution, KoUnit(KoUnit::Point), KoUnit(KoUnit::Inch)); } else { resolution = KoUnit::convertFromUnitToUnit(resolution, KoUnit(KoUnit::Point), KoUnit(KoUnit::Centimeter)); } return resolution; } void DlgImageSize::setCurrentResilutionPPI(qreal value) { qreal newValue = value; if (m_page->printResolutionUnit->currentText() == pixelsInchStr) { newValue = KoUnit::convertFromUnitToUnit(value, KoUnit(KoUnit::Inch), KoUnit(KoUnit::Point)); } else { newValue = KoUnit::convertFromUnitToUnit(value, KoUnit(KoUnit::Centimeter), KoUnit(KoUnit::Point)); } { KisSignalsBlocker b(m_page->printResolution); m_page->printResolution->setValue(newValue); } updatePrintSizeMaximum(); } void DlgImageSize::updatePrintSizeMaximum() { const qreal value = currentResolutionPPI(); if (value == 0.0) return; m_page->printWidth->setMaximum(maxImagePixelSize / value); m_page->printHeight->setMaximum(maxImagePixelSize / value); } diff --git a/plugins/extensions/imagesize/dlg_imagesize.h b/plugins/extensions/imagesize/dlg_imagesize.h index a449c158a5..fd12777036 100644 --- a/plugins/extensions/imagesize/dlg_imagesize.h +++ b/plugins/extensions/imagesize/dlg_imagesize.h @@ -1,90 +1,94 @@ /* * dlg_imagesize.h -- part of KimageShop^WKrayon^WKrita * * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2013 Juan Palacios * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DLG_IMAGESIZE #define DLG_IMAGESIZE #include class KisFilterStrategy; class WdgImageSize; class KisDocumentAwareSpinBoxUnitManager; class KisSpinBoxUnitManager; class KisAspectRatioLocker; #include "ui_wdg_imagesize.h" class WdgImageSize : public QWidget, public Ui::WdgImageSize { Q_OBJECT public: WdgImageSize(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class DlgImageSize: public KoDialog { Q_OBJECT public: + + static const QString PARAM_PREFIX; + static const QString PARAM_IMSIZE_UNIT; + static const QString PARAM_SIZE_UNIT; + static const QString PARAM_RES_UNIT; + static const QString PARAM_RATIO_LOCK; + static const QString PARAM_PRINT_SIZE_SEPARATE; + DlgImageSize(QWidget * parent, int width, int height, double resolution); ~DlgImageSize() override; qint32 width(); qint32 height(); double resolution(); KisFilterStrategy *filterType(); private Q_SLOTS: void slotSyncPrintToPixelSize(); void slotSyncPixelToPrintSize(); void slotPrintResolutionChanged(); void slotPrintResolutionUnitChanged(); void slotLockPixelRatioSwitched(bool value); void slotLockPrintRatioSwitched(bool value); void slotLockAllRatioSwitched(bool value); void slotAdjustSeparatelySwitched(bool value); - void slotPixelUnitBoxChanged(); - void slotPixelWidthUnitSuffixChanged(); - void slotPixelHeightUnitSuffixChanged(); - private: qreal currentResolutionPPI() const; void setCurrentResilutionPPI(qreal value); void updatePrintSizeMaximum(); WdgImageSize *m_page; KisAspectRatioLocker *m_pixelSizeLocker; KisAspectRatioLocker *m_printSizeLocker; KisDocumentAwareSpinBoxUnitManager* m_widthUnitManager; KisDocumentAwareSpinBoxUnitManager* m_heightUnitManager; KisSpinBoxUnitManager* m_printSizeUnitManager; }; #endif // DLG_IMAGESIZE diff --git a/plugins/extensions/imagesize/dlg_layersize.cc b/plugins/extensions/imagesize/dlg_layersize.cc index 290b9f829f..4f64284b4e 100644 --- a/plugins/extensions/imagesize/dlg_layersize.cc +++ b/plugins/extensions/imagesize/dlg_layersize.cc @@ -1,197 +1,226 @@ /* * dlg_layersize.cc - part of Krita * * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2005 Sven Langkamp * Copyright (c) 2013 Juan Palacios * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dlg_layersize.h" #include +#include #include #include #include // XXX: I'm really real bad at arithmetic, let alone math. Here // be rounding errors. (Boudewijn) +const QString DlgLayerSize::PARAM_PREFIX = "layersizedlg"; + +const QString DlgLayerSize::PARAM_WIDTH_UNIT = DlgLayerSize::PARAM_PREFIX + "_widthunit"; +const QString DlgLayerSize::PARAM_HEIGTH_UNIT = DlgLayerSize::PARAM_PREFIX + "_heightunit"; + +const QString DlgLayerSize::PARAM_KEEP_AR = DlgLayerSize::PARAM_PREFIX + "_keepar"; +const QString DlgLayerSize::PARAM_KEEP_PROP = DlgLayerSize::PARAM_PREFIX + "_keepprop"; + static const QString pixelStr(KoUnit::unitDescription(KoUnit::Pixel)); static const QString percentStr(i18n("Percent (%)")); DlgLayerSize::DlgLayerSize(QWidget * parent, const char * name, int width, int height, double resolution) : KoDialog(parent) , m_aspectRatio(((double) width) / height) , m_originalWidth(width) , m_originalHeight(height) , m_width(width) , m_height(height) , m_resolution(resolution) , m_keepAspect(true) { setCaption(i18n("Layer Size")); setObjectName(name); setButtons(Ok | Cancel); setDefaultButton(Ok); m_page = new WdgLayerSize(this); Q_CHECK_PTR(m_page); m_page->layout()->setMargin(0); m_page->setObjectName(name); + KisConfig cfg; + _widthUnitManager = new KisDocumentAwareSpinBoxUnitManager(this); _heightUnitManager = new KisDocumentAwareSpinBoxUnitManager(this, KisDocumentAwareSpinBoxUnitManager::PIX_DIR_Y); _widthUnitManager->setApparentUnitFromSymbol("px"); _heightUnitManager->setApparentUnitFromSymbol("px"); m_page->newWidthDouble->setUnitManager(_widthUnitManager); m_page->newHeightDouble->setUnitManager(_heightUnitManager); m_page->newWidthDouble->setDecimals(2); m_page->newHeightDouble->setDecimals(2); m_page->newWidthDouble->setDisplayUnit(false); m_page->newHeightDouble->setDisplayUnit(false); m_page->newWidthDouble->setValue(width); m_page->newWidthDouble->setFocus(); m_page->newHeightDouble->setValue(height); m_page->filterCmb->setIDList(KisFilterStrategyRegistry::instance()->listKeys()); m_page->filterCmb->setToolTip(KisFilterStrategyRegistry::instance()->formattedDescriptions()); m_page->filterCmb->setCurrent("Bicubic"); m_page->newWidthUnit->setModel(_widthUnitManager); m_page->newHeightUnit->setModel(_heightUnitManager); - const int pixelUnitIndex = _widthUnitManager->getsUnitSymbolList().indexOf("px"); //TODO: have a better way to identify units. - m_page->newWidthUnit->setCurrentIndex(pixelUnitIndex); - m_page->newHeightUnit->setCurrentIndex(pixelUnitIndex); + QString unitw = cfg.readEntry(PARAM_WIDTH_UNIT, "px"); + QString unith = cfg.readEntry(PARAM_HEIGTH_UNIT, "px"); + + _widthUnitManager->setApparentUnitFromSymbol(unitw); + _heightUnitManager->setApparentUnitFromSymbol(unith); + + const int wUnitIndex = _widthUnitManager->getsUnitSymbolList().indexOf(unitw); + const int hUnitIndex = _widthUnitManager->getsUnitSymbolList().indexOf(unith); - m_page->aspectRatioBtn->setKeepAspectRatio(true); - m_page->constrainProportionsCkb->setChecked(true); + m_page->newWidthUnit->setCurrentIndex(wUnitIndex); + m_page->newHeightUnit->setCurrentIndex(hUnitIndex); + + m_keepAspect = cfg.readEntry(PARAM_KEEP_AR,true); + m_page->aspectRatioBtn->setKeepAspectRatio(m_keepAspect); + m_page->constrainProportionsCkb->setChecked(cfg.readEntry(PARAM_KEEP_PROP,true)); setMainWidget(m_page); connect(this, SIGNAL(okClicked()), this, SLOT(accept())); connect(m_page->aspectRatioBtn, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(slotAspectChanged(bool))); connect(m_page->constrainProportionsCkb, SIGNAL(toggled(bool)), this, SLOT(slotAspectChanged(bool))); connect(m_page->newWidthDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotWidthChanged(double))); connect(m_page->newHeightDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotHeightChanged(double))); connect(m_page->newWidthUnit, SIGNAL(currentIndexChanged(int)), _widthUnitManager, SLOT(selectApparentUnitFromIndex(int))); connect(m_page->newHeightUnit, SIGNAL(currentIndexChanged(int)), _heightUnitManager, SLOT(selectApparentUnitFromIndex(int))); connect(_widthUnitManager, SIGNAL(unitChanged(int)), m_page->newWidthUnit, SLOT(setCurrentIndex(int))); connect(_heightUnitManager, SIGNAL(unitChanged(int)), m_page->newHeightUnit, SLOT(setCurrentIndex(int))); } DlgLayerSize::~DlgLayerSize() { + + KisConfig cfg; + + cfg.writeEntry(PARAM_KEEP_AR, m_page->aspectRatioBtn->keepAspectRatio()); + cfg.writeEntry(PARAM_KEEP_PROP, m_page->constrainProportionsCkb->isChecked()); + + cfg.writeEntry(PARAM_WIDTH_UNIT, _widthUnitManager->getApparentUnitSymbol()); + cfg.writeEntry(PARAM_HEIGTH_UNIT, _heightUnitManager->getApparentUnitSymbol()); + delete m_page; } qint32 DlgLayerSize::width() { return (qint32)m_width; } qint32 DlgLayerSize::height() { return (qint32)m_height; } KisFilterStrategy *DlgLayerSize::filterType() { KoID filterID = m_page->filterCmb->currentItem(); KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value(filterID.id()); return filter; } // SLOTS void DlgLayerSize::slotWidthChanged(double w) { //this slot receiv values in pt from the unitspinbox, so just use the resolution. const double resValue = w*_widthUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); m_width = qRound(resValue); if (m_keepAspect) { m_height = qRound(m_width / m_aspectRatio); m_page->newHeightDouble->blockSignals(true); m_page->newHeightDouble->changeValue(w / m_aspectRatio); m_page->newHeightDouble->blockSignals(false); } } void DlgLayerSize::slotHeightChanged(double h) { //this slot receiv values in pt from the unitspinbox, so just use the resolution. const double resValue = h*_heightUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); m_height = qRound(resValue); if (m_keepAspect) { m_width = qRound(m_height * m_aspectRatio); m_page->newWidthDouble->blockSignals(true); m_page->newWidthDouble->changeValue(h * m_aspectRatio); m_page->newWidthDouble->blockSignals(false); } } void DlgLayerSize::slotAspectChanged(bool keep) { m_page->aspectRatioBtn->blockSignals(true); m_page->constrainProportionsCkb->blockSignals(true); m_page->aspectRatioBtn->setKeepAspectRatio(keep); m_page->constrainProportionsCkb->setChecked(keep); m_page->aspectRatioBtn->blockSignals(false); m_page->constrainProportionsCkb->blockSignals(false); m_keepAspect = keep; if (keep) { // values may be out of sync, so we need to reset it to defaults m_width = m_originalWidth; m_height = m_originalHeight; updateWidthUIValue(m_width); updateHeightUIValue(m_height); } } void DlgLayerSize::updateWidthUIValue(double value) { m_page->newWidthDouble->blockSignals(true); const double resValue = value/_widthUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); m_page->newWidthDouble->changeValue(resValue); m_page->newWidthDouble->blockSignals(false); } void DlgLayerSize::updateHeightUIValue(double value) { m_page->newHeightDouble->blockSignals(true); const double resValue = value/_heightUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); m_page->newHeightDouble->changeValue(resValue); m_page->newHeightDouble->blockSignals(false); } diff --git a/plugins/extensions/imagesize/dlg_layersize.h b/plugins/extensions/imagesize/dlg_layersize.h index 100e46611a..fdfea9acf9 100644 --- a/plugins/extensions/imagesize/dlg_layersize.h +++ b/plugins/extensions/imagesize/dlg_layersize.h @@ -1,79 +1,85 @@ /* * dlg_layersize.h -- part of Krita * * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2005 Sven Langkamp * Copyright (c) 2013 Juan Palacios * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DLG_LAYERSIZE #define DLG_LAYERSIZE #include #include "ui_wdg_layersize.h" class KisDocumentAwareSpinBoxUnitManager; class WdgLayerSize : public QWidget, public Ui::WdgLayerSize { Q_OBJECT public: WdgLayerSize(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class KisFilterStrategy; class DlgLayerSize: public KoDialog { Q_OBJECT public: + static const QString PARAM_PREFIX; + static const QString PARAM_WIDTH_UNIT; + static const QString PARAM_HEIGTH_UNIT; + static const QString PARAM_KEEP_AR; + static const QString PARAM_KEEP_PROP; + DlgLayerSize(QWidget * parent, const char* name, int width, int height, double resolution); ~DlgLayerSize() override; qint32 width(); qint32 height(); KisFilterStrategy *filterType(); private Q_SLOTS: void slotWidthChanged(double w); void slotHeightChanged(double h); void slotAspectChanged(bool keep); private: void updateWidthUIValue(double value); void updateHeightUIValue(double value); WdgLayerSize * m_page; const double m_aspectRatio; const int m_originalWidth, m_originalHeight; int m_width, m_height; const double m_resolution; bool m_keepAspect; KisDocumentAwareSpinBoxUnitManager* _widthUnitManager; KisDocumentAwareSpinBoxUnitManager* _heightUnitManager; }; #endif // DLG_IMAGESIZE diff --git a/plugins/extensions/offsetimage/dlg_offsetimage.cpp b/plugins/extensions/offsetimage/dlg_offsetimage.cpp index 481fd36d36..2f6d955a14 100644 --- a/plugins/extensions/offsetimage/dlg_offsetimage.cpp +++ b/plugins/extensions/offsetimage/dlg_offsetimage.cpp @@ -1,109 +1,129 @@ /* * Copyright (c) 2013 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dlg_offsetimage.h" #include #include +#include #include "kis_document_aware_spin_box_unit_manager.h" +const QString DlgOffsetImage::PARAM_PREFIX = "imageoffsetdlg"; +const QString DlgOffsetImage::PARAM_XOFFSET_UNIT = DlgOffsetImage::PARAM_PREFIX + "_xoffsetunit"; +const QString DlgOffsetImage::PARAM_YOFFSET_UNIT = DlgOffsetImage::PARAM_PREFIX + "_yoffsetunit"; + DlgOffsetImage::DlgOffsetImage(QWidget * parent, const char * name, QSize imageSize) : KoDialog(parent), m_offsetSize(imageSize) { setCaption("BUG: No sane caption is set"); setButtons(Ok | Cancel); setDefaultButton(Ok); setObjectName(name); m_lock = false; m_page = new WdgOffsetImage(this); Q_CHECK_PTR(m_page); m_page->setObjectName("offset_image"); setMainWidget(m_page); resize(m_page->sizeHint()); _widthUnitManager = new KisDocumentAwareSpinBoxUnitManager(this); _heightUnitManager = new KisDocumentAwareSpinBoxUnitManager(this, KisDocumentAwareSpinBoxUnitManager::PIX_DIR_Y); _widthUnitManager->setApparentUnitFromSymbol("px"); _heightUnitManager->setApparentUnitFromSymbol("px"); m_page->offsetXdoubleSpinBox->setUnitManager(_widthUnitManager); m_page->offsetYdoubleSpinBox->setUnitManager(_heightUnitManager); m_page->offsetXdoubleSpinBox->setDecimals(2); m_page->offsetYdoubleSpinBox->setDecimals(2); m_page->offsetXdoubleSpinBox->setDisplayUnit(false); m_page->offsetYdoubleSpinBox->setDisplayUnit(false); m_page->offsetXdoubleSpinBox->setReturnUnit("px"); m_page->offsetYdoubleSpinBox->setReturnUnit("px"); m_page->unitXComboBox->setModel(_widthUnitManager); m_page->unitYComboBox->setModel(_heightUnitManager); - const int pixelUnitIndex = _widthUnitManager->getsUnitSymbolList().indexOf("px"); //TODO: have a better way to identify units. - m_page->unitXComboBox->setCurrentIndex(pixelUnitIndex); - m_page->unitYComboBox->setCurrentIndex(pixelUnitIndex); + KisConfig cfg; + + QString unitx = cfg.readEntry(PARAM_XOFFSET_UNIT, "px"); + QString unity = cfg.readEntry(PARAM_YOFFSET_UNIT, "px"); + + _widthUnitManager->setApparentUnitFromSymbol(unitx); + _heightUnitManager->setApparentUnitFromSymbol(unity); + + const int xUnitIndex = _widthUnitManager->getsUnitSymbolList().indexOf(unitx); + const int yUnitIndex = _heightUnitManager->getsUnitSymbolList().indexOf(unity); + + m_page->unitXComboBox->setCurrentIndex(xUnitIndex); + m_page->unitYComboBox->setCurrentIndex(yUnitIndex); connect(this, SIGNAL(okClicked()),this, SLOT(okClicked())); connect(m_page->middleOffsetBtn, SIGNAL(clicked()), this, SLOT(slotMiddleOffset())); connect(m_page->offsetXdoubleSpinBox, SIGNAL(valueChangedPt(double)), this, SLOT(slotOffsetXChanged(double))); connect(m_page->offsetYdoubleSpinBox, SIGNAL(valueChangedPt(double)), this, SLOT(slotOffsetYChanged(double))); connect(m_page->unitXComboBox, SIGNAL(currentIndexChanged(int)), _widthUnitManager, SLOT(selectApparentUnitFromIndex(int))); connect(m_page->unitYComboBox, SIGNAL(currentIndexChanged(int)), _heightUnitManager, SLOT(selectApparentUnitFromIndex(int))); connect(_widthUnitManager, SIGNAL(unitChanged(int)), m_page->unitXComboBox, SLOT(setCurrentIndex(int))); connect(_heightUnitManager, SIGNAL(unitChanged(int)), m_page->unitYComboBox, SLOT(setCurrentIndex(int))); slotMiddleOffset(); } DlgOffsetImage::~DlgOffsetImage() { + KisConfig cfg; + + cfg.writeEntry(PARAM_XOFFSET_UNIT, _widthUnitManager->getApparentUnitSymbol()); + cfg.writeEntry(PARAM_YOFFSET_UNIT, _heightUnitManager->getApparentUnitSymbol()); + delete m_page; } void DlgOffsetImage::slotOffsetXChanged(double newOffsetX) { m_offsetX = newOffsetX; } void DlgOffsetImage::slotOffsetYChanged(double newOffsetY) { m_offsetY = newOffsetY; } void DlgOffsetImage::slotMiddleOffset() { int offsetX = m_offsetSize.width() / 2; int offsetY = m_offsetSize.height() / 2; m_page->offsetXdoubleSpinBox->changeValue(offsetX); m_page->offsetYdoubleSpinBox->changeValue(offsetY); } void DlgOffsetImage::okClicked() { accept(); } diff --git a/plugins/extensions/offsetimage/dlg_offsetimage.h b/plugins/extensions/offsetimage/dlg_offsetimage.h index b0d1ef1973..a30f86100c 100644 --- a/plugins/extensions/offsetimage/dlg_offsetimage.h +++ b/plugins/extensions/offsetimage/dlg_offsetimage.h @@ -1,70 +1,75 @@ /* * Copyright (c) 2013 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DLG_OFFSETIMAGE #define DLG_OFFSETIMAGE #include #include #include "ui_wdg_offsetimage.h" class KisDocumentAwareSpinBoxUnitManager; class WdgOffsetImage : public QWidget, public Ui::WdgOffsetImage { Q_OBJECT public: WdgOffsetImage(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class DlgOffsetImage: public KoDialog { Q_OBJECT public: + + static const QString PARAM_PREFIX; + static const QString PARAM_XOFFSET_UNIT; + static const QString PARAM_YOFFSET_UNIT; + DlgOffsetImage(QWidget * parent = 0, const char* name = 0, QSize imageSize = QSize()); ~DlgOffsetImage() override; int offsetX() const { return m_offsetX;} int offsetY() const { return m_offsetY;} private Q_SLOTS: void okClicked(); void slotOffsetXChanged(double); void slotOffsetYChanged(double); void slotMiddleOffset(); private: WdgOffsetImage * m_page; int m_offsetX; int m_offsetY; bool m_lock; QSize m_offsetSize; KisDocumentAwareSpinBoxUnitManager* _widthUnitManager; KisDocumentAwareSpinBoxUnitManager* _heightUnitManager; }; #endif // DLG_OFFSETIMAGE