34
34
#include <klocale.h>
37
#include <lib/documentview/rasterimageview.h>
37
38
#include "croptool.h"
38
#include "imageview.h"
39
39
#include "signalblocker.h"
40
40
#include "ui_cropwidget.h"
45
45
// Euclidean algorithm to compute the greatest common divisor of two integers.
47
47
// http://en.wikipedia.org/wiki/Euclidean_algorithm
48
static int gcd(int a, int b) {
49
return b == 0 ? a : gcd(b, a % b);
53
static QSize screenRatio() {
54
const QRect rect = QApplication::desktop()->screenGeometry();
55
const int width = rect.width();
56
const int height = rect.height();
57
const int divisor = gcd(width, height);
58
return QSize(width / divisor, height / divisor);
48
static int gcd(int a, int b)
50
return b == 0 ? a : gcd(b, a % b);
53
static QSize screenRatio()
55
const QRect rect = QApplication::desktop()->screenGeometry();
56
const int width = rect.width();
57
const int height = rect.height();
58
const int divisor = gcd(width, height);
59
return QSize(width / divisor, height / divisor);
62
62
struct CropWidgetPrivate : public Ui_CropWidget {
65
Document::Ptr mDocument;
67
bool mUpdatingFromCropTool;
70
bool ratioIsConstrained() const {
71
return cropRatio() > 0;
75
double cropRatio() const {
76
const QStringList lst = ratioComboBox->currentText().split(':');
77
if (lst.size() != 2) {
82
const double width = lst[0].toDouble(&ok);
86
const double height = lst[1].toDouble(&ok);
91
return height / width;
95
void addRatioToComboBox(const QSize& size, const QString& label = QString()) {
96
QString text = QString("%1:%2").arg(size.width()).arg(size.height());
97
if (!label.isEmpty()) {
98
text += QString(" (%1)").arg(label);
100
ratioComboBox->addItem(text, QVariant(size));
104
void addSeparatorToComboBox() {
105
ratioComboBox->insertSeparator(ratioComboBox->count());
109
void initRatioComboBox() {
110
QList<QSize> ratioList;
119
addRatioToComboBox(QSize(1, 1), i18n("Square"));
120
addRatioToComboBox(screenRatio(), i18n("This Screen"));
121
addSeparatorToComboBox();
123
Q_FOREACH(const QSize& size, ratioList) {
124
addRatioToComboBox(size);
126
addSeparatorToComboBox();
127
Q_FOREACH(QSize size, ratioList) {
129
addRatioToComboBox(size);
132
ratioComboBox->setMaxVisibleItems(ratioComboBox->count());
133
ratioComboBox->setEditText(QString());
135
KLineEdit* edit = qobject_cast<KLineEdit*>(ratioComboBox->lineEdit());
137
// Do not use i18n("%1:%2") because ':' should not be translated, it is
138
// used to parse the ratio string.
139
edit->setClickMessage(QString("%1:%2").arg(i18n("Width")).arg(i18n("Height")));
143
QRect cropRect() const {
145
leftSpinBox->value(),
147
widthSpinBox->value(),
148
heightSpinBox->value()
154
void initSpinBoxes() {
155
QSize size = mDocument->size();
156
leftSpinBox->setMaximum(size.width());
157
widthSpinBox->setMaximum(size.width());
158
topSpinBox->setMaximum(size.height());
159
heightSpinBox->setMaximum(size.height());
163
void initCropButton() {
164
cropButton->setIcon(KIcon("transform-crop-and-resize"));
166
QObject::connect(cropButton, SIGNAL(clicked()),
167
that, SIGNAL(cropRequested()) );
65
Document::Ptr mDocument;
67
bool mUpdatingFromCropTool;
69
bool ratioIsConstrained() const {
70
return cropRatio() > 0;
73
double cropRatio() const {
74
const QStringList lst = ratioComboBox->currentText().split(':');
75
if (lst.size() != 2) {
80
const double width = lst[0].toDouble(&ok);
84
const double height = lst[1].toDouble(&ok);
89
return height / width;
92
void addRatioToComboBox(const QSize& size, const QString& label = QString())
94
QString text = QString("%1:%2").arg(size.width()).arg(size.height());
95
if (!label.isEmpty()) {
96
text += QString(" (%1)").arg(label);
98
ratioComboBox->addItem(text, QVariant(size));
101
void addSeparatorToComboBox()
103
ratioComboBox->insertSeparator(ratioComboBox->count());
106
void initRatioComboBox()
108
QList<QSize> ratioList;
117
addRatioToComboBox(QSize(1, 1), i18n("Square"));
118
addRatioToComboBox(screenRatio(), i18n("This Screen"));
119
addSeparatorToComboBox();
121
Q_FOREACH(const QSize & size, ratioList) {
122
addRatioToComboBox(size);
124
addSeparatorToComboBox();
125
Q_FOREACH(QSize size, ratioList) {
127
addRatioToComboBox(size);
130
ratioComboBox->setMaxVisibleItems(ratioComboBox->count());
131
ratioComboBox->setEditText(QString());
133
KLineEdit* edit = qobject_cast<KLineEdit*>(ratioComboBox->lineEdit());
135
// Do not use i18n("%1:%2") because ':' should not be translated, it is
136
// used to parse the ratio string.
137
edit->setClickMessage(QString("%1:%2").arg(i18n("Width")).arg(i18n("Height")));
140
QRect cropRect() const {
142
leftSpinBox->value(),
144
widthSpinBox->value(),
145
heightSpinBox->value()
152
QSize size = mDocument->size();
153
leftSpinBox->setMaximum(size.width());
154
widthSpinBox->setMaximum(size.width());
155
topSpinBox->setMaximum(size.height());
156
heightSpinBox->setMaximum(size.height());
159
void initDialogButtonBox()
161
QPushButton* cropButton = dialogButtonBox->button(QDialogButtonBox::Ok);
162
cropButton->setIcon(KIcon("transform-crop-and-resize"));
163
cropButton->setText(i18n("Crop"));
165
QObject::connect(dialogButtonBox, SIGNAL(accepted()),
166
q, SIGNAL(cropRequested()));
167
QObject::connect(dialogButtonBox, SIGNAL(rejected()),
172
CropWidget::CropWidget(QWidget* parent, ImageView* imageView, CropTool* cropTool)
172
CropWidget::CropWidget(QWidget* parent, RasterImageView* imageView, CropTool* cropTool)
173
173
: QWidget(parent)
174
, d(new CropWidgetPrivate) {
175
setWindowFlags(Qt::Tool);
177
d->mDocument = imageView->document();
178
d->mUpdatingFromCropTool = false;
179
d->mCropTool = cropTool;
181
setFont(KGlobalSettings::smallestReadableFont());
182
layout()->setMargin(KDialog::marginHint());
183
layout()->setSizeConstraint(QLayout::SetFixedSize);
185
connect(d->advancedCheckBox, SIGNAL(toggled(bool)),
186
d->advancedWidget, SLOT(setVisible(bool)));
187
d->advancedWidget->setVisible(false);
188
d->advancedWidget->layout()->setMargin(0);
190
d->initRatioComboBox();
192
connect(d->mCropTool, SIGNAL(rectUpdated(const QRect&)),
193
SLOT(setCropRect(const QRect&)) );
195
connect(d->leftSpinBox, SIGNAL(valueChanged(int)),
196
SLOT(slotPositionChanged()) );
197
connect(d->topSpinBox, SIGNAL(valueChanged(int)),
198
SLOT(slotPositionChanged()) );
199
connect(d->widthSpinBox, SIGNAL(valueChanged(int)),
200
SLOT(slotWidthChanged()) );
201
connect(d->heightSpinBox, SIGNAL(valueChanged(int)),
202
SLOT(slotHeightChanged()) );
206
connect(d->ratioComboBox, SIGNAL(editTextChanged(const QString&)),
207
SLOT(slotRatioComboBoxEditTextChanged()) );
208
connect(d->ratioComboBox, SIGNAL(activated(int)),
209
SLOT(slotRatioComboBoxActivated()) );
211
// Don't do this before signals are connected, otherwise the tool won't get
215
setCropRect(d->mCropTool->rect());
219
CropWidget::~CropWidget() {
224
void CropWidget::setCropRect(const QRect& rect) {
225
d->mUpdatingFromCropTool = true;
226
d->leftSpinBox->setValue(rect.left());
227
d->topSpinBox->setValue(rect.top());
228
d->widthSpinBox->setValue(rect.width());
229
d->heightSpinBox->setValue(rect.height());
230
d->mUpdatingFromCropTool = false;
234
void CropWidget::slotPositionChanged() {
235
const QSize size = d->mDocument->size();
236
d->widthSpinBox->setMaximum(size.width() - d->leftSpinBox->value());
237
d->heightSpinBox->setMaximum(size.height() - d->topSpinBox->value());
239
if (d->mUpdatingFromCropTool) {
242
d->mCropTool->setRect(d->cropRect());
246
void CropWidget::slotWidthChanged() {
247
d->leftSpinBox->setMaximum(d->mDocument->width() - d->widthSpinBox->value());
249
if (d->mUpdatingFromCropTool) {
252
if (d->ratioIsConstrained()) {
253
int height = int(d->widthSpinBox->value() * d->cropRatio());
254
d->heightSpinBox->setValue(height);
256
d->mCropTool->setRect(d->cropRect());
260
void CropWidget::slotHeightChanged() {
261
d->topSpinBox->setMaximum(d->mDocument->height() - d->heightSpinBox->value());
263
if (d->mUpdatingFromCropTool) {
266
if (d->ratioIsConstrained()) {
267
int width = int(d->heightSpinBox->value() / d->cropRatio());
268
d->widthSpinBox->setValue(width);
270
d->mCropTool->setRect(d->cropRect());
274
void CropWidget::applyRatioConstraint() {
275
double ratio = d->cropRatio();
276
d->mCropTool->setCropRatio(ratio);
278
if (!d->ratioIsConstrained()) {
281
QRect rect = d->cropRect();
282
rect.setHeight(int(rect.width() * ratio));
283
d->mCropTool->setRect(rect);
287
void CropWidget::slotRatioComboBoxEditTextChanged() {
288
applyRatioConstraint();
292
void CropWidget::slotRatioComboBoxActivated() {
293
// If the ratioComboBox contains text like this: "w:h (foo bar)", change it
294
// to "w:h" only, so that it's easier to edit for the user.
295
QStringList lst = d->ratioComboBox->currentText().split(' ');
296
if (lst.size() > 1) {
297
SignalBlocker blocker(d->ratioComboBox);
298
d->ratioComboBox->setEditText(lst[0]);
299
applyRatioConstraint();
174
, d(new CropWidgetPrivate)
176
setWindowFlags(Qt::Tool);
178
d->mDocument = imageView->document();
179
d->mUpdatingFromCropTool = false;
180
d->mCropTool = cropTool;
182
setFont(KGlobalSettings::smallestReadableFont());
183
layout()->setMargin(KDialog::marginHint());
184
layout()->setSizeConstraint(QLayout::SetFixedSize);
186
connect(d->advancedCheckBox, SIGNAL(toggled(bool)),
187
d->advancedWidget, SLOT(setVisible(bool)));
188
d->advancedWidget->setVisible(false);
189
d->advancedWidget->layout()->setMargin(0);
191
d->initRatioComboBox();
193
connect(d->mCropTool, SIGNAL(rectUpdated(QRect)),
194
SLOT(setCropRect(QRect)));
196
connect(d->leftSpinBox, SIGNAL(valueChanged(int)),
197
SLOT(slotPositionChanged()));
198
connect(d->topSpinBox, SIGNAL(valueChanged(int)),
199
SLOT(slotPositionChanged()));
200
connect(d->widthSpinBox, SIGNAL(valueChanged(int)),
201
SLOT(slotWidthChanged()));
202
connect(d->heightSpinBox, SIGNAL(valueChanged(int)),
203
SLOT(slotHeightChanged()));
205
d->initDialogButtonBox();
207
connect(d->ratioComboBox, SIGNAL(editTextChanged(QString)),
208
SLOT(slotRatioComboBoxEditTextChanged()));
209
connect(d->ratioComboBox, SIGNAL(activated(int)),
210
SLOT(slotRatioComboBoxActivated()));
212
// Don't do this before signals are connected, otherwise the tool won't get
216
setCropRect(d->mCropTool->rect());
219
CropWidget::~CropWidget()
224
void CropWidget::setCropRect(const QRect& rect)
226
d->mUpdatingFromCropTool = true;
227
d->leftSpinBox->setValue(rect.left());
228
d->topSpinBox->setValue(rect.top());
229
d->widthSpinBox->setValue(rect.width());
230
d->heightSpinBox->setValue(rect.height());
231
d->mUpdatingFromCropTool = false;
234
void CropWidget::slotPositionChanged()
236
const QSize size = d->mDocument->size();
237
d->widthSpinBox->setMaximum(size.width() - d->leftSpinBox->value());
238
d->heightSpinBox->setMaximum(size.height() - d->topSpinBox->value());
240
if (d->mUpdatingFromCropTool) {
243
d->mCropTool->setRect(d->cropRect());
246
void CropWidget::slotWidthChanged()
248
d->leftSpinBox->setMaximum(d->mDocument->width() - d->widthSpinBox->value());
250
if (d->mUpdatingFromCropTool) {
253
if (d->ratioIsConstrained()) {
254
int height = int(d->widthSpinBox->value() * d->cropRatio());
255
d->heightSpinBox->setValue(height);
257
d->mCropTool->setRect(d->cropRect());
260
void CropWidget::slotHeightChanged()
262
d->topSpinBox->setMaximum(d->mDocument->height() - d->heightSpinBox->value());
264
if (d->mUpdatingFromCropTool) {
267
if (d->ratioIsConstrained()) {
268
int width = int(d->heightSpinBox->value() / d->cropRatio());
269
d->widthSpinBox->setValue(width);
271
d->mCropTool->setRect(d->cropRect());
274
void CropWidget::applyRatioConstraint()
276
double ratio = d->cropRatio();
277
d->mCropTool->setCropRatio(ratio);
279
if (!d->ratioIsConstrained()) {
282
QRect rect = d->cropRect();
283
rect.setHeight(int(rect.width() * ratio));
284
d->mCropTool->setRect(rect);
287
void CropWidget::slotRatioComboBoxEditTextChanged()
289
applyRatioConstraint();
292
void CropWidget::slotRatioComboBoxActivated()
294
// If the ratioComboBox contains text like this: "w:h (foo bar)", change it
295
// to "w:h" only, so that it's easier to edit for the user.
296
QStringList lst = d->ratioComboBox->currentText().split(' ');
297
if (lst.size() > 1) {
298
SignalBlocker blocker(d->ratioComboBox);
299
d->ratioComboBox->setEditText(lst[0]);
300
applyRatioConstraint();