1 /*
2  *  dlg_imagesize.cc - part of KimageShop^WKrayon^WKrita
3  *
4  *  Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
5  *  Copyright (c) 2009 C. Boemann <cbo@boemann.dk>
6  *  Copyright (c) 2013 Juan Palacios <jpalaciosdev@gmail.com>
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  */
22 
23 #include "dlg_imagesize.h"
24 
25 #include <QLocale>
26 #include <kis_config.h>
27 
28 #include <KoUnit.h>
29 #include <kis_size_group.h>
30 #include <klocalizedstring.h>
31 
32 #include <kis_filter_strategy.h>
33 
34 #include "kis_aspect_ratio_locker.h"
35 #include "kis_acyclic_signal_connector.h"
36 #include "kis_signals_blocker.h"
37 
38 #include "kis_double_parse_unit_spin_box.h"
39 #include "kis_document_aware_spin_box_unit_manager.h"
40 
41 static const int maxImagePixelSize = 100000000;
42 
43 static const QString pixelStr(KoUnit::unitDescription(KoUnit::Pixel));
44 static const QString percentStr(i18n("Percent (%)"));
45 static const QString pixelsInchStr(i18n("Pixels/Inch"));
46 static const QString pixelsCentimeterStr(i18n("Pixels/Centimeter"));
47 
48 const QString DlgImageSize::PARAM_PREFIX = "imagesizedlg";
49 const QString DlgImageSize::PARAM_IMSIZE_UNIT = DlgImageSize::PARAM_PREFIX + "_imsizeunit";
50 const QString DlgImageSize::PARAM_SIZE_UNIT = DlgImageSize::PARAM_PREFIX + "_sizeunit";
51 const QString DlgImageSize::PARAM_RES_UNIT = DlgImageSize::PARAM_PREFIX + "_resunit";
52 const QString DlgImageSize::PARAM_RATIO_LOCK = DlgImageSize::PARAM_PREFIX + "_ratioLock";
53 const QString DlgImageSize::PARAM_PRINT_SIZE_SEPARATE = DlgImageSize::PARAM_PREFIX + "_printSizeSeparatly";
54 
DlgImageSize(QWidget * parent,int width,int height,double resolution)55 DlgImageSize::DlgImageSize(QWidget *parent, int width, int height, double resolution)
56     : KoDialog(parent)
57 {
58     setCaption(i18n("Scale To New Size"));
59     setButtons(Ok | Cancel);
60     setDefaultButton(Ok);
61 
62     m_page = new WdgImageSize(this);
63 
64     Q_CHECK_PTR(m_page);
65     m_page->layout()->setMargin(0);
66     m_page->setObjectName("image_size");
67 
68     m_page->pixelFilterCmb->setIDList(KisFilterStrategyRegistry::instance()->listKeys());
69     m_page->pixelFilterCmb->setToolTip(KisFilterStrategyRegistry::instance()->formattedDescriptions());
70     m_page->pixelFilterCmb->setCurrent("Bicubic");
71 
72     /**
73      * Initialize Pixel Width and Height fields
74      */
75 
76     m_widthUnitManager = new KisDocumentAwareSpinBoxUnitManager(this);
77     m_heightUnitManager = new KisDocumentAwareSpinBoxUnitManager(this, KisDocumentAwareSpinBoxUnitManager::PIX_DIR_Y);
78 
79     KisConfig cfg(true);
80 
81     /// configure the unit to image length, default unit is pixel and printing units are forbidden.
82     m_widthUnitManager->setUnitDimension(KisSpinBoxUnitManager::IMLENGTH);
83     m_heightUnitManager->setUnitDimension(KisSpinBoxUnitManager::IMLENGTH);
84 
85     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.
86 
87     m_widthUnitManager->setApparentUnitFromSymbol("px"); //set unit to pixel.
88 
89     m_page->pixelWidthDouble->setUnitManager(m_widthUnitManager);
90     m_page->pixelHeightDouble->setUnitManager(m_heightUnitManager);
91     m_page->pixelWidthDouble->setMaximum(maxImagePixelSize);
92     m_page->pixelHeightDouble->setMaximum(maxImagePixelSize);
93     m_page->pixelWidthDouble->changeValue(width);
94     m_page->pixelHeightDouble->changeValue(height);
95     m_page->pixelWidthDouble->setDisplayUnit(false);
96     m_page->pixelHeightDouble->setDisplayUnit(false);
97 
98     /// add custom units
99 
100     int unitId = m_widthUnitManager->getApparentUnitId();
101 
102     m_page->pixelSizeUnit->setModel(m_widthUnitManager);
103     m_page->pixelSizeUnit->setCurrentIndex(unitId);
104 
105     /**
106      * Connect Pixel Unit switching controls
107      */
108 
109     KisAcyclicSignalConnector *pixelUnitConnector = new KisAcyclicSignalConnector(this);
110     pixelUnitConnector->connectForwardInt(m_page->pixelSizeUnit, SIGNAL(currentIndexChanged(int)),
111                                           m_widthUnitManager, SLOT(selectApparentUnitFromIndex(int)));
112     pixelUnitConnector->connectBackwardInt(m_widthUnitManager, SIGNAL(unitChanged(int)),
113                                            m_page->pixelSizeUnit, SLOT(setCurrentIndex(int)));
114 
115     QString imSizeUnit = cfg.readEntry<QString>(PARAM_IMSIZE_UNIT, "px");
116 
117     m_widthUnitManager->setApparentUnitFromSymbol(imSizeUnit);
118 
119     /**
120      * Initialize Print Width, Height and Resolution fields
121      */
122 
123     m_printSizeUnitManager = new KisSpinBoxUnitManager(this);
124 
125     m_page->printWidth->setUnitManager(m_printSizeUnitManager);
126     m_page->printHeight->setUnitManager(m_printSizeUnitManager);
127     m_page->printWidth->setDecimals(2);
128     m_page->printHeight->setDecimals(2);
129     m_page->printWidth->setDisplayUnit(false);
130     m_page->printHeight->setDisplayUnit(false);
131     m_page->printResolution->setDecimals(2);
132     m_page->printResolution->setAlignment(Qt::AlignRight);
133 
134     m_page->printWidthUnit->setModel(m_printSizeUnitManager);
135 
136     //TODO: create a resolution dimension in the unit manager.
137     m_page->printResolutionUnit->addItem(pixelsInchStr);
138     m_page->printResolutionUnit->addItem(pixelsCentimeterStr);
139 
140 
141     /**
142      * Initialize labels and layout
143      */
144     KisSizeGroup *labelsGroup = new KisSizeGroup(this);
145     labelsGroup->addWidget(m_page->lblPixelWidth);
146     labelsGroup->addWidget(m_page->lblPixelHeight);
147     labelsGroup->addWidget(m_page->lblPixelFilter);
148     labelsGroup->addWidget(m_page->lblPrintWidth);
149     labelsGroup->addWidget(m_page->lblPrintHeight);
150     labelsGroup->addWidget(m_page->lblResolution);
151 
152     KisSizeGroup *spinboxesGroup = new KisSizeGroup(this);
153     spinboxesGroup->addWidget(m_page->pixelWidthDouble);
154     spinboxesGroup->addWidget(m_page->pixelHeightDouble);
155     spinboxesGroup->addWidget(m_page->printWidth);
156     spinboxesGroup->addWidget(m_page->printHeight);
157     spinboxesGroup->addWidget(m_page->printResolution);
158 
159     KisSizeGroup *comboboxesGroup = new KisSizeGroup(this);
160     comboboxesGroup->addWidget(m_page->pixelSizeUnit);
161     comboboxesGroup->addWidget(m_page->printWidthUnit);
162     comboboxesGroup->addWidget(m_page->printResolutionUnit);
163     connect(this, SIGNAL(okClicked()), this, SLOT(accept()));
164 
165     /**
166      * Initialize aspect ratio buttons and lockers
167      */
168 
169     m_page->pixelAspectRatioBtn->setKeepAspectRatio(true);
170     m_page->printAspectRatioBtn->setKeepAspectRatio(true);
171     m_page->constrainProportionsCkb->setChecked(true);
172 
173     m_pixelSizeLocker = new KisAspectRatioLocker(this);
174     m_pixelSizeLocker->connectSpinBoxes(m_page->pixelWidthDouble, m_page->pixelHeightDouble, m_page->pixelAspectRatioBtn);
175 
176     m_printSizeLocker = new KisAspectRatioLocker(this);
177     m_printSizeLocker->connectSpinBoxes(m_page->printWidth, m_page->printHeight, m_page->printAspectRatioBtn);
178 
179     /**
180      * Connect Keep Aspect Lock buttons
181      */
182 
183     KisAcyclicSignalConnector *constrainsConnector = new KisAcyclicSignalConnector(this);
184     constrainsConnector->connectBackwardBool(
185         m_page->constrainProportionsCkb, SIGNAL(toggled(bool)),
186         this, SLOT(slotLockAllRatioSwitched(bool)));
187 
188     constrainsConnector->connectForwardBool(
189         m_pixelSizeLocker, SIGNAL(aspectButtonToggled(bool)),
190         this, SLOT(slotLockPixelRatioSwitched(bool)));
191 
192     constrainsConnector->createCoordinatedConnector()->connectBackwardBool(
193         m_printSizeLocker, SIGNAL(aspectButtonToggled(bool)),
194         this, SLOT(slotLockPrintRatioSwitched(bool)));
195 
196     constrainsConnector->createCoordinatedConnector()->connectBackwardBool(
197         m_page->adjustPrintSizeSeparatelyCkb, SIGNAL(toggled(bool)),
198         this, SLOT(slotAdjustSeparatelySwitched(bool)));
199 
200     /**
201      * Connect Print Unit switching controls
202      */
203 
204     KisAcyclicSignalConnector *printUnitConnector = new KisAcyclicSignalConnector(this);
205     printUnitConnector->connectForwardInt(
206         m_page->printWidthUnit, SIGNAL(currentIndexChanged(int)),
207         m_printSizeUnitManager, SLOT(selectApparentUnitFromIndex(int)));
208 
209     printUnitConnector->connectBackwardInt(
210         m_printSizeUnitManager, SIGNAL(unitChanged(int)),
211         m_page->printWidthUnit, SLOT(setCurrentIndex(int)));
212 
213     /// connect resolution
214     connect(m_page->printResolutionUnit, SIGNAL(currentIndexChanged(int)),
215             this, SLOT(slotPrintResolutionUnitChanged()));
216 
217     /**
218      * Create syncing connections between Pixel and Print values
219      */
220     KisAcyclicSignalConnector *syncConnector = new KisAcyclicSignalConnector(this);
221     syncConnector->connectForwardVoid(
222         m_pixelSizeLocker, SIGNAL(sliderValueChanged()),
223         this, SLOT(slotSyncPixelToPrintSize()));
224 
225     syncConnector->connectBackwardVoid(
226         m_printSizeLocker, SIGNAL(sliderValueChanged()),
227         this, SLOT(slotSyncPrintToPixelSize()));
228 
229     syncConnector->createCoordinatedConnector()->connectBackwardVoid(
230         m_page->printResolution, SIGNAL(valueChanged(double)),
231         this, SLOT(slotPrintResolutionChanged()));
232 
233 
234     /**
235      * Initialize printing values from the predefined image values
236      */
237     QString printSizeUnit;
238 
239     if (QLocale().measurementSystem() == QLocale::MetricSystem) {
240         printSizeUnit = "cm";
241     } else { // Imperial
242         printSizeUnit = "in";
243     }
244 
245     printSizeUnit = cfg.readEntry<QString>(PARAM_SIZE_UNIT, printSizeUnit);
246 
247     m_printSizeUnitManager->setApparentUnitFromSymbol(printSizeUnit);
248 
249     setCurrentResolutionPPI(resolution);
250     slotSyncPixelToPrintSize();
251 
252     /**
253      * Initialize aspect ratio lockers with the current proportion.
254      */
255     m_pixelSizeLocker->updateAspect();
256     m_printSizeLocker->updateAspect();
257 
258     QString printResUnit = cfg.readEntry<QString>(PARAM_RES_UNIT, "");
259     m_page->printResolutionUnit->setCurrentText(printResUnit);
260 
261     m_page->constrainProportionsCkb->setChecked(cfg.readEntry<bool>(PARAM_RATIO_LOCK, true));
262     m_page->adjustPrintSizeSeparatelyCkb->setChecked(cfg.readEntry<bool>(PARAM_PRINT_SIZE_SEPARATE, false));
263 
264     setMainWidget(m_page);
265 }
266 
~DlgImageSize()267 DlgImageSize::~DlgImageSize()
268 {
269     KisConfig cfg(false);
270     cfg.writeEntry<bool>(PARAM_PRINT_SIZE_SEPARATE, m_page->adjustPrintSizeSeparatelyCkb->isChecked());
271     cfg.writeEntry<bool>(PARAM_RATIO_LOCK, m_page->constrainProportionsCkb->isChecked());
272 
273     cfg.writeEntry<QString>(PARAM_IMSIZE_UNIT, m_widthUnitManager->getApparentUnitSymbol());
274     cfg.writeEntry<QString>(PARAM_SIZE_UNIT, m_printSizeUnitManager->getApparentUnitSymbol());
275     cfg.writeEntry<QString>(PARAM_RES_UNIT, m_page->printResolutionUnit->currentText());
276 
277     delete m_page;
278 }
279 
width()280 qint32 DlgImageSize::width()
281 {
282     return int(m_page->pixelWidthDouble->value());
283 }
284 
height()285 qint32 DlgImageSize::height()
286 {
287     return int(m_page->pixelHeightDouble->value());
288 }
289 
resolution()290 double DlgImageSize::resolution()
291 {
292     return currentResolutionPPI();
293 }
294 
filterType()295 KisFilterStrategy *DlgImageSize::filterType()
296 {
297     KoID filterID = m_page->pixelFilterCmb->currentItem();
298     KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value(filterID.id());
299     return filter;
300 }
301 
slotSyncPrintToPixelSize()302 void DlgImageSize::slotSyncPrintToPixelSize()
303 {
304     const bool printIsSeparate = m_page->adjustPrintSizeSeparatelyCkb->isChecked();
305 
306     if (!printIsSeparate) {
307         KisSignalsBlocker b(m_page->pixelWidthDouble, m_page->pixelHeightDouble);
308         m_page->pixelWidthDouble->changeValue(m_page->printWidth->value() * currentResolutionPPI());
309         m_page->pixelHeightDouble->changeValue(m_page->printHeight->value() * currentResolutionPPI());
310     } else if (m_page->pixelWidthDouble->value() != 0.0) {
311         const qreal resolution = qMax(0.001, m_page->pixelWidthDouble->value() / m_page->printWidth->value());
312         setCurrentResolutionPPI(resolution);
313     }
314 }
315 
slotSyncPixelToPrintSize()316 void DlgImageSize::slotSyncPixelToPrintSize()
317 {
318     const qreal resolution = currentResolutionPPI();
319     if (resolution != 0.0) {
320         KisSignalsBlocker b(m_page->printWidth, m_page->printHeight);
321         m_page->printWidth->changeValue(m_page->pixelWidthDouble->value() / resolution);
322         m_page->printHeight->changeValue(m_page->pixelHeightDouble->value() / resolution);
323     }
324 }
325 
slotPrintResolutionChanged()326 void DlgImageSize::slotPrintResolutionChanged()
327 {
328     const bool printIsSeparate = m_page->adjustPrintSizeSeparatelyCkb->isChecked();
329 
330     if (printIsSeparate) {
331         slotSyncPixelToPrintSize();
332     } else {
333         slotSyncPrintToPixelSize();
334     }
335 
336     updatePrintSizeMaximum();
337 }
338 
slotPrintResolutionUnitChanged()339 void DlgImageSize::slotPrintResolutionUnitChanged()
340 {
341     qreal resolution = m_page->printResolution->value();
342 
343     if (m_page->printResolutionUnit->currentText() == pixelsInchStr) {
344         resolution = KoUnit::convertFromUnitToUnit(resolution, KoUnit(KoUnit::Inch), KoUnit(KoUnit::Centimeter));
345     } else {
346         resolution = KoUnit::convertFromUnitToUnit(resolution, KoUnit(KoUnit::Centimeter), KoUnit(KoUnit::Inch));
347     }
348 
349     {
350         KisSignalsBlocker b(m_page->printResolution);
351         m_page->printResolution->setValue(resolution);
352     }
353 }
354 
slotLockPixelRatioSwitched(bool value)355 void DlgImageSize::slotLockPixelRatioSwitched(bool value)
356 {
357     const bool printIsSeparate = m_page->adjustPrintSizeSeparatelyCkb->isChecked();
358 
359     if (!printIsSeparate) {
360         m_page->printAspectRatioBtn->setKeepAspectRatio(value);
361     }
362     m_page->constrainProportionsCkb->setChecked(value);
363 }
364 
slotLockPrintRatioSwitched(bool value)365 void DlgImageSize::slotLockPrintRatioSwitched(bool value)
366 {
367     m_page->pixelAspectRatioBtn->setKeepAspectRatio(value);
368     m_page->constrainProportionsCkb->setChecked(value);
369 }
370 
slotLockAllRatioSwitched(bool value)371 void DlgImageSize::slotLockAllRatioSwitched(bool value)
372 {
373     const bool printIsSeparate = m_page->adjustPrintSizeSeparatelyCkb->isChecked();
374 
375     m_page->pixelAspectRatioBtn->setKeepAspectRatio(value);
376 
377     if (!printIsSeparate) {
378         m_page->printAspectRatioBtn->setKeepAspectRatio(value);
379     }
380 }
381 
slotAdjustSeparatelySwitched(bool value)382 void DlgImageSize::slotAdjustSeparatelySwitched(bool value)
383 {
384     m_page->printAspectRatioBtn->setEnabled(!value);
385     m_page->printAspectRatioBtn->setKeepAspectRatio(!value ? m_page->constrainProportionsCkb->isChecked() : true);
386 }
387 
currentResolutionPPI() const388 qreal DlgImageSize::currentResolutionPPI() const
389 {
390     qreal resolution = m_page->printResolution->value();
391 
392     if (m_page->printResolutionUnit->currentText() == pixelsInchStr) {
393         resolution = KoUnit::convertFromUnitToUnit(resolution, KoUnit(KoUnit::Point), KoUnit(KoUnit::Inch));
394     } else {
395         resolution = KoUnit::convertFromUnitToUnit(resolution, KoUnit(KoUnit::Point), KoUnit(KoUnit::Centimeter));
396     }
397 
398     return resolution;
399 }
400 
setCurrentResolutionPPI(qreal value)401 void DlgImageSize::setCurrentResolutionPPI(qreal value)
402 {
403     qreal newValue = value;
404 
405     if (m_page->printResolutionUnit->currentText() == pixelsInchStr) {
406         newValue = KoUnit::convertFromUnitToUnit(value, KoUnit(KoUnit::Inch), KoUnit(KoUnit::Point));
407     } else {
408         newValue = KoUnit::convertFromUnitToUnit(value, KoUnit(KoUnit::Centimeter), KoUnit(KoUnit::Point));
409     }
410 
411     {
412         KisSignalsBlocker b(m_page->printResolution);
413         m_page->printResolution->setValue(newValue);
414     }
415 
416     updatePrintSizeMaximum();
417 }
418 
updatePrintSizeMaximum()419 void DlgImageSize::updatePrintSizeMaximum()
420 {
421     const qreal value = currentResolutionPPI();
422     if (value == 0.0) return;
423 
424     m_page->printWidth->setMaximum(maxImagePixelSize / value);
425     m_page->printHeight->setMaximum(maxImagePixelSize / value);
426 }
427