1 // Copyright (C) 2012-2019 The VPaint Developers.
2 // See the COPYRIGHT file at the top-level directory of this distribution
3 // and at https://github.com/dalboris/vpaint/blob/master/COPYRIGHT
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16
17 #include "BackgroundWidget.h"
18
19 #include "Background.h"
20 #include "BackgroundUrlValidator.h"
21 #include "../ColorSelector.h"
22 #include "../Global.h"
23
24 #include <QFormLayout>
25 #include <QHBoxLayout>
26 #include <QVBoxLayout>
27 #include <QGridLayout>
28 #include <QLabel>
29 #include <QLineEdit>
30 #include <QPushButton>
31 #include <QCheckBox>
32 #include <QDoubleSpinBox>
33 #include <QComboBox>
34 #include <QFileDialog>
35 #include <QTextEdit>
36 #include <QDialogButtonBox>
37
BackgroundWidget(QWidget * parent)38 BackgroundWidget::BackgroundWidget(QWidget * parent) :
39 QWidget(parent),
40 background_(nullptr),
41 isUpdatingFromBackground_(false),
42 isBeingEdited_(false)
43 {
44 // Clarify that there is one background per layer
45 QVBoxLayout * whichLayerAndFormLayout = new QVBoxLayout();
46 setLayout(whichLayerAndFormLayout);
47 whichLayerAndFormLayout->addWidget(new QLabel(tr("(Note: each layer has its own background)")));
48
49 // Form layout (contains everything except the clarification note above)
50 QFormLayout * layout = new QFormLayout();
51 whichLayerAndFormLayout->addLayout(layout);
52 whichLayerAndFormLayout->addStretch();
53
54 // Color
55 colorSelector_ = new ColorSelector(Qt::transparent);
56 colorSelector_->setToolTip(tr("Set background color"));
57 colorSelector_->setStatusTip(tr("Set background color, possibly transparent."));
58 layout->addRow(tr("Color:"), colorSelector_);
59 connect(colorSelector_, SIGNAL(colorChanged(Color)),
60 this, SLOT(processColorSelectorColorChanged_(Color)));
61
62 // Images
63 imageLineEdit_ = new QLineEdit();
64 imageLineEdit_->setValidator(new BackgroundUrlValidator(imageLineEdit_));
65 imageLineEdit_->setToolTip(tr("Set background image(s) url\n\n"
66 "Example 1: 'image.png' for the same image at all frames\n"
67 "Example 2: 'image*.png' for 'image2.png' on frame 2, etc."));
68 imageLineEdit_->setStatusTip(tr("Set background image(s) url. For example, set "
69 "'image.png' for a fixed image shared across all frames, "
70 ", or set 'image*.png' for 'image1.png' at frame 1, "
71 "'image2.png' at frame 2, etc. Paths must be relative to "
72 "where the vec file is saved."));
73 imageBrowseButton_ = new QPushButton("...");
74 imageBrowseButton_->setToolTip(tr("Browse for background image(s)"));
75 imageBrowseButton_->setStatusTip(tr("Browse for background image(s). Select two or more files, "
76 "and a pattern of the form 'image*.png' will be automatically "
77 "detected, loading all images matching patterns even if not selected."));
78 imageBrowseButton_->setMaximumWidth(30);
79 imageRefreshButton_ = new QPushButton(QIcon(":/images/refresh.png"), tr(""));
80 imageRefreshButton_->setToolTip(tr("Reload background image(s)"));
81 imageRefreshButton_->setStatusTip(tr("Reload background image(s) to reflect changes on disk."));
82 imageRefreshButton_->setMaximumWidth(30);
83 QHBoxLayout * imagesLayout = new QHBoxLayout();
84 #ifdef Q_OS_MAC
85 imagesLayout->setSpacing(10);
86 #else
87 imagesLayout->setSpacing(0);
88 #endif
89 imagesLayout->addWidget(imageLineEdit_);
90 imagesLayout->addWidget(imageBrowseButton_);
91 imagesLayout->addWidget(imageRefreshButton_);
92 layout->addRow(tr("Image(s):"), imagesLayout);
93 connect(imageLineEdit_, SIGNAL(editingFinished()),
94 this, SLOT(processImageLineEditEditingFinished_()));
95 connect(imageBrowseButton_, SIGNAL(clicked(bool)),
96 this, SLOT(processImageBrowseButtonClicked_()));
97 connect(imageRefreshButton_, SIGNAL(clicked(bool)),
98 this, SLOT(processImageRefreshButtonClicked_()));
99
100 // Position
101 leftSpinBox_ = new QDoubleSpinBox();
102 leftSpinBox_->setToolTip(tr("X coordinate of top-left corner of background image(s)"));
103 leftSpinBox_->setStatusTip(tr("Set the X coordinate of the position of the top-left corner of background image(s)."));
104 leftSpinBox_->setMaximumWidth(80);
105 leftSpinBox_->setMinimum(-1e6);
106 leftSpinBox_->setMaximum(1e6);
107 topSpinBox_ = new QDoubleSpinBox();
108 topSpinBox_->setToolTip(tr("Y coordinate of top-left corner of background image(s)"));
109 topSpinBox_->setStatusTip(tr("Set the Y coordinate of the position of the top-left corner of background image(s)."));
110 topSpinBox_->setMaximumWidth(80);
111 topSpinBox_->setMinimum(-1e6);
112 topSpinBox_->setMaximum(1e6);
113 QHBoxLayout * positionLayout = new QHBoxLayout();
114 positionLayout->addWidget(leftSpinBox_);
115 positionLayout->addWidget(topSpinBox_);
116 layout->addRow(tr("Position:"), positionLayout);
117 connect(leftSpinBox_, SIGNAL(valueChanged(double)),
118 this, SLOT(processLeftSpinBoxValueChanged_(double)));
119 connect(topSpinBox_, SIGNAL(valueChanged(double)),
120 this, SLOT(processTopSpinBoxValueChanged_(double)));
121 connect(leftSpinBox_, SIGNAL(editingFinished()),
122 this, SLOT(processLeftSpinBoxEditingFinished_()));
123 connect(topSpinBox_, SIGNAL(editingFinished()),
124 this, SLOT(processTopSpinBoxEditingFinished_()));
125
126 // Size
127 sizeComboBox_ = new QComboBox();
128 sizeComboBox_->setToolTip(tr("Set size of background image(s)"));
129 sizeComboBox_->setStatusTip(tr("Set the size of background image(s)."));
130 sizeComboBox_->addItem(tr("Fit to canvas"));
131 sizeComboBox_->addItem(tr("Manual"));
132 sizeComboBox_->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred));
133 widthSpinBox_ = new QDoubleSpinBox();
134 widthSpinBox_->setToolTip(tr("Width of background image(s)"));
135 widthSpinBox_->setStatusTip(tr("Set width of background image(s)."));
136 widthSpinBox_->setMaximumWidth(80);
137 widthSpinBox_->setMinimum(-1e6);
138 widthSpinBox_->setMaximum(1e6);
139 widthSpinBox_->setValue(1280);
140 heightSpinBox_ = new QDoubleSpinBox();
141 heightSpinBox_->setToolTip(tr("Height of background image(s)"));
142 heightSpinBox_->setStatusTip(tr("Set height of background image(s)."));
143 heightSpinBox_->setMaximumWidth(80);
144 heightSpinBox_->setMinimum(-1e6);
145 heightSpinBox_->setMaximum(1e6);
146 heightSpinBox_->setValue(720);
147 QGridLayout * sizeLayout = new QGridLayout();
148 sizeLayout->addWidget(sizeComboBox_, 0, 0, 1, 2);
149 sizeLayout->addWidget(widthSpinBox_, 1, 0);
150 sizeLayout->addWidget(heightSpinBox_, 1, 1);
151 layout->addRow(tr("Size:"), sizeLayout);
152 connect(sizeComboBox_, SIGNAL(currentIndexChanged(int)),
153 this, SLOT(processSizeComboBoxCurrentIndexChanged_(int)));
154 connect(widthSpinBox_, SIGNAL(valueChanged(double)),
155 this, SLOT(processWidthSpinBoxValueChanged_(double)));
156 connect(heightSpinBox_, SIGNAL(valueChanged(double)),
157 this, SLOT(processHeightSpinBoxValueChanged_(double)));
158 connect(widthSpinBox_, SIGNAL(editingFinished()),
159 this, SLOT(processWidthSpinBoxEditingFinished_()));
160 connect(heightSpinBox_, SIGNAL(editingFinished()),
161 this, SLOT(processHeightSpinBoxEditingFinished_()));
162
163 // Repeat
164 repeatComboBox_ = new QComboBox();
165 repeatComboBox_->setToolTip(tr("Repeat background image(s)"));
166 repeatComboBox_->setStatusTip(tr("Set whether background image(s) should "
167 "be repeated, either horizontally, vertically, or both"));
168 repeatComboBox_->addItem(tr("No"));
169 repeatComboBox_->addItem(tr("Horizontally"));
170 repeatComboBox_->addItem(tr("Vertically"));
171 repeatComboBox_->addItem(tr("Both"));
172 repeatComboBox_->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred));
173 layout->addRow(tr("Repeat:"), repeatComboBox_);
174 connect(repeatComboBox_, SIGNAL(currentIndexChanged(int)),
175 this, SLOT(processRepeatComboBoxCurrentIndexChanged_(int)));
176
177 // Opacity
178 opacitySpinBox_ = new QDoubleSpinBox();
179 opacitySpinBox_->setToolTip(tr("Opacity of background image(s)"));
180 opacitySpinBox_->setStatusTip(tr("Set the opacity of background image(s). Note: this does "
181 "not affect the opacity of the background color (use an alpha "
182 "value for the color instead)."));
183 opacitySpinBox_->setMaximumWidth(80);
184 opacitySpinBox_->setMinimum(0);
185 opacitySpinBox_->setMaximum(1);
186 opacitySpinBox_->setSingleStep(0.1);
187 opacitySpinBox_->setValue(1.0);
188 layout->addRow(tr("Opacity:"), opacitySpinBox_);
189 connect(opacitySpinBox_, SIGNAL(valueChanged(double)),
190 this, SLOT(processOpacitySpinBoxValueChanged_(double)));
191 connect(opacitySpinBox_, SIGNAL(editingFinished()),
192 this, SLOT(processOpacitySpinBoxEditingFinished_()));
193
194 // Hold
195 holdCheckBox_ = new QCheckBox();
196 holdCheckBox_->setToolTip(tr("Hold background image(s)"));
197 holdCheckBox_->setStatusTip(tr("Set whether to hold background image(s). Example: 'image*.png'"
198 " with only 'image01.png' and 'image03.png' on disk. At "
199 "frame 2, if hold is checked, 'image01.png' appears. If hold is "
200 "not checked, no image appears, unless 'image.png' exists in which "
201 "case it is used as a fallback value."));
202 holdCheckBox_->setChecked(true );
203 layout->addRow(tr("Hold:"), holdCheckBox_);
204 connect(holdCheckBox_, SIGNAL(toggled(bool)),
205 this, SLOT(processHoldCheckBoxToggled_(bool)));
206
207 // Set no background
208 setBackground(nullptr);
209 }
210
setBackground(Background * background)211 void BackgroundWidget::setBackground(Background * background)
212 {
213 // Disconnect previous connections. This assumes that background_
214 // is either nullptr or not destroyed yet, which is ensured by
215 // onBackgroundDestroyed_().
216 if (background_)
217 {
218 background_->disconnect(this);
219 }
220
221 // Store value
222 background_ = background;
223
224 // Disable all widgets if background_ == NULL
225 bool areChildrenEnabled = background_ ? true : false;
226 QList<QWidget*> children = findChildren<QWidget*>(QString(), Qt::FindDirectChildrenOnly);
227 foreach (QWidget * w, children)
228 w->setEnabled(areChildrenEnabled);
229
230 // Set widgets values from background values
231 updateFromBackground_();
232
233 // Update widget values when data changes
234 if (background_)
235 {
236 connect(background_, SIGNAL(destroyed()), this, SLOT(onBackgroundDestroyed_()));
237 connect(background_, SIGNAL(changed()), this, SLOT(updateFromBackground_()));
238 }
239 }
240
updateFromBackground_()241 void BackgroundWidget::updateFromBackground_()
242 {
243 if (background_)
244 {
245 // Set guard
246 isUpdatingFromBackground_ = true;
247
248 // Color
249 colorSelector_->setColor(background_->color());
250
251 // Image
252 imageLineEdit_->setText(background_->imageUrl());
253
254 // Position
255 leftSpinBox_->setValue(background_->position()[0]);
256 topSpinBox_->setValue(background_->position()[1]);
257
258 // Size
259 sizeComboBox_->setCurrentIndex((int) background_->sizeType());
260 widthSpinBox_->setValue(background_->size()[0]);
261 heightSpinBox_->setValue(background_->size()[1]);
262 switch (background_->sizeType())
263 {
264 case Background::SizeType::Cover:
265 widthSpinBox_->hide();
266 heightSpinBox_->hide();
267 break;
268 case Background::SizeType::Manual:
269 widthSpinBox_->show();
270 heightSpinBox_->show();
271 break;
272 }
273
274 // Repeat
275 repeatComboBox_->setCurrentIndex((int) background_->repeatType());
276
277 // Opacity
278 opacitySpinBox_->setValue(background_->opacity());
279
280 // Hold
281 holdCheckBox_->setChecked(background_->hold());
282
283 // Cache value before editing
284 if (!isBeingEdited_)
285 {
286 dataBeforeEditing_ = background_->data();
287 }
288
289 // Unset guard
290 isUpdatingFromBackground_ = false;
291 }
292 }
293
onBackgroundDestroyed_()294 void BackgroundWidget::onBackgroundDestroyed_()
295 {
296 background_ = nullptr;
297 setBackground(nullptr);
298 }
299
background() const300 Background * BackgroundWidget::background() const
301 {
302 return background_;
303 }
304
processColorSelectorColorChanged_(const Color & newColor)305 void BackgroundWidget::processColorSelectorColorChanged_(const Color & newColor)
306 {
307 if (background_ && !isUpdatingFromBackground_)
308 {
309 isBeingEdited_ = true;
310 background_->setColor(newColor);
311 isBeingEdited_ = false;
312 emitCheckpoint_();
313 }
314 }
315
processImageLineEditEditingFinished_()316 void BackgroundWidget::processImageLineEditEditingFinished_()
317 {
318 if (background_ && !isUpdatingFromBackground_)
319 {
320 isBeingEdited_ = true;
321 background_->setImageUrl(imageLineEdit_->text());
322 isBeingEdited_ = false;
323 emitCheckpoint_();
324 }
325 }
326
327
328 namespace
329 {
330 class InconsistentFileNamesDialog: public QDialog
331 {
332 public:
InconsistentFileNamesDialog(QWidget * parent=0)333 InconsistentFileNamesDialog(QWidget * parent = 0) :
334 QDialog(parent)
335 {
336 // Window title
337 setWindowTitle("Inconsistent file names");
338
339 // Label with warning message and pattern
340 label_ = new QLabel;
341 label_->setWordWrap(true);
342
343 // Text edit with list of inconsistent file names
344 textEdit_ = new QTextEdit;
345 textEdit_->setReadOnly(true);
346
347 // Button box
348 QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
349 connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
350
351 // Layout
352 QVBoxLayout * layout = new QVBoxLayout(this);
353 layout->addWidget(label_);
354 layout->addWidget(textEdit_);
355 layout->addWidget(buttonBox);
356 setLayout(layout);
357 }
358
setPattern(const QString & pattern)359 void setPattern(const QString & pattern)
360 {
361 label_->setText(
362 tr("Warning: The selected files do not have consistent names. "
363 "The detected pattern is \"%1\", but the following files "
364 "do not match it and therefore will be ignored:").arg(pattern));
365 }
366
setFileNames(const QStringList & fileNames)367 void setFileNames(const QStringList & fileNames)
368 {
369 QString text;
370 for (int i=0; i<fileNames.size(); ++i)
371 {
372 if (i>0)
373 text += '\n';
374 text += fileNames[i];
375 }
376 textEdit_->setText(text);
377 }
378
379 private:
380 QLabel * label_;
381 QTextEdit * textEdit_;
382 };
383 }
384
processImageBrowseButtonClicked_()385 void BackgroundWidget::processImageBrowseButtonClicked_()
386 {
387 // Get filenames
388 QDir documentDir = global()->documentDir();
389 QStringList filenames = QFileDialog::getOpenFileNames(
390 this,
391 tr("Select image, or sequence of images, to set as background"),
392 documentDir.path(),
393 tr("Image files (*.jpg *.png)"));
394
395
396 // Convert to path relative to current document
397 for (int i=0; i<filenames.size(); ++i)
398 {
399 filenames[i] = documentDir.relativeFilePath(filenames[i]);
400 }
401
402 // Detect wildcard
403 QString url;
404 if (filenames.size() == 0)
405 {
406 url = QString();
407 }
408 else if (filenames.size() == 1)
409 {
410 url = filenames[0];
411 }
412 else // filenames.size() >= 2
413 {
414 // Compute largest shared prefix of first two filenames
415 const QString & s0 = filenames[0];
416 const QString & s1 = filenames[1];
417 int prefixLength = 0;
418 while (s0.length() > prefixLength &&
419 s1.length() > prefixLength &&
420 s0[prefixLength] == s1[prefixLength])
421 {
422 prefixLength++;
423 }
424
425 // Chop digits at the end of prefix
426 while (prefixLength > 0 &&
427 s0[prefixLength-1].isDigit())
428 {
429 prefixLength--;
430 }
431
432 // Chop minus sign, unless all filenames have one, in which case it's
433 // probably intented to be a separating dash and not a minus sign
434 if (prefixLength > 0 && s0[prefixLength-1] == '-')
435 {
436 bool theyAllHaveIt = true;
437 for (int i=0; i<filenames.size(); ++i)
438 {
439 if ( (filenames[i].length() < prefixLength) ||
440 (filenames[i][prefixLength-1] != '-' ) )
441 {
442 theyAllHaveIt = false;
443 break;
444 }
445 }
446
447 if (!theyAllHaveIt)
448 prefixLength--;
449 }
450
451 // Read wildcard of s0
452 int s0WildcardLength = 0;
453 if (s0.length() == prefixLength)
454 {
455 // That's weird, but might be the fallback value with
456 // a wildcard at the end (i.e., without file extension)
457 s0WildcardLength = 0;
458 }
459 else if (s0[prefixLength] == '-')
460 {
461 // s0 wildcard is negative
462 s0WildcardLength++;
463 while (s0.length() > prefixLength+s0WildcardLength &&
464 s0[prefixLength+s0WildcardLength].isDigit())
465 {
466 s0WildcardLength++;
467 }
468 }
469 else if (s0[prefixLength].isDigit())
470 {
471 // s0 wildcard is positive
472 while (s0.length() > prefixLength+s0WildcardLength &&
473 s0[prefixLength+s0WildcardLength].isDigit())
474 {
475 s0WildcardLength++;
476 }
477 }
478 else
479 {
480 // Might be the fallback value
481 s0WildcardLength = 0;
482 }
483
484 // Deduce prefix and suffix
485 int suffixLength = s0.length() - prefixLength - s0WildcardLength;
486 QString prefix = s0.left(prefixLength);
487 QString suffix = s0.right(suffixLength);
488
489 // Set url
490 url = prefix + "*" + suffix;
491
492 // Check for inconsistent names
493 QStringList inconsistentFilenames;
494 for (int i=0; i<filenames.size(); ++i)
495 {
496 // Check that prefix and suffix match
497 if (filenames[i].left(prefixLength) != prefix ||
498 filenames[i].right(suffixLength) != suffix )
499 {
500 inconsistentFilenames << filenames[i];
501 }
502
503 // Check that wildcard can be converted to an int
504 else
505 {
506 // Get wildcard and convert to int
507 QString w = filenames[i].mid(prefixLength, filenames[i].length() - url.length() + 1);
508 bool canConvertToInt;
509 w.toInt(&canConvertToInt);
510
511 // Add to inconsistent names if it cannot be converted to an int
512 // (unless length == 0, in which case it's the fallback value)
513 if (w.length() > 0 && !canConvertToInt)
514 {
515 inconsistentFilenames << filenames[i];
516 }
517 }
518 }
519
520 // Display warning to user if some file names are inconsistent
521 if (inconsistentFilenames.length() > 0)
522 {
523 // issue warning
524 InconsistentFileNamesDialog dialog(this);
525 dialog.setPattern(url);
526 dialog.setFileNames(inconsistentFilenames);
527 dialog.exec();
528 }
529 }
530
531 // Set image url
532 if (background_ && !isUpdatingFromBackground_)
533 {
534 isBeingEdited_ = true;
535 background_->setImageUrl(url);
536 isBeingEdited_ = false;
537 emitCheckpoint_();
538 }
539 }
540
processImageRefreshButtonClicked_()541 void BackgroundWidget::processImageRefreshButtonClicked_()
542 {
543 if (background_)
544 {
545 background_->clearCache();
546 }
547 }
548
processLeftSpinBoxValueChanged_(double newLeft)549 void BackgroundWidget::processLeftSpinBoxValueChanged_(double newLeft)
550 {
551 if (background_ && !isUpdatingFromBackground_)
552 {
553 isBeingEdited_ = true;
554 double top = background_->position()[1];
555 background_->setPosition(Eigen::Vector2d(newLeft, top));
556 isBeingEdited_ = false;
557 }
558 }
559
processTopSpinBoxValueChanged_(double newTop)560 void BackgroundWidget::processTopSpinBoxValueChanged_(double newTop)
561 {
562 if (background_ && !isUpdatingFromBackground_)
563 {
564 isBeingEdited_ = true;
565 double left = background_->position()[0];
566 background_->setPosition(Eigen::Vector2d(left, newTop));
567 isBeingEdited_ = false;
568 }
569 }
570
processLeftSpinBoxEditingFinished_()571 void BackgroundWidget::processLeftSpinBoxEditingFinished_()
572 {
573 emitCheckpoint_();
574 }
575
processTopSpinBoxEditingFinished_()576 void BackgroundWidget::processTopSpinBoxEditingFinished_()
577 {
578 emitCheckpoint_();
579 }
580
processSizeComboBoxCurrentIndexChanged_(int newSizeType)581 void BackgroundWidget::processSizeComboBoxCurrentIndexChanged_(int newSizeType)
582 {
583 if (background_ && !isUpdatingFromBackground_)
584 {
585 isBeingEdited_ = true;
586 background_->setSizeType(static_cast<Background::SizeType>(newSizeType));
587 isBeingEdited_ = false;
588 emitCheckpoint_();
589 }
590 }
591
processWidthSpinBoxValueChanged_(double newWidth)592 void BackgroundWidget::processWidthSpinBoxValueChanged_(double newWidth)
593 {
594 if (background_ && !isUpdatingFromBackground_)
595 {
596 isBeingEdited_ = true;
597 double height = background_->size()[1];
598 background_->setSize(Eigen::Vector2d(newWidth, height));
599 isBeingEdited_ = false;
600 }
601 }
602
processHeightSpinBoxValueChanged_(double newHeight)603 void BackgroundWidget::processHeightSpinBoxValueChanged_(double newHeight)
604 {
605 if (background_ && !isUpdatingFromBackground_)
606 {
607 isBeingEdited_ = true;
608 double width = background_->size()[0];
609 background_->setSize(Eigen::Vector2d(width, newHeight));
610 isBeingEdited_ = false;
611 }
612 }
613
processWidthSpinBoxEditingFinished_()614 void BackgroundWidget::processWidthSpinBoxEditingFinished_()
615 {
616 emitCheckpoint_();
617 }
618
processHeightSpinBoxEditingFinished_()619 void BackgroundWidget::processHeightSpinBoxEditingFinished_()
620 {
621 emitCheckpoint_();
622 }
623
processRepeatComboBoxCurrentIndexChanged_(int newRepeatType)624 void BackgroundWidget::processRepeatComboBoxCurrentIndexChanged_(int newRepeatType)
625 {
626 if (background_ && !isUpdatingFromBackground_)
627 {
628 isBeingEdited_ = true;
629 background_->setRepeatType(static_cast<Background::RepeatType>(newRepeatType));
630 isBeingEdited_ = false;
631 emitCheckpoint_();
632 }
633 }
634
processOpacitySpinBoxValueChanged_(double newOpacity)635 void BackgroundWidget::processOpacitySpinBoxValueChanged_(double newOpacity)
636 {
637 if (background_ && !isUpdatingFromBackground_)
638 {
639 isBeingEdited_ = true;
640 background_->setOpacity(newOpacity);
641 isBeingEdited_ = false;
642 }
643 }
644
processOpacitySpinBoxEditingFinished_()645 void BackgroundWidget::processOpacitySpinBoxEditingFinished_()
646 {
647 emitCheckpoint_();
648 }
649
processHoldCheckBoxToggled_(bool newHold)650 void BackgroundWidget::processHoldCheckBoxToggled_(bool newHold)
651 {
652 if (background_ && !isUpdatingFromBackground_)
653 {
654 isBeingEdited_ = true;
655 background_->setHold(newHold);
656 isBeingEdited_ = false;
657 emitCheckpoint_();
658 }
659 }
660
emitCheckpoint_()661 void BackgroundWidget::emitCheckpoint_()
662 {
663 if (background_ && (background_->data() != dataBeforeEditing_))
664 {
665 dataBeforeEditing_ = background_->data();
666 background_->emitCheckpoint();
667 }
668 }
669