File indexing completed on 2024-05-12 04:19:36
0001 // vim: set tabstop=4 shiftwidth=4 expandtab: 0002 /* 0003 Gwenview: an image viewer 0004 Copyright 2007 Aurélien Gâteau <agateau@kde.org> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License 0008 as published by the Free Software Foundation; either version 2 0009 of the License, or (at your option) any later version. 0010 0011 This program is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 GNU General Public License for more details. 0015 0016 You should have received a copy of the GNU General Public License 0017 along with this program; if not, write to the Free Software 0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0019 0020 */ 0021 // Self 0022 0023 // Qt 0024 #include <QApplication> 0025 #include <QCheckBox> 0026 #include <QComboBox> 0027 // #include <QDesktopWidget> 0028 #include <QDialogButtonBox> 0029 #include <QFontDatabase> 0030 #include <QHBoxLayout> 0031 #include <QLabel> 0032 #include <QLineEdit> 0033 #include <QPushButton> 0034 #include <QScreen> 0035 #include <QSpinBox> 0036 #include <QtMath> 0037 0038 // KF 0039 #include <KLocalizedString> 0040 0041 // Local 0042 #include "croptool.h" 0043 #include "cropwidget.h" 0044 #include "flowlayout.h" 0045 #include <lib/documentview/rasterimageview.h> 0046 0047 namespace Gwenview 0048 { 0049 // Euclidean algorithm to compute the greatest common divisor of two integers. 0050 // Found at: 0051 // https://en.wikipedia.org/wiki/Euclidean_algorithm 0052 static int gcd(int a, int b) 0053 { 0054 return b == 0 ? a : gcd(b, a % b); 0055 } 0056 0057 static QSize ratio(const QSize &size) 0058 { 0059 const int divisor = gcd(size.width(), size.height()); 0060 return size / divisor; 0061 } 0062 0063 struct CropWidgetPrivate : public QWidget { 0064 CropWidget *q; 0065 QList<QWidget *> mAdvancedWidgets; 0066 QWidget *mPreserveAspectRatioWidget = nullptr; 0067 QCheckBox *advancedCheckBox = nullptr; 0068 QComboBox *ratioComboBox = nullptr; 0069 QSpinBox *widthSpinBox = nullptr; 0070 QSpinBox *heightSpinBox = nullptr; 0071 QSpinBox *leftSpinBox = nullptr; 0072 QSpinBox *topSpinBox = nullptr; 0073 QCheckBox *preserveAspectRatioCheckBox = nullptr; 0074 QDialogButtonBox *dialogButtonBox = nullptr; 0075 0076 Document::Ptr mDocument; 0077 CropTool *mCropTool = nullptr; 0078 bool mUpdatingFromCropTool; 0079 bool mUpdatingFromWidthSpinBox = false; 0080 bool mUpdatingFromHeightSpinBox = false; 0081 int mCurrentImageComboBoxIndex; 0082 int mCropRatioComboBoxCurrentIndex; 0083 0084 bool ratioIsConstrained() const 0085 { 0086 return cropRatio() > 0; 0087 } 0088 0089 QSizeF chosenRatio() const 0090 { 0091 // A size of 0 represents no ratio, i.e. the combobox is empty 0092 if (ratioComboBox->currentText().isEmpty()) { 0093 return QSizeF(0, 0); 0094 } 0095 0096 // A preset ratio is selected 0097 const int index = ratioComboBox->currentIndex(); 0098 if (index != -1 && ratioComboBox->currentText() == ratioComboBox->itemText(index)) { 0099 return ratioComboBox->currentData().toSizeF(); 0100 } 0101 0102 // A custom ratio has been entered, extract ratio from the text 0103 // If invalid, return zero size instead 0104 const QStringList lst = ratioComboBox->currentText().split(QLatin1Char(':')); 0105 if (lst.size() != 2) { 0106 return QSizeF(0, 0); 0107 } 0108 bool ok; 0109 const double width = lst[0].toDouble(&ok); 0110 if (!ok) { 0111 return QSizeF(0, 0); 0112 } 0113 const double height = lst[1].toDouble(&ok); 0114 if (!ok) { 0115 return QSizeF(0, 0); 0116 } 0117 0118 // Valid custom value 0119 return QSizeF(width, height); 0120 } 0121 0122 void setChosenRatio(QSizeF size) const 0123 { 0124 // Size matches preset ratio, let's set the combobox to that 0125 const int index = ratioComboBox->findData(size); 0126 if (index >= 0) { 0127 ratioComboBox->setCurrentIndex(index); 0128 return; 0129 } 0130 0131 // Deselect whatever was selected if anything 0132 ratioComboBox->setCurrentIndex(-1); 0133 0134 // If size is 0 (represents blank combobox, i.e., unrestricted) 0135 if (size.isEmpty()) { 0136 ratioComboBox->clearEditText(); 0137 return; 0138 } 0139 0140 // Size must be custom ratio, convert to text and add to combobox 0141 QString ratioString = QStringLiteral("%1:%2").arg(size.width()).arg(size.height()); 0142 ratioComboBox->setCurrentText(ratioString); 0143 } 0144 0145 double cropRatio() const 0146 { 0147 if (q->advancedSettingsEnabled()) { 0148 const QSizeF size = chosenRatio(); 0149 if (size.isEmpty()) { 0150 return 0; 0151 } 0152 return size.height() / size.width(); 0153 } 0154 0155 if (q->preserveAspectRatio()) { 0156 const QSizeF size = ratio(mDocument->size()); 0157 return size.height() / size.width(); 0158 } 0159 0160 return 0; 0161 } 0162 0163 void addRatioToComboBox(const QSizeF &size, const QString &label = QString()) 0164 { 0165 const QString text = label.isEmpty() ? QStringLiteral("%1:%2").arg(size.width()).arg(size.height()) : label; 0166 ratioComboBox->addItem(text, QVariant(size)); 0167 } 0168 0169 void addSectionHeaderToComboBox(const QString &title) 0170 { 0171 // Insert a line 0172 ratioComboBox->insertSeparator(ratioComboBox->count()); 0173 0174 // Insert our section header 0175 // This header is made of a separator with a text. We reset 0176 // Qt::AccessibleDescriptionRole to the header text otherwise QComboBox 0177 // delegate will draw a separator line instead of our text. 0178 int index = ratioComboBox->count(); 0179 ratioComboBox->insertSeparator(index); 0180 ratioComboBox->setItemText(index, title); 0181 ratioComboBox->setItemData(index, title, Qt::AccessibleDescriptionRole); 0182 ratioComboBox->setItemData(index, Qt::AlignHCenter, Qt::TextAlignmentRole); 0183 } 0184 0185 void initRatioComboBox() 0186 { 0187 QList<QSizeF> ratioList; 0188 const qreal sqrt2 = qSqrt(2.); 0189 ratioList << QSizeF(16, 9) << QSizeF(7, 5) << QSizeF(3, 2) << QSizeF(4, 3) << QSizeF(5, 4); 0190 0191 addRatioToComboBox(ratio(mDocument->size()), i18n("Current Image")); 0192 mCurrentImageComboBoxIndex = ratioComboBox->count() - 1; // We need to refer to this ratio later 0193 0194 addRatioToComboBox(QSizeF(1, 1), i18n("Square")); 0195 const QScreen *screen = QGuiApplication::screenAt(QCursor::pos()); 0196 if (screen) { 0197 addRatioToComboBox(ratio(screen->geometry().size()), i18n("This Screen")); 0198 } 0199 addSectionHeaderToComboBox(i18n("Landscape")); 0200 0201 for (const QSizeF &size : qAsConst(ratioList)) { 0202 addRatioToComboBox(size); 0203 } 0204 addRatioToComboBox(QSizeF(sqrt2, 1), i18n("ISO (A4, A3...)")); 0205 addRatioToComboBox(QSizeF(11, 8.5), i18n("US Letter")); 0206 addSectionHeaderToComboBox(i18n("Portrait")); 0207 for (QSizeF size : qAsConst(ratioList)) { 0208 size.transpose(); 0209 addRatioToComboBox(size); 0210 } 0211 addRatioToComboBox(QSizeF(1, sqrt2), i18n("ISO (A4, A3...)")); 0212 addRatioToComboBox(QSizeF(8.5, 11), i18n("US Letter")); 0213 0214 ratioComboBox->setMaxVisibleItems(ratioComboBox->count()); 0215 ratioComboBox->clearEditText(); 0216 0217 auto edit = qobject_cast<QLineEdit *>(ratioComboBox->lineEdit()); 0218 Q_ASSERT(edit); 0219 // Do not use i18n("%1:%2") because ':' should not be translated, it is 0220 // used to parse the ratio string. 0221 edit->setPlaceholderText(QStringLiteral("%1:%2").arg(i18n("Width"), i18n("Height"))); 0222 0223 // Enable clear button 0224 edit->setClearButtonEnabled(true); 0225 // Must manually adjust minimum width because the auto size adjustment doesn't take the 0226 // clear button into account 0227 const int width = ratioComboBox->minimumSizeHint().width(); 0228 ratioComboBox->setMinimumWidth(width + 24); 0229 0230 mCropRatioComboBoxCurrentIndex = -1; 0231 ratioComboBox->setCurrentIndex(mCropRatioComboBoxCurrentIndex); 0232 } 0233 0234 QRect cropRect() const 0235 { 0236 const QRect rect(leftSpinBox->value(), topSpinBox->value(), widthSpinBox->value(), heightSpinBox->value()); 0237 return rect; 0238 } 0239 0240 void initSpinBoxes() 0241 { 0242 const QSize size = mDocument->size(); 0243 leftSpinBox->setMaximum(size.width()); 0244 widthSpinBox->setMaximum(size.width()); 0245 topSpinBox->setMaximum(size.height()); 0246 heightSpinBox->setMaximum(size.height()); 0247 0248 // When users change the crop rectangle, QSpinBox::setMaximum will be called 0249 // again, which then adapts the sizeHint due to a different maximum number 0250 // of digits, leading to horizontal movement in the layout. This can be 0251 // avoided by setting the minimum width so it fits the largest value possible. 0252 leftSpinBox->setMinimumWidth(leftSpinBox->sizeHint().width()); 0253 widthSpinBox->setMinimumWidth(widthSpinBox->sizeHint().width()); 0254 topSpinBox->setMinimumWidth(topSpinBox->sizeHint().width()); 0255 heightSpinBox->setMinimumWidth(heightSpinBox->sizeHint().width()); 0256 } 0257 0258 void initDialogButtonBox() 0259 { 0260 QPushButton *cropButton = dialogButtonBox->button(QDialogButtonBox::Ok); 0261 cropButton->setIcon(QIcon::fromTheme(QStringLiteral("transform-crop-and-resize"))); 0262 cropButton->setText(i18n("Crop")); 0263 0264 QObject::connect(dialogButtonBox, &QDialogButtonBox::accepted, q, &CropWidget::cropRequested); 0265 QObject::connect(dialogButtonBox, &QDialogButtonBox::rejected, q, &CropWidget::done); 0266 QPushButton *resetButton = dialogButtonBox->button(QDialogButtonBox::Reset); 0267 connect(resetButton, &QPushButton::clicked, q, &CropWidget::reset); 0268 } 0269 0270 QWidget *boxWidget(QWidget *parent = nullptr) 0271 { 0272 auto widget = new QWidget(parent); 0273 auto layout = new QHBoxLayout(widget); 0274 layout->setContentsMargins(0, 0, 0, 0); 0275 layout->setSpacing(2); 0276 return widget; 0277 } 0278 0279 void setupUi(QWidget *cropWidget) 0280 { 0281 cropWidget->setObjectName(QStringLiteral("CropWidget")); 0282 0283 auto flowLayout = new FlowLayout(cropWidget, 6, 0); 0284 flowLayout->setObjectName(QStringLiteral("CropWidgetFlowLayout")); 0285 flowLayout->setAlignment(Qt::AlignCenter); 0286 flowLayout->setVerticalSpacing(6); 0287 0288 // (1) Checkbox 0289 QWidget *box = boxWidget(cropWidget); 0290 advancedCheckBox = new QCheckBox(i18nc("@option:check", "Advanced settings"), box); 0291 advancedCheckBox->setFocusPolicy(Qt::NoFocus); 0292 box->layout()->addWidget(advancedCheckBox); 0293 flowLayout->addWidget(box); 0294 flowLayout->addSpacing(14); 0295 0296 // (2) Ratio combobox (Advanced settings) 0297 box = boxWidget(cropWidget); 0298 mAdvancedWidgets << box; 0299 auto label = new QLabel(i18nc("@label:listbox", "Aspect ratio:"), box); 0300 label->setContentsMargins(4, 4, 4, 4); 0301 box->layout()->addWidget(label); 0302 ratioComboBox = new QComboBox(box); 0303 ratioComboBox->setEditable(true); 0304 ratioComboBox->setInsertPolicy(QComboBox::NoInsert); 0305 box->layout()->addWidget(ratioComboBox); 0306 flowLayout->addWidget(box); 0307 flowLayout->addSpacing(8); 0308 0309 // (3) Size spinboxes (Advanced settings) 0310 box = boxWidget(cropWidget); 0311 mAdvancedWidgets << box; 0312 label = new QLabel(i18nc("@label:spinbox", "Size:"), box); 0313 label->setContentsMargins(4, 4, 4, 4); 0314 box->layout()->addWidget(label); 0315 0316 auto innerLayout = new QHBoxLayout(); 0317 innerLayout->setSpacing(3); 0318 0319 widthSpinBox = new QSpinBox(box); 0320 widthSpinBox->setAlignment(Qt::AlignCenter); 0321 innerLayout->addWidget(widthSpinBox); 0322 0323 heightSpinBox = new QSpinBox(box); 0324 heightSpinBox->setAlignment(Qt::AlignCenter); 0325 innerLayout->addWidget(heightSpinBox); 0326 0327 box->layout()->addItem(innerLayout); 0328 flowLayout->addWidget(box); 0329 flowLayout->addSpacing(8); 0330 0331 // (4) Position spinboxes (Advanced settings) 0332 box = boxWidget(cropWidget); 0333 mAdvancedWidgets << box; 0334 label = new QLabel(i18nc("@label:spinbox", "Position:"), box); 0335 label->setContentsMargins(4, 4, 4, 4); 0336 box->layout()->addWidget(label); 0337 0338 innerLayout = new QHBoxLayout(); 0339 innerLayout->setSpacing(3); 0340 0341 leftSpinBox = new QSpinBox(box); 0342 leftSpinBox->setAlignment(Qt::AlignCenter); 0343 innerLayout->addWidget(leftSpinBox); 0344 0345 topSpinBox = new QSpinBox(box); 0346 topSpinBox->setAlignment(Qt::AlignCenter); 0347 innerLayout->addWidget(topSpinBox); 0348 0349 box->layout()->addItem(innerLayout); 0350 flowLayout->addWidget(box); 0351 flowLayout->addSpacing(18); 0352 0353 // (5) Preserve ratio checkbox 0354 mPreserveAspectRatioWidget = boxWidget(cropWidget); 0355 preserveAspectRatioCheckBox = new QCheckBox(i18nc("@option:check", "Preserve aspect ratio"), mPreserveAspectRatioWidget); 0356 mPreserveAspectRatioWidget->layout()->addWidget(preserveAspectRatioCheckBox); 0357 flowLayout->addWidget(mPreserveAspectRatioWidget); 0358 flowLayout->addSpacing(18); 0359 0360 // (6) Dialog buttons 0361 box = boxWidget(cropWidget); 0362 dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Reset | QDialogButtonBox::Ok, box); 0363 box->layout()->addWidget(dialogButtonBox); 0364 flowLayout->addWidget(box); 0365 } 0366 }; 0367 0368 CropWidget::CropWidget(QWidget *parent, RasterImageView *imageView, CropTool *cropTool) 0369 : QWidget(parent) 0370 , d(new CropWidgetPrivate) 0371 { 0372 setWindowFlags(Qt::Tool); 0373 d->q = this; 0374 d->mDocument = imageView->document(); 0375 d->mUpdatingFromCropTool = false; 0376 d->mCropTool = cropTool; 0377 d->setupUi(this); 0378 setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); 0379 0380 connect(d->advancedCheckBox, &QCheckBox::toggled, this, &CropWidget::slotAdvancedCheckBoxToggled); 0381 for (auto w : qAsConst(d->mAdvancedWidgets)) { 0382 w->setVisible(false); 0383 } 0384 0385 connect(d->preserveAspectRatioCheckBox, &QCheckBox::toggled, this, &CropWidget::applyRatioConstraint); 0386 0387 d->initRatioComboBox(); 0388 0389 connect(d->mCropTool, &CropTool::rectUpdated, this, &CropWidget::setCropRect); 0390 0391 connect(d->leftSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &CropWidget::slotPositionChanged); 0392 connect(d->topSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &CropWidget::slotPositionChanged); 0393 connect(d->widthSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &CropWidget::slotWidthChanged); 0394 connect(d->heightSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &CropWidget::slotHeightChanged); 0395 0396 d->initDialogButtonBox(); 0397 0398 // We need to listen for both signals because the combobox is multi-function: 0399 // Text Changed: required so that manual ratio entry is detected (index doesn't change) 0400 // Index Changed: required so that choosing an item with the same text is detected (e.g. going from US Letter portrait 0401 // to US Letter landscape) 0402 connect(d->ratioComboBox, &QComboBox::editTextChanged, this, &CropWidget::slotRatioComboBoxChanged); 0403 connect(d->ratioComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &CropWidget::slotRatioComboBoxChanged); 0404 0405 // Don't do this before signals are connected, otherwise the tool won't get 0406 // initialized 0407 d->initSpinBoxes(); 0408 0409 setCropRect(d->mCropTool->rect()); 0410 } 0411 0412 CropWidget::~CropWidget() 0413 { 0414 delete d; 0415 } 0416 0417 void CropWidget::setAdvancedSettingsEnabled(bool enable) 0418 { 0419 d->advancedCheckBox->setChecked(enable); 0420 } 0421 0422 bool CropWidget::advancedSettingsEnabled() const 0423 { 0424 return d->advancedCheckBox->isChecked(); 0425 } 0426 0427 void CropWidget::setPreserveAspectRatio(bool preserve) 0428 { 0429 d->preserveAspectRatioCheckBox->setChecked(preserve); 0430 } 0431 0432 bool CropWidget::preserveAspectRatio() const 0433 { 0434 return d->preserveAspectRatioCheckBox->isChecked(); 0435 } 0436 0437 void CropWidget::setCropRatio(QSizeF size) 0438 { 0439 d->setChosenRatio(size); 0440 } 0441 0442 QSizeF CropWidget::cropRatio() const 0443 { 0444 return d->chosenRatio(); 0445 } 0446 0447 void CropWidget::reset() 0448 { 0449 d->ratioComboBox->clearEditText(); 0450 d->ratioComboBox->setCurrentIndex(-1); 0451 0452 QSize size = d->mDocument->size(); 0453 d->leftSpinBox->setValue(0); 0454 d->widthSpinBox->setValue(size.width()); 0455 d->topSpinBox->setValue(0); 0456 d->heightSpinBox->setValue(size.height()); 0457 Q_EMIT rectReset(); 0458 } 0459 0460 void CropWidget::setCropRatioIndex(int index) 0461 { 0462 d->ratioComboBox->setCurrentIndex(index); 0463 } 0464 0465 int CropWidget::cropRatioIndex() const 0466 { 0467 return d->mCropRatioComboBoxCurrentIndex; 0468 } 0469 0470 void CropWidget::setCropRect(const QRect &rect) 0471 { 0472 d->mUpdatingFromCropTool = true; 0473 d->leftSpinBox->setValue(rect.left()); 0474 d->topSpinBox->setValue(rect.top()); 0475 d->widthSpinBox->setValue(rect.width()); 0476 d->heightSpinBox->setValue(rect.height()); 0477 d->mUpdatingFromCropTool = false; 0478 } 0479 0480 void CropWidget::slotPositionChanged() 0481 { 0482 const QSize size = d->mDocument->size(); 0483 d->widthSpinBox->setMaximum(size.width() - d->leftSpinBox->value()); 0484 d->heightSpinBox->setMaximum(size.height() - d->topSpinBox->value()); 0485 0486 if (d->mUpdatingFromCropTool) { 0487 return; 0488 } 0489 d->mCropTool->setRect(d->cropRect()); 0490 } 0491 0492 void CropWidget::slotWidthChanged() 0493 { 0494 d->leftSpinBox->setMaximum(d->mDocument->width() - d->widthSpinBox->value()); 0495 0496 if (d->mUpdatingFromCropTool || d->mUpdatingFromHeightSpinBox) { 0497 return; 0498 } 0499 0500 if (d->ratioIsConstrained()) { 0501 d->mUpdatingFromWidthSpinBox = true; 0502 int height = int(d->widthSpinBox->value() * d->cropRatio()); 0503 d->heightSpinBox->setValue(height); 0504 d->mUpdatingFromWidthSpinBox = false; 0505 } 0506 d->mCropTool->setRect(d->cropRect()); 0507 } 0508 0509 void CropWidget::slotHeightChanged() 0510 { 0511 d->topSpinBox->setMaximum(d->mDocument->height() - d->heightSpinBox->value()); 0512 0513 if (d->mUpdatingFromCropTool || d->mUpdatingFromWidthSpinBox) { 0514 return; 0515 } 0516 0517 if (d->ratioIsConstrained()) { 0518 d->mUpdatingFromHeightSpinBox = true; 0519 int width = int(d->heightSpinBox->value() / d->cropRatio()); 0520 d->widthSpinBox->setValue(width); 0521 d->mUpdatingFromHeightSpinBox = false; 0522 } 0523 d->mCropTool->setRect(d->cropRect()); 0524 } 0525 0526 void CropWidget::applyRatioConstraint() 0527 { 0528 double ratio = d->cropRatio(); 0529 d->mCropTool->setCropRatio(ratio); 0530 0531 if (!d->ratioIsConstrained()) { 0532 return; 0533 } 0534 QRect rect = d->cropRect(); 0535 rect.setHeight(int(rect.width() * ratio)); 0536 d->mCropTool->setRect(rect); 0537 } 0538 0539 void CropWidget::slotAdvancedCheckBoxToggled(bool checked) 0540 { 0541 for (auto w : qAsConst(d->mAdvancedWidgets)) { 0542 w->setVisible(checked); 0543 } 0544 d->mPreserveAspectRatioWidget->setVisible(!checked); 0545 applyRatioConstraint(); 0546 } 0547 0548 void CropWidget::slotRatioComboBoxChanged() 0549 { 0550 const QString text = d->ratioComboBox->currentText(); 0551 0552 // If text cleared, clear the current item as well 0553 if (text.isEmpty()) { 0554 d->ratioComboBox->setCurrentIndex(-1); 0555 } 0556 0557 // We want to keep track of the selected ratio, including when the user has entered a custom ratio 0558 // or cleared the text. We can't simply use currentIndex() because this stays >= 0 when the user manually 0559 // enters text. We also can't set the current index to -1 when there is no match like above because that 0560 // interferes when manually entering text. 0561 // Furthermore, since there can be duplicate text items, we can't rely on findText() as it will stop on 0562 // the first match it finds. Therefore we must check if there's a match, and if so, get the index directly. 0563 if (d->ratioComboBox->findText(text) >= 0) { 0564 d->mCropRatioComboBoxCurrentIndex = d->ratioComboBox->currentIndex(); 0565 } else { 0566 d->mCropRatioComboBoxCurrentIndex = -1; 0567 } 0568 0569 applyRatioConstraint(); 0570 } 0571 0572 void CropWidget::updateCropRatio() 0573 { 0574 // First we need to re-calculate the "Current Image" ratio in case the user rotated the image 0575 d->ratioComboBox->setItemData(d->mCurrentImageComboBoxIndex, QVariant(ratio(d->mDocument->size()))); 0576 0577 // Always re-apply the constraint, even though we only need to when the user has "Current Image" 0578 // selected or the "Preserve aspect ratio" checked, since there's no harm 0579 applyRatioConstraint(); 0580 // If the ratio is unrestricted, calling applyRatioConstraint doesn't update the rect, so we call 0581 // this manually to make sure the rect is adjusted to fit within the image 0582 d->mCropTool->setRect(d->mCropTool->rect()); 0583 } 0584 0585 } // namespace 0586 0587 #include "moc_cropwidget.cpp"