1 
2 
3 // TnzCore includes
4 #include "tpalette.h"
5 #include "tcolorstyles.h"
6 #include "tundo.h"
7 
8 // TnzBase includes
9 #include "tproperty.h"
10 
11 // TnzLib includes
12 #include "toonz/palettecontroller.h"
13 #include "toonz/tpalettehandle.h"
14 #include "toonz/tobjecthandle.h"
15 #include "toonz/stage2.h"
16 #include "toonz/doubleparamcmd.h"
17 #include "toonz/preferences.h"
18 
19 // TnzQt includes
20 #include "toonzqt/gutil.h"
21 
22 // TnzTools includes
23 #include "tools/tool.h"
24 #include "rasterselectiontool.h"
25 #include "vectorselectiontool.h"
26 // to enable the increase/decrease shortcuts while hiding the tool option
27 #include "tools/toolhandle.h"
28 // to enable shortcuts only when the viewer is focused
29 #include "tools/tooloptions.h"
30 
31 // Qt includes
32 #include <QPainter>
33 #include <QVBoxLayout>
34 #include <QMouseEvent>
35 #include <QAction>
36 #include <QLabel>
37 #include <QMainWindow>
38 #include <QButtonGroup>
39 #include <QMenu>
40 #include <QListView>
41 
42 #include "tooloptionscontrols.h"
43 
44 using namespace DVGui;
45 
46 //***********************************************************************************
47 //    ToolOptionControl  implementation
48 //***********************************************************************************
49 
ToolOptionControl(TTool * tool,std::string propertyName,ToolHandle * toolHandle)50 ToolOptionControl::ToolOptionControl(TTool *tool, std::string propertyName,
51                                      ToolHandle *toolHandle)
52     : m_tool(tool), m_propertyName(propertyName), m_toolHandle(toolHandle) {}
53 
54 //-----------------------------------------------------------------------------
55 
notifyTool(bool addToUndo)56 void ToolOptionControl::notifyTool(bool addToUndo) {
57   std::string tempPropertyName = m_propertyName;
58   if (addToUndo && m_propertyName == "Maximum Gap")
59     tempPropertyName = tempPropertyName + "withUndo";
60   m_tool->onPropertyChanged(tempPropertyName);
61 }
62 
63 //-----------------------------------------------------------------------------
64 /*! return true if the control is belonging to the visible combo viewer. very
65  * dirty implementation.
66  */
isInVisibleViewer(QWidget * widget)67 bool ToolOptionControl::isInVisibleViewer(QWidget *widget) {
68   if (!widget) return false;
69 
70   if (widget->isVisible()) return true;
71 
72   ToolOptionsBox *parentTOB =
73       dynamic_cast<ToolOptionsBox *>(widget->parentWidget());
74   if (!parentTOB) return false;
75 
76   ToolOptions *grandParentTO =
77       dynamic_cast<ToolOptions *>(parentTOB->parentWidget());
78   if (!grandParentTO) return false;
79 
80   // There must be a QFrame between a ComboViewerPanel and a ToolOptions
81   QFrame *greatGrandParentFrame =
82       dynamic_cast<QFrame *>(grandParentTO->parentWidget());
83   if (!greatGrandParentFrame) return false;
84 
85   return greatGrandParentFrame->isVisible();
86 }
87 
88 //***********************************************************************************
89 //    ToolOptionControl derivative  implementations
90 //***********************************************************************************
91 
ToolOptionCheckbox(TTool * tool,TBoolProperty * property,ToolHandle * toolHandle,QWidget * parent)92 ToolOptionCheckbox::ToolOptionCheckbox(TTool *tool, TBoolProperty *property,
93                                        ToolHandle *toolHandle, QWidget *parent)
94     : CheckBox(parent)
95     , ToolOptionControl(tool, property->getName(), toolHandle)
96     , m_property(property) {
97   setText(property->getQStringName());
98   m_property->addListener(this);
99   updateStatus();
100   // synchronize the state with the same widgets in other tool option bars
101   if (toolHandle)
102     connect(this, SIGNAL(clicked(bool)), toolHandle, SIGNAL(toolChanged()));
103 }
104 
105 //-----------------------------------------------------------------------------
106 
updateStatus()107 void ToolOptionCheckbox::updateStatus() {
108   bool check = m_property->getValue();
109 
110   if (!actions().isEmpty() && actions()[0]->isCheckable() &&
111       actions()[0]->isChecked() != check)
112     actions()[0]->setChecked(check);
113 
114   if (isChecked() == check) return;
115 
116   setCheckState(check ? Qt::Checked : Qt::Unchecked);
117 }
118 
119 //-----------------------------------------------------------------------------
120 
nextCheckState()121 void ToolOptionCheckbox::nextCheckState() {
122   QAbstractButton::nextCheckState();
123   m_property->setValue(checkState() == Qt::Checked);
124   notifyTool();
125 }
126 
127 //-----------------------------------------------------------------------------
128 
doClick(bool checked)129 void ToolOptionCheckbox::doClick(bool checked) {
130   if (m_toolHandle && m_toolHandle->getTool() != m_tool) return;
131   // active only if the belonging combo-viewer is visible
132   if (!isInVisibleViewer(this)) return;
133 
134   if (isChecked() == checked) return;
135 
136   setChecked(checked);
137   m_property->setValue(checked);
138   notifyTool();
139 
140   // for updating a cursor without any effect to the tool options
141   if (m_toolHandle) m_toolHandle->notifyToolCursorTypeChanged();
142 }
143 
144 //=============================================================================
145 
ToolOptionSlider(TTool * tool,TDoubleProperty * property,ToolHandle * toolHandle)146 ToolOptionSlider::ToolOptionSlider(TTool *tool, TDoubleProperty *property,
147                                    ToolHandle *toolHandle)
148     : DoubleField()
149     , ToolOptionControl(tool, property->getName(), toolHandle)
150     , m_property(property) {
151   setLinearSlider(property->isLinearSlider());
152   m_property->addListener(this);
153   TDoubleProperty::Range range = property->getRange();
154   setRange(range.first, range.second);
155 
156   // calculate maximum text length which includes length for decimals (for now
157   // it's fixed to 2) and period
158   int textMaxLength = std::max(QString::number((int)range.first).length(),
159                                QString::number((int)range.second).length()) +
160                       m_lineEdit->getDecimals() + 1;
161   QString txt;
162   // set the maximum width of the widget according to the text length (with 5
163   // pixels margin)
164   txt.fill('0', textMaxLength);
165   int widgetWidth = fontMetrics().width(txt) + 5;
166   m_lineEdit->parentWidget()->setMaximumWidth(widgetWidth);
167   // set the maximum width of the slider to 250 pixels
168   setMaximumWidth(250 + widgetWidth);
169   setMinimumWidth(50 + widgetWidth);
170   updateStatus();
171   connect(this, SIGNAL(valueChanged(bool)), SLOT(onValueChanged(bool)));
172   // synchronize the state with the same widgets in other tool option bars
173   if (toolHandle)
174     connect(this, SIGNAL(valueEditedByHand()), toolHandle,
175             SIGNAL(toolChanged()));
176 }
177 
178 //-----------------------------------------------------------------------------
179 
updateStatus()180 void ToolOptionSlider::updateStatus() {
181   double v = m_property->getValue();
182   if (getValue() == v) return;
183 
184   setValue(v);
185 }
186 
187 //-----------------------------------------------------------------------------
188 
onValueChanged(bool isDragging)189 void ToolOptionSlider::onValueChanged(bool isDragging) {
190   m_property->setValue(getValue());
191   notifyTool(!isDragging);
192 }
193 
194 //-----------------------------------------------------------------------------
195 
increase(double step)196 void ToolOptionSlider::increase(double step) {
197   if (m_toolHandle && m_toolHandle->getTool() != m_tool) return;
198   // active only if the belonging combo-viewer is visible
199   if (!isInVisibleViewer(this)) return;
200 
201   double value = getValue();
202   double minValue, maxValue;
203   getRange(minValue, maxValue);
204 
205   value += step;
206   if (value > maxValue) value = maxValue;
207 
208   setValue(value);
209   m_property->setValue(getValue());
210   notifyTool();
211   // update the interface
212   repaint();
213 }
214 
215 //-----------------------------------------------------------------------------
216 
increaseFractional()217 void ToolOptionSlider::increaseFractional() { increase(0.06); }
218 
219 //-----------------------------------------------------------------------------
220 
decrease(double step)221 void ToolOptionSlider::decrease(double step) {
222   if (m_toolHandle && m_toolHandle->getTool() != m_tool) return;
223   // active only if the belonging combo-viewer is visible
224   if (!isInVisibleViewer(this)) return;
225 
226   double value = getValue();
227   double minValue, maxValue;
228   getRange(minValue, maxValue);
229 
230   value -= step;
231   if (value < minValue) value = minValue;
232 
233   setValue(value);
234   m_property->setValue(getValue());
235   notifyTool();
236   // update the interface
237   repaint();
238 }
239 
240 //-----------------------------------------------------------------------------
241 
decreaseFractional()242 void ToolOptionSlider::decreaseFractional() { decrease(0.06); }
243 
244 //=============================================================================
245 
ToolOptionPairSlider(TTool * tool,TDoublePairProperty * property,const QString & leftName,const QString & rightName,ToolHandle * toolHandle)246 ToolOptionPairSlider::ToolOptionPairSlider(TTool *tool,
247                                            TDoublePairProperty *property,
248                                            const QString &leftName,
249                                            const QString &rightName,
250                                            ToolHandle *toolHandle)
251     : DoublePairField(0, property->isMaxRangeLimited())
252     , ToolOptionControl(tool, property->getName(), toolHandle)
253     , m_property(property) {
254   setLinearSlider(property->isLinearSlider());
255   m_property->addListener(this);
256   TDoublePairProperty::Value value = property->getValue();
257   TDoublePairProperty::Range range = property->getRange();
258   setRange(range.first, range.second);
259 
260   // calculate maximum text length which includes length for decimals (for now
261   // it's fixed to 2) and period
262   int textMaxLength = std::max(QString::number((int)range.first).length(),
263                                QString::number((int)range.second).length()) +
264                       m_leftLineEdit->getDecimals() + 1;
265   QString txt;
266   // set the maximum width of the widget according to the text length (with 5
267   // pixels margin)
268   txt.fill('0', textMaxLength);
269   int widgetWidth = fontMetrics().width(txt) + 5;
270   m_leftLineEdit->setFixedWidth(widgetWidth);
271   m_rightLineEdit->setFixedWidth(widgetWidth);
272   m_leftMargin  = widgetWidth + 12;
273   m_rightMargin = widgetWidth + 12;
274   // set the maximum width of the slider to 300 pixels
275   setMaximumWidth(300 + m_leftMargin + m_rightMargin);
276   setMinimumWidth(120 + m_leftMargin + m_rightMargin);
277   setLeftText(leftName);
278   setRightText(rightName);
279 
280   updateStatus();
281   connect(this, SIGNAL(valuesChanged(bool)), SLOT(onValuesChanged(bool)));
282 }
283 
284 //-----------------------------------------------------------------------------
285 
updateStatus()286 void ToolOptionPairSlider::updateStatus() {
287   TDoublePairProperty::Value value = m_property->getValue();
288   setValues(value);
289 }
290 
291 //-----------------------------------------------------------------------------
292 
onValuesChanged(bool isDragging)293 void ToolOptionPairSlider::onValuesChanged(bool isDragging) {
294   m_property->setValue(getValues());
295   notifyTool();
296   // synchronize the state with the same widgets in other tool option bars
297   if (m_toolHandle) m_toolHandle->notifyToolChanged();
298 }
299 
300 //-----------------------------------------------------------------------------
301 
increaseMaxValue()302 void ToolOptionPairSlider::increaseMaxValue() {
303   if (m_toolHandle && m_toolHandle->getTool() != m_tool) return;
304   // active only if the belonging combo-viewer is visible
305   if (!isInVisibleViewer(this)) return;
306 
307   std::pair<double, double> values = getValues();
308   double minValue, maxValue;
309   getRange(minValue, maxValue);
310   values.second += 1;
311   if (values.second > maxValue) values.second = maxValue;
312   setValues(values);
313   m_property->setValue(getValues());
314   notifyTool();
315   // update the interface
316   repaint();
317 }
318 
319 //-----------------------------------------------------------------------------
320 
decreaseMaxValue()321 void ToolOptionPairSlider::decreaseMaxValue() {
322   if (m_toolHandle && m_toolHandle->getTool() != m_tool) return;
323   // active only if the belonging combo-viewer is visible
324   if (!isInVisibleViewer(this)) return;
325 
326   std::pair<double, double> values = getValues();
327   values.second -= 1;
328   if (values.second < values.first) values.second = values.first;
329   setValues(values);
330   m_property->setValue(getValues());
331   notifyTool();
332   // update the interface
333   repaint();
334 }
335 
336 //-----------------------------------------------------------------------------
337 
increaseMinValue()338 void ToolOptionPairSlider::increaseMinValue() {
339   if (m_toolHandle && m_toolHandle->getTool() != m_tool) return;
340   // active only if the belonging combo-viewer is visible
341   if (!isInVisibleViewer(this)) return;
342 
343   std::pair<double, double> values = getValues();
344   values.first += 1;
345   if (values.first > values.second) values.first = values.second;
346   setValues(values);
347   m_property->setValue(getValues());
348   notifyTool();
349   // update the interface
350   repaint();
351 }
352 
353 //-----------------------------------------------------------------------------
354 
decreaseMinValue()355 void ToolOptionPairSlider::decreaseMinValue() {
356   if (m_toolHandle && m_toolHandle->getTool() != m_tool) return;
357   // active only if the belonging combo-viewer is visible
358   if (!isInVisibleViewer(this)) return;
359 
360   std::pair<double, double> values = getValues();
361   double minValue, maxValue;
362   getRange(minValue, maxValue);
363   values.first -= 1;
364   if (values.first < minValue) values.first = minValue;
365   setValues(values);
366   m_property->setValue(getValues());
367   notifyTool();
368   // update the interface
369   repaint();
370 }
371 
372 //=============================================================================
373 
ToolOptionIntPairSlider(TTool * tool,TIntPairProperty * property,const QString & leftName,const QString & rightName,ToolHandle * toolHandle)374 ToolOptionIntPairSlider::ToolOptionIntPairSlider(TTool *tool,
375                                                  TIntPairProperty *property,
376                                                  const QString &leftName,
377                                                  const QString &rightName,
378                                                  ToolHandle *toolHandle)
379     : IntPairField(0, property->isMaxRangeLimited())
380     , ToolOptionControl(tool, property->getName(), toolHandle)
381     , m_property(property) {
382   setLinearSlider(property->isLinearSlider());
383   setLeftText(leftName);
384   setRightText(rightName);
385   m_property->addListener(this);
386   TIntPairProperty::Value value = property->getValue();
387   TIntPairProperty::Range range = property->getRange();
388   setRange(range.first, range.second);
389   setMaximumWidth(300);
390   setMinimumWidth(200);
391   updateStatus();
392   connect(this, SIGNAL(valuesChanged(bool)), SLOT(onValuesChanged(bool)));
393 }
394 
395 //-----------------------------------------------------------------------------
396 
updateStatus()397 void ToolOptionIntPairSlider::updateStatus() {
398   TIntPairProperty::Value value = m_property->getValue();
399   setValues(value);
400 }
401 
402 //-----------------------------------------------------------------------------
403 
onValuesChanged(bool isDragging)404 void ToolOptionIntPairSlider::onValuesChanged(bool isDragging) {
405   m_property->setValue(getValues());
406   notifyTool();
407   // synchronize the state with the same widgets in other tool option bars
408   if (m_toolHandle) m_toolHandle->notifyToolChanged();
409 }
410 
411 //-----------------------------------------------------------------------------
412 
increaseMaxValue()413 void ToolOptionIntPairSlider::increaseMaxValue() {
414   if (m_toolHandle && m_toolHandle->getTool() != m_tool) return;
415   // active only if the belonging combo-viewer is visible
416   if (!isInVisibleViewer(this)) return;
417 
418   std::pair<int, int> values = getValues();
419   int minValue, maxValue;
420   getRange(minValue, maxValue);
421   values.second += 1;
422 
423   // a "cross-like shape" of the brush size = 3 is hard to use. so skip it
424   if (values.second == 3 && m_tool->isPencilModeActive()) values.second += 1;
425 
426   if (values.second > maxValue) values.second = maxValue;
427   setValues(values);
428   m_property->setValue(getValues());
429   notifyTool();
430   // update the interface
431   repaint();
432 }
433 
434 //-----------------------------------------------------------------------------
435 
decreaseMaxValue()436 void ToolOptionIntPairSlider::decreaseMaxValue() {
437   if (m_toolHandle && m_toolHandle->getTool() != m_tool) return;
438   // active only if the belonging combo-viewer is visible
439   if (!isInVisibleViewer(this)) return;
440 
441   std::pair<int, int> values = getValues();
442   values.second -= 1;
443 
444   // a "cross-like shape" of the brush size = 3 is hard to use. so skip it
445   if (values.second == 3 && m_tool->isPencilModeActive()) values.second -= 1;
446 
447   if (values.second < values.first) values.second = values.first;
448   setValues(values);
449   m_property->setValue(getValues());
450   notifyTool();
451   // update the interface
452   repaint();
453 }
454 
455 //-----------------------------------------------------------------------------
456 
increaseMinValue()457 void ToolOptionIntPairSlider::increaseMinValue() {
458   if (m_toolHandle && m_toolHandle->getTool() != m_tool) return;
459   // active only if the belonging combo-viewer is visible
460   if (!isInVisibleViewer(this)) return;
461 
462   std::pair<int, int> values = getValues();
463   values.first += 1;
464   if (values.first > values.second) values.first = values.second;
465   setValues(values);
466   m_property->setValue(getValues());
467   notifyTool();
468   // update the interface
469   repaint();
470 }
471 
472 //-----------------------------------------------------------------------------
473 
decreaseMinValue()474 void ToolOptionIntPairSlider::decreaseMinValue() {
475   if (m_toolHandle && m_toolHandle->getTool() != m_tool) return;
476   // active only if the belonging combo-viewer is visible
477   if (!isInVisibleViewer(this)) return;
478 
479   std::pair<int, int> values = getValues();
480   int minValue, maxValue;
481   getRange(minValue, maxValue);
482   values.first -= 1;
483   if (values.first < minValue) values.first = minValue;
484   setValues(values);
485   m_property->setValue(getValues());
486   notifyTool();
487   // update the interface
488   repaint();
489 }
490 
491 //=============================================================================
492 
ToolOptionIntSlider(TTool * tool,TIntProperty * property,ToolHandle * toolHandle)493 ToolOptionIntSlider::ToolOptionIntSlider(TTool *tool, TIntProperty *property,
494                                          ToolHandle *toolHandle)
495     : IntField(0, property->isMaxRangeLimited())
496     , ToolOptionControl(tool, property->getName(), toolHandle)
497     , m_property(property) {
498   setLinearSlider(property->isLinearSlider());
499   m_property->addListener(this);
500   TIntProperty::Range range = property->getRange();
501   setRange(range.first, range.second);
502   setMaximumWidth(300);
503   setMinimumWidth(50);
504   updateStatus();
505   connect(this, SIGNAL(valueChanged(bool)), SLOT(onValueChanged(bool)));
506   // synchronize the state with the same widgets in other tool option bars
507   if (toolHandle)
508     connect(this, SIGNAL(valueEditedByHand()), toolHandle,
509             SIGNAL(toolChanged()));
510 }
511 
512 //-----------------------------------------------------------------------------
513 
updateStatus()514 void ToolOptionIntSlider::updateStatus() {
515   int v = m_property->getValue();
516   if (getValue() == v) return;
517 
518   setValue(v);
519 }
520 
521 //-----------------------------------------------------------------------------
522 
increase()523 void ToolOptionIntSlider::increase() {
524   if (m_toolHandle && m_toolHandle->getTool() != m_tool) return;
525   // active only if the belonging combo-viewer is visible
526   if (!isInVisibleViewer(this)) return;
527 
528   int value = getValue();
529   int minValue, maxValue;
530   getRange(minValue, maxValue);
531   value += 1;
532   // a "cross-like shape" of the brush size = 3 is hard to use. so skip it
533   if (value == 3 && m_tool->isPencilModeActive()) value += 1;
534 
535   if (value > maxValue) value = maxValue;
536 
537   setValue(value);
538   m_property->setValue(getValue());
539   notifyTool();
540   // update the interface
541   repaint();
542 }
543 
544 //-----------------------------------------------------------------------------
545 
decrease()546 void ToolOptionIntSlider::decrease() {
547   if (m_toolHandle && m_toolHandle->getTool() != m_tool) return;
548   // active only if the belonging combo-viewer is visible
549   if (!isInVisibleViewer(this)) return;
550 
551   int value = getValue();
552   int minValue, maxValue;
553   getRange(minValue, maxValue);
554   value -= 1;
555 
556   // a "cross-like shape" of the brush size = 3 is hard to use. so skip it
557   if (value == 3 && m_tool->isPencilModeActive()) value -= 1;
558 
559   if (value < minValue) value = minValue;
560 
561   setValue(value);
562   m_property->setValue(getValue());
563   notifyTool();
564   // update the interface
565   repaint();
566 }
567 
568 //-----------------------------------------------------------------------------
569 
onValueChanged(bool isDragging)570 void ToolOptionIntSlider::onValueChanged(bool isDragging) {
571   m_property->setValue(getValue());
572   notifyTool();
573 }
574 
575 //=============================================================================
576 
ToolOptionCombo(TTool * tool,TEnumProperty * property,ToolHandle * toolHandle)577 ToolOptionCombo::ToolOptionCombo(TTool *tool, TEnumProperty *property,
578                                  ToolHandle *toolHandle)
579     : QComboBox()
580     , ToolOptionControl(tool, property->getName(), toolHandle)
581     , m_property(property) {
582   setMinimumWidth(65);
583   m_property->addListener(this);
584   loadEntries();
585   setSizeAdjustPolicy(QComboBox::AdjustToContents);
586   connect(this, SIGNAL(activated(int)), this, SLOT(onActivated(int)));
587   // synchronize the state with the same widgets in other tool option bars
588   if (toolHandle) {
589     connect(this, SIGNAL(activated(int)), toolHandle, SIGNAL(toolChanged()));
590   }
591 }
592 
593 //-----------------------------------------------------------------------------
594 
reloadComboBoxList(std::string id)595 void ToolOptionCombo::reloadComboBoxList(std::string id) {
596   if (id == "" || m_property->getName() != id) return;
597   loadEntries();
598 }
599 
600 //-----------------------------------------------------------------------------
601 
loadEntries()602 void ToolOptionCombo::loadEntries() {
603   const TEnumProperty::Range &range = m_property->getRange();
604   const TEnumProperty::Items &items = m_property->getItems();
605 
606   const int count = m_property->getCount();
607   int maxWidth    = 0;
608 
609   clear();
610   bool hasIcon = false;
611   for (int i = 0; i < count; ++i) {
612     QString itemStr = QString::fromStdWString(range[i]);
613     if (items[i].iconName.isEmpty())
614       addItem(items[i].UIName, itemStr);
615     else {
616       addItem(createQIcon(items[i].iconName.toUtf8()), items[i].UIName,
617               itemStr);
618       if (!hasIcon) {
619         hasIcon = true;
620         setIconSize(QSize(18, 18));
621         // add margin between items if they are with icons
622         setView(new QListView());
623         view()->setIconSize(QSize(18, 18));
624         setStyleSheet(
625             "QComboBox  QAbstractItemView::item{ \
626                        margin: 5 0 0 0;\
627                       }");
628       }
629     }
630     int tmpWidth                      = fontMetrics().width(items[i].UIName);
631     if (tmpWidth > maxWidth) maxWidth = tmpWidth;
632   }
633 
634   // set the maximum width according to the longest item with 25 pixels for
635   // arrow button and margin
636   setMaximumWidth(maxWidth + 25 + (hasIcon ? 23 : 0));
637 
638   updateStatus();
639 }
640 
641 //-----------------------------------------------------------------------------
642 
updateStatus()643 void ToolOptionCombo::updateStatus() {
644   QString value = QString::fromStdWString(m_property->getValue());
645   int index     = findData(value);
646   if (index >= 0 && index != currentIndex()) setCurrentIndex(index);
647 }
648 
649 //-----------------------------------------------------------------------------
650 
onActivated(int index)651 void ToolOptionCombo::onActivated(int index) {
652   const TEnumProperty::Range &range = m_property->getRange();
653   if (index < 0 || index >= (int)range.size()) return;
654 
655   std::wstring item = range[index];
656   m_property->setValue(item);
657   notifyTool();
658 }
659 
660 //-----------------------------------------------------------------------------
661 
doShowPopup()662 void ToolOptionCombo::doShowPopup() {
663   if (Preferences::instance()->getDropdownShortcutsCycleOptions()) {
664     const TEnumProperty::Range &range           = m_property->getRange();
665     int theIndex                                = currentIndex() + 1;
666     if (theIndex >= (int)range.size()) theIndex = 0;
667     doOnActivated(theIndex);
668   } else {
669     if (isVisible()) showPopup();
670   }
671 }
672 
673 //-----------------------------------------------------------------------------
674 
doOnActivated(int index)675 void ToolOptionCombo::doOnActivated(int index) {
676   if (m_toolHandle && m_toolHandle->getTool() != m_tool) return;
677   // active only if the belonging combo-viewer is visible
678   if (!isInVisibleViewer(this)) return;
679   bool cycleOptions =
680       Preferences::instance()->getDropdownShortcutsCycleOptions();
681   // Just move the index if the first item is not "Normal"
682   if (m_property->indexOf(L"Normal") != 0) {
683     onActivated(index);
684     setCurrentIndex(index);
685     // for updating the cursor
686     if (m_toolHandle) m_toolHandle->notifyToolChanged();
687     return;
688   }
689 
690   // If the first item of this combo box is "Normal", enable shortcut key toggle
691   // can "back and forth" behavior.
692   if (currentIndex() == index) {
693     // estimating that the "Normal" option is located at the index 0
694     onActivated(0);
695     setCurrentIndex(0);
696   } else {
697     onActivated(index);
698     setCurrentIndex(index);
699   }
700 
701   // for updating a cursor without any effect to the tool options
702   if (m_toolHandle) m_toolHandle->notifyToolCursorTypeChanged();
703 }
704 
705 //=============================================================================
706 
ToolOptionFontCombo(TTool * tool,TEnumProperty * property,ToolHandle * toolHandle)707 ToolOptionFontCombo::ToolOptionFontCombo(TTool *tool, TEnumProperty *property,
708                                          ToolHandle *toolHandle)
709     : QFontComboBox()
710     , ToolOptionControl(tool, property->getName(), toolHandle)
711     , m_property(property) {
712   setMaximumWidth(250);
713   m_property->addListener(this);
714   setSizeAdjustPolicy(QFontComboBox::AdjustToContents);
715   connect(this, SIGNAL(activated(int)), this, SLOT(onActivated(int)));
716   // synchronize the state with the same widgets in other tool option bars
717   if (toolHandle)
718     connect(this, SIGNAL(activated(int)), toolHandle, SIGNAL(toolChanged()));
719 
720   updateStatus();
721 }
722 
723 //-----------------------------------------------------------------------------
724 
updateStatus()725 void ToolOptionFontCombo::updateStatus() {
726   QString value = QString::fromStdWString(m_property->getValue());
727   int index     = findText(value);
728   if (index >= 0 && index != currentIndex()) setCurrentIndex(index);
729 }
730 
731 //-----------------------------------------------------------------------------
732 
onActivated(int index)733 void ToolOptionFontCombo::onActivated(int index) {
734   const TEnumProperty::Range &range = m_property->getRange();
735   if (index < 0 || index >= (int)range.size()) return;
736 
737   std::wstring item = range[index];
738   m_property->setValue(item);
739   notifyTool();
740 }
741 
742 //-----------------------------------------------------------------------------
743 
doShowPopup()744 void ToolOptionFontCombo::doShowPopup() {
745   if (!isInVisibleViewer(this)) return;
746   if (Preferences::instance()->getDropdownShortcutsCycleOptions()) {
747     const TEnumProperty::Range &range           = m_property->getRange();
748     int theIndex                                = currentIndex() + 1;
749     if (theIndex >= (int)range.size()) theIndex = 0;
750     onActivated(theIndex);
751     setCurrentIndex(theIndex);
752   } else {
753     if (isVisible()) showPopup();
754   }
755 }
756 
757 //=============================================================================
758 
ToolOptionPopupButton(TTool * tool,TEnumProperty * property)759 ToolOptionPopupButton::ToolOptionPopupButton(TTool *tool,
760                                              TEnumProperty *property)
761     : PopupButton()
762     , ToolOptionControl(tool, property->getName())
763     , m_property(property) {
764   setObjectName(QString::fromStdString(property->getName()));
765   setFixedHeight(20);
766   m_property->addListener(this);
767 
768   const TEnumProperty::Items &items = m_property->getItems();
769   const int count                   = m_property->getCount();
770   for (int i = 0; i < count; ++i) {
771     QAction *action = addItem(createQIcon(items[i].iconName.toUtf8()));
772     // make the tooltip text
773     action->setToolTip(items[i].UIName);
774   }
775   setCurrentIndex(0);
776   updateStatus();
777   connect(this, SIGNAL(activated(int)), this, SLOT(onActivated(int)));
778 }
779 
780 //-----------------------------------------------------------------------------
781 
updateStatus()782 void ToolOptionPopupButton::updateStatus() {
783   int index = m_property->getIndex();
784   if (index >= 0 && index != currentIndex()) setCurrentIndex(index);
785 }
786 
787 //-----------------------------------------------------------------------------
788 
onActivated(int index)789 void ToolOptionPopupButton::onActivated(int index) {
790   const TEnumProperty::Range &range = m_property->getRange();
791   if (index < 0 || index >= (int)range.size()) return;
792 
793   std::wstring item = range[index];
794   m_property->setValue(item);
795   notifyTool();
796 }
797 
798 //-----------------------------------------------------------------------------
799 
doShowPopup()800 void ToolOptionPopupButton::doShowPopup() {
801   if (isVisible()) showMenu();
802 }
803 
804 //-----------------------------------------------------------------------------
805 
doSetCurrentIndex(int index)806 void ToolOptionPopupButton::doSetCurrentIndex(int index) {
807   if (isVisible()) setCurrentIndex(index);
808 }
809 
810 //-----------------------------------------------------------------------------
811 
doOnActivated(int index)812 void ToolOptionPopupButton::doOnActivated(int index) {
813   if (isVisible()) onActivated(index);
814 }
815 
816 //=============================================================================
817 
ToolOptionTextField(TTool * tool,TStringProperty * property)818 ToolOptionTextField::ToolOptionTextField(TTool *tool, TStringProperty *property)
819     : LineEdit()
820     , ToolOptionControl(tool, property->getName())
821     , m_property(property) {
822   setFixedSize(100, 23);
823   m_property->addListener(this);
824 
825   updateStatus();
826   connect(this, SIGNAL(editingFinished()), SLOT(onValueChanged()));
827 }
828 
829 //-----------------------------------------------------------------------------
830 
updateStatus()831 void ToolOptionTextField::updateStatus() {
832   QString newText = QString::fromStdWString(m_property->getValue());
833   if (newText == text()) return;
834 
835   setText(newText);
836 }
837 
838 //-----------------------------------------------------------------------------
839 
onValueChanged()840 void ToolOptionTextField::onValueChanged() {
841   m_property->setValue(text().toStdWString());
842   notifyTool();
843   // synchronize the state with the same widgets in other tool option bars
844   if (m_toolHandle) m_toolHandle->notifyToolChanged();
845 }
846 
847 //=============================================================================
848 
StyleIndexFieldAndChip(TTool * tool,TStyleIndexProperty * property,TPaletteHandle * pltHandle,ToolHandle * toolHandle)849 StyleIndexFieldAndChip::StyleIndexFieldAndChip(TTool *tool,
850                                                TStyleIndexProperty *property,
851                                                TPaletteHandle *pltHandle,
852                                                ToolHandle *toolHandle)
853     : StyleIndexLineEdit()
854     , ToolOptionControl(tool, property->getName(), toolHandle)
855     , m_property(property)
856     , m_pltHandle(pltHandle) {
857   m_property->addListener(this);
858 
859   updateStatus();
860   connect(this, SIGNAL(textChanged(const QString &)),
861           SLOT(onValueChanged(const QString &)));
862 
863   setPaletteHandle(pltHandle);
864   connect(pltHandle, SIGNAL(colorStyleSwitched()), SLOT(updateColor()));
865   connect(pltHandle, SIGNAL(colorStyleChanged(bool)), SLOT(updateColor()));
866 }
867 
868 //-----------------------------------------------------------------------------
869 
updateStatus()870 void StyleIndexFieldAndChip::updateStatus() {
871   QString newText = QString::fromStdWString(m_property->getValue());
872   if (newText == text()) return;
873 
874   setText(newText);
875 }
876 
877 //-----------------------------------------------------------------------------
878 
onValueChanged(const QString & changedText)879 void StyleIndexFieldAndChip::onValueChanged(const QString &changedText) {
880   QString style;
881 
882   // Aware of both "current" and translated string
883   if (!QString("current").contains(changedText) &&
884       !StyleIndexLineEdit::tr("current").contains(changedText)) {
885     int index     = changedText.toInt();
886     TPalette *plt = m_pltHandle->getPalette();
887     if (plt && index > plt->getStyleCount())
888       style = QString::number(plt->getStyleCount() - 1);
889     else
890       style = text();
891     m_property->setValue(style.toStdWString());
892   } else
893     m_property->setValue(changedText.toStdWString());
894 
895   repaint();
896   // synchronize the state with the same widgets in other tool option bars
897   if (m_toolHandle) m_toolHandle->notifyToolChanged();
898 }
899 
900 //-----------------------------------------------------------------------------
901 
updateColor()902 void StyleIndexFieldAndChip::updateColor() { repaint(); }
903 
904 //=============================================================================
905 
ToolOptionParamRelayField(TTool * tool,TDoubleParamRelayProperty * property,int decimals)906 ToolOptionParamRelayField::ToolOptionParamRelayField(
907     TTool *tool, TDoubleParamRelayProperty *property, int decimals)
908     : MeasuredDoubleLineEdit()
909     , ToolOptionControl(tool, property->getName())
910     , m_param()
911     , m_measure()
912     , m_property(property)
913     , m_globalKey()
914     , m_globalGroup() {
915   setFixedSize(70, 20);
916   m_property->addListener(this);
917 
918   setDecimals(decimals);
919   updateStatus();
920   connect(this, SIGNAL(valueChanged()), SLOT(onValueChanged()));
921 }
922 
923 //-----------------------------------------------------------------------------
924 
setGlobalKey(TBoolProperty * globalKey,TPropertyGroup * globalGroup)925 void ToolOptionParamRelayField::setGlobalKey(TBoolProperty *globalKey,
926                                              TPropertyGroup *globalGroup) {
927   m_globalKey = globalKey, m_globalGroup = globalGroup;
928 }
929 
930 //-----------------------------------------------------------------------------
931 
updateStatus()932 void ToolOptionParamRelayField::updateStatus() {
933   TDoubleParamP param(m_property->getParam());
934   if (param != m_param) {
935     // Initialize param referencing
936     m_param = param.getPointer();
937 
938     if (param) {
939       m_measure = param->getMeasure();
940       setMeasure(m_measure ? m_measure->getName() : "");
941 
942       setValue(m_property->getValue());
943     }
944   }
945 
946   if (!param) {
947     setEnabled(false);
948     m_measure = 0;
949     setText("");
950 
951     return;
952   }
953 
954   setEnabled(true);
955 
956   TMeasure *measure = param->getMeasure();
957   if (measure != m_measure) {
958     // Update measure if needed
959     m_measure = measure;
960     setMeasure(measure ? measure->getName() : "");
961   }
962 
963   double v = m_property->getValue();
964   if (getValue() == v) return;
965 
966   // Update value if needed
967   setValue(v);
968 }
969 
970 //-----------------------------------------------------------------------------
971 
onValueChanged()972 void ToolOptionParamRelayField::onValueChanged() {
973   struct locals {
974     static inline void setKeyframe(TDoubleParamRelayProperty *prop) {
975       if (!prop) return;
976 
977       TDoubleParam *param = prop->getParam().getPointer();
978       if (!param) return;
979 
980       double frame = prop->frame();
981       if (!param->isKeyframe(frame)) {
982         KeyframeSetter setter(param, -1, true);
983         setter.createKeyframe(frame);
984       }
985     }
986 
987     //-----------------------------------------------------------------------------
988 
989     struct SetValueUndo final : public TUndo {
990       TDoubleParamP m_param;      //!< The referenced param
991       double m_oldVal, m_newVal;  //!< Values before and after the set action...
992       double m_frame;             //!< ... at this frame
993 
994     public:
995       SetValueUndo(const TDoubleParamP &param, double oldVal, double newVal,
996                    double frame)
997           : m_param(param)
998           , m_oldVal(oldVal)
999           , m_newVal(newVal)
1000           , m_frame(frame) {}
1001 
1002       int getSize() const {
1003         return sizeof(SetValueUndo) + sizeof(TDoubleParam);
1004       }
1005       void undo() const { m_param->setValue(m_frame, m_oldVal); }
1006       void redo() const { m_param->setValue(m_frame, m_newVal); }
1007     };
1008   };
1009 
1010   //-----------------------------------------------------------------------------
1011 
1012   // NOTE: Values are extracted upon entry, since setting a keyframe reverts the
1013   // lineEdit
1014   // field value back to the original value (due to feedback from the param
1015   // itself)...
1016   double oldVal = m_property->getValue(), newVal = getValue(),
1017          frame = m_property->frame();
1018 
1019   TDoubleParamP param = m_property->getParam();
1020   if (!param) return;
1021 
1022   TUndoManager *manager = TUndoManager::manager();
1023   manager->beginBlock();
1024 
1025   if (m_globalKey && m_globalGroup && m_globalKey->getValue()) {
1026     // Set a keyframe for each of the TDoubleParam relayed in the globalGroup
1027     int p, pCount = m_globalGroup->getPropertyCount();
1028     for (p = 0; p != pCount; ++p) {
1029       TProperty *prop = m_globalGroup->getProperty(p);
1030       if (TDoubleParamRelayProperty *relProp =
1031               dynamic_cast<TDoubleParamRelayProperty *>(prop))
1032         locals::setKeyframe(relProp);
1033     }
1034   } else {
1035     // Set a keyframe just for our param
1036     locals::setKeyframe(m_property);
1037   }
1038 
1039   // Assign the edited value to the relayed param
1040   m_property->setValue(newVal);
1041   notifyTool();
1042 
1043   manager->add(new locals::SetValueUndo(param, oldVal, newVal, frame));
1044   manager->endBlock();
1045 }
1046 
1047 //=============================================================================
1048 //
1049 // Widget specifici di ArrowTool (derivati da ToolOptionControl)
1050 //
1051 
1052 // SPOSTA in un file a parte!
1053 
1054 using namespace DVGui;
1055 
MeasuredValueField(QWidget * parent,QString name)1056 MeasuredValueField::MeasuredValueField(QWidget *parent, QString name)
1057     : LineEdit(name, parent)
1058     , m_isGlobalKeyframe(false)
1059     , m_modified(false)
1060     , m_errorHighlighting(false)
1061     , m_precision(2) {
1062   setObjectName("MeasuredValueField");
1063 
1064   m_value = new TMeasuredValue("length");
1065   setText(QString::fromStdWString(m_value->toWideString(m_precision)));
1066   connect(this, SIGNAL(textChanged(const QString &)), this,
1067           SLOT(onTextChanged(const QString &)));
1068   connect(this, SIGNAL(editingFinished()), SLOT(commit()));
1069   connect(&m_errorHighlightingTimer, SIGNAL(timeout()), this,
1070           SLOT(errorHighlightingTick()));
1071 }
1072 
1073 //-----------------------------------------------------------------------------
1074 
~MeasuredValueField()1075 MeasuredValueField::~MeasuredValueField() { delete m_value; }
1076 
1077 //-----------------------------------------------------------------------------
1078 
setMeasure(std::string name)1079 void MeasuredValueField::setMeasure(std::string name) {
1080   // for reproducing the precision
1081   int oldPrec = -1;
1082 
1083   delete m_value;
1084   m_value = new TMeasuredValue(name != "" ? name : "dummy");
1085 
1086   setText(QString::fromStdWString(m_value->toWideString(m_precision)));
1087 }
1088 
1089 //-----------------------------------------------------------------------------
1090 
commit()1091 void MeasuredValueField::commit() {
1092   if (!m_modified && !isReturnPressed()) return;
1093   // commit is called when the field comes out of focus.
1094   // mouse drag will call this - return if coming from mouse drag.
1095   // else undo is set twice
1096   if (m_mouseEdit) {
1097     m_mouseEdit = false;
1098     return;
1099   }
1100   int err    = 1;
1101   bool isSet = m_value->setValue(text().toStdWString(), &err);
1102   m_modified = false;
1103   if (err != 0) {
1104     setText(QString::fromStdWString(m_value->toWideString(m_precision)));
1105     m_errorHighlighting = 1;
1106     if (!m_errorHighlightingTimer.isActive())
1107       m_errorHighlightingTimer.start(40);
1108   } else {
1109     if (m_errorHighlightingTimer.isActive()) m_errorHighlightingTimer.stop();
1110     m_errorHighlighting = 0;
1111     setStyleSheet("");
1112   }
1113 
1114   if (!isSet && !isReturnPressed()) return;
1115 
1116   setText(QString::fromStdWString(m_value->toWideString(m_precision)));
1117   m_modified = false;
1118   emit measuredValueChanged(m_value);
1119 }
1120 
1121 //-----------------------------------------------------------------------------
1122 
onTextChanged(const QString &)1123 void MeasuredValueField::onTextChanged(const QString &) { m_modified = true; }
1124 
1125 //-----------------------------------------------------------------------------
1126 
setValue(double v)1127 void MeasuredValueField::setValue(double v) {
1128   if (getValue() == v) return;
1129   m_value->setValue(TMeasuredValue::MainUnit, v);
1130   setText(QString::fromStdWString(m_value->toWideString(m_precision)));
1131 }
1132 
1133 //-----------------------------------------------------------------------------
1134 
getValue() const1135 double MeasuredValueField::getValue() const {
1136   return m_value->getValue(TMeasuredValue::MainUnit);
1137 }
1138 
1139 //-----------------------------------------------------------------------------
1140 
errorHighlightingTick()1141 void MeasuredValueField::errorHighlightingTick() {
1142   if (m_errorHighlighting < 0.01) {
1143     if (m_errorHighlightingTimer.isActive()) m_errorHighlightingTimer.stop();
1144     m_errorHighlighting = 0;
1145     setStyleSheet("");
1146   } else {
1147     int v               = 255 - (int)(m_errorHighlighting * 255);
1148     m_errorHighlighting = m_errorHighlighting * 0.8;
1149     int c               = 255 << 16 | v << 8 | v;
1150     setStyleSheet(QString("#MeasuredValueField {background-color:#%1}")
1151                       .arg(c, 6, 16, QLatin1Char('0')));
1152   }
1153 }
1154 
1155 //-----------------------------------------------------------------------------
1156 
setPrecision(int precision)1157 void MeasuredValueField::setPrecision(int precision) {
1158   m_precision = precision;
1159   setText(QString::fromStdWString(m_value->toWideString(m_precision)));
1160 }
1161 
1162 //-----------------------------------------------------------------------------
1163 
mousePressEvent(QMouseEvent * e)1164 void MeasuredValueField::mousePressEvent(QMouseEvent *e) {
1165   if (!isEnabled()) return;
1166   if ((e->buttons() == Qt::MiddleButton) || m_labelClicked) {
1167     m_xMouse        = e->x();
1168     m_mouseEdit     = true;
1169     m_originalValue = m_value->getValue(TMeasuredValue::CurrentUnit);
1170   } else {
1171     QLineEdit::mousePressEvent(e);
1172     if (!m_isTyping) {  // only the first click will select all
1173       selectAll();
1174       m_isTyping = true;
1175     }
1176   }
1177 }
1178 
1179 //-----------------------------------------------------------------------------
1180 
mouseMoveEvent(QMouseEvent * e)1181 void MeasuredValueField::mouseMoveEvent(QMouseEvent *e) {
1182   if (!isEnabled()) return;
1183   if ((e->buttons() == Qt::MiddleButton) || m_labelClicked) {
1184     m_value->modifyValue((e->x() - m_xMouse) / 2);
1185     setText(QString::fromStdWString(m_value->toWideString(m_precision)));
1186     m_xMouse = e->x();
1187     // measuredValueChanged to update the UI, but don't add to undo
1188     emit measuredValueChanged(m_value, false);
1189   } else
1190     QLineEdit::mouseMoveEvent(e);
1191 }
1192 
1193 //-----------------------------------------------------------------------------
1194 
mouseReleaseEvent(QMouseEvent * e)1195 void MeasuredValueField::mouseReleaseEvent(QMouseEvent *e) {
1196   if (!isEnabled()) return;
1197   // m_mouseEdit will be set false in commit
1198   if (m_mouseEdit) {
1199     // This seems redundant, but this is necessary for undo to work
1200     double valueToRestore = m_value->getValue(TMeasuredValue::CurrentUnit);
1201     m_value->setValue(TMeasuredValue::CurrentUnit, m_originalValue);
1202     setText(QString::fromStdWString(m_value->toWideString(m_precision)));
1203     emit measuredValueChanged(m_value, false);
1204     // add this to undo
1205     m_value->setValue(TMeasuredValue::CurrentUnit, valueToRestore);
1206     setText(QString::fromStdWString(m_value->toWideString(m_precision)));
1207     emit measuredValueChanged(m_value, true);
1208     clearFocus();
1209   } else
1210     QLineEdit::mouseReleaseEvent(e);
1211 }
1212 
1213 //-----------------------------------------------------------------------------
1214 
focusOutEvent(QFocusEvent * e)1215 void MeasuredValueField::focusOutEvent(QFocusEvent *e) {
1216   DVGui::LineEdit::focusOutEvent(e);
1217   m_isTyping = false;
1218 }
1219 
1220 //-----------------------------------------------------------------------------
1221 
receiveMousePress(QMouseEvent * e)1222 void MeasuredValueField::receiveMousePress(QMouseEvent *e) {
1223   m_labelClicked = true;
1224   mousePressEvent(e);
1225 }
1226 
receiveMouseMove(QMouseEvent * e)1227 void MeasuredValueField::receiveMouseMove(QMouseEvent *e) { mouseMoveEvent(e); }
1228 
receiveMouseRelease(QMouseEvent * e)1229 void MeasuredValueField::receiveMouseRelease(QMouseEvent *e) {
1230   mouseReleaseEvent(e);
1231   m_labelClicked = false;
1232 }
1233 
1234 //=============================================================================
1235 
1236 namespace {
1237 // calculate maximum field size (once) with 10 pixels margin
getMaximumWidthForEditToolField(QWidget * widget)1238 int getMaximumWidthForEditToolField(QWidget *widget) {
1239   static int fieldMaxWidth = widget->fontMetrics().width("-0000.00 field") + 10;
1240   return fieldMaxWidth;
1241 }
1242 }  // namespace
1243 
PegbarChannelField(TTool * tool,enum TStageObject::Channel actionId,QString name,TFrameHandle * frameHandle,TObjectHandle * objHandle,TXsheetHandle * xshHandle,QWidget * parent)1244 PegbarChannelField::PegbarChannelField(TTool *tool,
1245                                        enum TStageObject::Channel actionId,
1246                                        QString name, TFrameHandle *frameHandle,
1247                                        TObjectHandle *objHandle,
1248                                        TXsheetHandle *xshHandle,
1249                                        QWidget *parent)
1250     : MeasuredValueField(parent, name)
1251     , ToolOptionControl(tool, "")
1252     , m_actionId(actionId)
1253     , m_frameHandle(frameHandle)
1254     , m_objHandle(objHandle)
1255     , m_xshHandle(xshHandle)
1256     , m_scaleType(eNone) {
1257   bool ret = connect(this, SIGNAL(measuredValueChanged(TMeasuredValue *, bool)),
1258                      SLOT(onChange(TMeasuredValue *, bool)));
1259   assert(ret);
1260   // NOTA: per le unita' di misura controlla anche tpegbar.cpp
1261   switch (actionId) {
1262   case TStageObject::T_X:
1263     setMeasure("length.x");
1264     break;
1265   case TStageObject::T_Y:
1266     setMeasure("length.y");
1267     break;
1268   case TStageObject::T_Z:
1269     setMeasure("zdepth");
1270     break;
1271   case TStageObject::T_Path:
1272     setMeasure("percentage2");
1273     break;
1274   case TStageObject::T_ShearX:
1275   case TStageObject::T_ShearY:
1276     setMeasure("shear");
1277     break;
1278   case TStageObject::T_Angle:
1279     setMeasure("angle");
1280     break;
1281   case TStageObject::T_ScaleX:
1282   case TStageObject::T_ScaleY:
1283   case TStageObject::T_Scale:
1284     setMeasure("scale");
1285     break;
1286   default:
1287     setMeasure("dummy");
1288     break;
1289   }
1290 
1291   setMaximumWidth(getMaximumWidthForEditToolField(this));
1292 
1293   updateStatus();
1294 }
1295 
1296 //-----------------------------------------------------------------------------
1297 
onChange(TMeasuredValue * fld,bool addToUndo)1298 void PegbarChannelField::onChange(TMeasuredValue *fld, bool addToUndo) {
1299   if (!m_tool->isEnabled()) return;
1300 
1301   // the camera will crash with a value of 0
1302   if (m_tool->getObjectId().isCamera()) {
1303     if (fld->getMeasure()->getName() == "scale" &&
1304         fld->getValue(TMeasuredValue::MainUnit) == 0) {
1305       fld->setValue(TMeasuredValue::MainUnit, 0.0001);
1306     }
1307   }
1308   bool modifyConnectedActionId = false;
1309   if (addToUndo) TUndoManager::manager()->beginBlock();
1310   // m_firstMouseDrag is set to true only if addToUndo is false
1311   // and only for the first drag
1312   // This should always fire if addToUndo is true
1313   if (!m_firstMouseDrag) {
1314     m_before = TStageObjectValues();
1315     m_before.setFrameHandle(m_frameHandle);
1316     m_before.setObjectHandle(m_objHandle);
1317     m_before.setXsheetHandle(m_xshHandle);
1318     m_before.add(m_actionId);
1319     if (m_scaleType != eNone) {
1320       modifyConnectedActionId = true;
1321       if (m_actionId == TStageObject::T_ScaleX)
1322         m_before.add(TStageObject::T_ScaleY);
1323       else if (m_actionId == TStageObject::T_ScaleY)
1324         m_before.add(TStageObject::T_ScaleX);
1325       else
1326         modifyConnectedActionId = false;
1327     }
1328     if (m_isGlobalKeyframe) {
1329       m_before.add(TStageObject::T_Angle);
1330       m_before.add(TStageObject::T_X);
1331       m_before.add(TStageObject::T_Y);
1332       m_before.add(TStageObject::T_Z);
1333       m_before.add(TStageObject::T_SO);
1334       m_before.add(TStageObject::T_ScaleX);
1335       m_before.add(TStageObject::T_ScaleY);
1336       m_before.add(TStageObject::T_Scale);
1337       m_before.add(TStageObject::T_Path);
1338       m_before.add(TStageObject::T_ShearX);
1339       m_before.add(TStageObject::T_ShearY);
1340     }
1341     m_before.updateValues();
1342   }
1343   TStageObjectValues after;
1344   after    = m_before;
1345   double v = fld->getValue(TMeasuredValue::MainUnit);
1346   if (modifyConnectedActionId) {
1347     double oldv1 = after.getValue(0);
1348     double oldv2 = after.getValue(1);
1349     double newV;
1350     if (m_scaleType == eAR)
1351       newV = (v / oldv1) * oldv2;
1352     else
1353       newV = (v == 0) ? 10000 : (1 / v) * (oldv1 * oldv2);
1354     after.setValues(v, newV);
1355   } else
1356     after.setValue(v);
1357   after.applyValues();
1358 
1359   TTool::Viewer *viewer = m_tool->getViewer();
1360   if (viewer) m_tool->invalidate();
1361   setCursorPosition(0);
1362 
1363   if (addToUndo) {
1364     UndoStageObjectMove *undo = new UndoStageObjectMove(m_before, after);
1365     undo->setObjectHandle(m_objHandle);
1366     TUndoManager::manager()->add(undo);
1367     TUndoManager::manager()->endBlock();
1368     m_firstMouseDrag = false;
1369   }
1370   if (!addToUndo && !m_firstMouseDrag) m_firstMouseDrag = true;
1371   m_objHandle->notifyObjectIdChanged(false);
1372 }
1373 
1374 //-----------------------------------------------------------------------------
1375 
updateStatus()1376 void PegbarChannelField::updateStatus() {
1377   TXsheet *xsh         = m_tool->getXsheet();
1378   int frame            = m_tool->getFrame();
1379   TStageObjectId objId = m_tool->getObjectId();
1380   if (m_actionId == TStageObject::T_Z)
1381     setMeasure(objId.isCamera() ? "zdepth.cam" : "zdepth");
1382 
1383   double v = xsh->getStageObject(objId)->getParam(m_actionId, frame);
1384 
1385   if (getValue() == v) return;
1386   setValue(v);
1387   setCursorPosition(0);
1388 }
1389 
1390 //-----------------------------------------------------------------------------
1391 
onScaleTypeChanged(int type)1392 void PegbarChannelField::onScaleTypeChanged(int type) {
1393   m_scaleType = (ScaleType)type;
1394 }
1395 
1396 //=============================================================================
1397 
PegbarCenterField(TTool * tool,int index,QString name,TObjectHandle * objHandle,TXsheetHandle * xshHandle,QWidget * parent)1398 PegbarCenterField::PegbarCenterField(TTool *tool, int index, QString name,
1399                                      TObjectHandle *objHandle,
1400                                      TXsheetHandle *xshHandle, QWidget *parent)
1401     : MeasuredValueField(parent, name)
1402     , ToolOptionControl(tool, "")
1403     , m_index(index)
1404     , m_objHandle(objHandle)
1405     , m_xshHandle(xshHandle) {
1406   TStageObjectId objId = m_tool->getObjectId();
1407   setMeasure(m_index == 0 ? "length.x" : "length.y");
1408   connect(this, SIGNAL(measuredValueChanged(TMeasuredValue *, bool)),
1409           SLOT(onChange(TMeasuredValue *, bool)));
1410   updateStatus();
1411   setMaximumWidth(getMaximumWidthForEditToolField(this));
1412 }
1413 
1414 //-----------------------------------------------------------------------------
1415 
onChange(TMeasuredValue * fld,bool addToUndo)1416 void PegbarCenterField::onChange(TMeasuredValue *fld, bool addToUndo) {
1417   if (!m_tool->isEnabled()) return;
1418   TXsheet *xsh         = m_tool->getXsheet();
1419   int frame            = m_tool->getFrame();
1420   TStageObjectId objId = m_tool->getObjectId();
1421 
1422   TStageObject *obj = xsh->getStageObject(objId);
1423 
1424   double v                           = fld->getValue(TMeasuredValue::MainUnit);
1425   TPointD center                     = obj->getCenter(frame);
1426   if (!m_firstMouseDrag) m_oldCenter = center;
1427   if (m_index == 0)
1428     center.x = v;
1429   else
1430     center.y = v;
1431   obj->setCenter(frame, center);
1432   m_tool->invalidate();
1433 
1434   if (addToUndo) {
1435     UndoStageObjectCenterMove *undo =
1436         new UndoStageObjectCenterMove(objId, frame, m_oldCenter, center);
1437     undo->setObjectHandle(m_objHandle);
1438     undo->setXsheetHandle(m_xshHandle);
1439     TUndoManager::manager()->add(undo);
1440     m_firstMouseDrag = false;
1441   }
1442   if (!addToUndo && !m_firstMouseDrag) m_firstMouseDrag = true;
1443   m_objHandle->notifyObjectIdChanged(false);
1444 }
1445 
1446 //-----------------------------------------------------------------------------
1447 
updateStatus()1448 void PegbarCenterField::updateStatus() {
1449   TXsheet *xsh         = m_tool->getXsheet();
1450   int frame            = m_tool->getFrame();
1451   TStageObjectId objId = m_tool->getObjectId();
1452   TStageObject *obj    = xsh->getStageObject(objId);
1453   TPointD center       = obj->getCenter(frame);
1454 
1455   double v = m_index == 0 ? center.x : center.y;
1456   if (getValue() == v) return;
1457   setValue(v);
1458 }
1459 
1460 //=============================================================================
1461 
NoScaleField(TTool * tool,QString name)1462 NoScaleField::NoScaleField(TTool *tool, QString name)
1463     : MeasuredValueField(0, name), ToolOptionControl(tool, "") {
1464   TStageObjectId objId = m_tool->getObjectId();
1465   setMeasure("zdepth");
1466   connect(this, SIGNAL(measuredValueChanged(TMeasuredValue *, bool)),
1467           SLOT(onChange(TMeasuredValue *, bool)));
1468   updateStatus();
1469   setMaximumWidth(getMaximumWidthForEditToolField(this));
1470 }
1471 
1472 //-----------------------------------------------------------------------------
1473 
onChange(TMeasuredValue * fld,bool addToUndo)1474 void NoScaleField::onChange(TMeasuredValue *fld, bool addToUndo) {
1475   // addToUndo isn't needed here as the field denominator
1476   // doesn't have an undo
1477   if (!m_tool->isEnabled()) return;
1478   TXsheet *xsh         = m_tool->getXsheet();
1479   int frame            = m_tool->getFrame();
1480   TStageObjectId objId = m_tool->getObjectId();
1481   TStageObject *obj    = xsh->getStageObject(objId);
1482 
1483   if (m_isGlobalKeyframe)
1484     xsh->getStageObject(objId)->setKeyframeWithoutUndo(frame);
1485 
1486   double v = fld->getValue(TMeasuredValue::MainUnit);
1487   obj->setNoScaleZ(v);
1488   m_tool->invalidate();
1489 }
1490 
1491 //-----------------------------------------------------------------------------
1492 
updateStatus()1493 void NoScaleField::updateStatus() {
1494   TXsheet *xsh         = m_tool->getXsheet();
1495   int frame            = m_tool->getFrame();
1496   TStageObjectId objId = m_tool->getObjectId();
1497   TStageObject *obj    = xsh->getStageObject(objId);
1498 
1499   double v = obj->getNoScaleZ();
1500   if (getValue() == v) return;
1501   setValue(v);
1502 }
1503 
1504 //=============================================================================
1505 
PropertyMenuButton(QWidget * parent,TTool * tool,QList<TBoolProperty * > properties,QIcon icon,QString tooltip)1506 PropertyMenuButton::PropertyMenuButton(QWidget *parent, TTool *tool,
1507                                        QList<TBoolProperty *> properties,
1508                                        QIcon icon, QString tooltip)
1509     : QToolButton(parent)
1510     , ToolOptionControl(tool, "")
1511     , m_properties(properties) {
1512   setPopupMode(QToolButton::InstantPopup);
1513   setIcon(icon);
1514   setToolTip(tooltip);
1515 
1516   QMenu *menu                     = new QMenu(tooltip, this);
1517   if (!tooltip.isEmpty()) tooltip = tooltip + " ";
1518 
1519   QActionGroup *actiongroup = new QActionGroup(this);
1520   actiongroup->setExclusive(false);
1521   int i;
1522   for (i = 0; i < m_properties.count(); i++) {
1523     TBoolProperty *prop  = m_properties.at(i);
1524     QString propertyName = prop->getQStringName();
1525     // Se il tooltip essagnato e' contenuto nel nome della proprieta' lo levo
1526     // dalla stringa dell'azione
1527     if (propertyName.contains(tooltip)) propertyName.remove(tooltip);
1528     QAction *action = menu->addAction(propertyName);
1529     action->setCheckable(true);
1530     action->setChecked(prop->getValue());
1531     action->setData(QVariant(i));
1532     actiongroup->addAction(action);
1533   }
1534   bool ret = connect(actiongroup, SIGNAL(triggered(QAction *)),
1535                      SLOT(onActionTriggered(QAction *)));
1536   assert(ret);
1537 
1538   setMenu(menu);
1539 }
1540 
1541 //-----------------------------------------------------------------------------
1542 
updateStatus()1543 void PropertyMenuButton::updateStatus() {
1544   QMenu *m = menu();
1545   assert(m);
1546   QList<QAction *> actionList = m->actions();
1547   assert(actionList.count() == m_properties.count());
1548 
1549   int i;
1550   for (i = 0; i < m_properties.count(); i++) {
1551     TBoolProperty *prop   = m_properties.at(i);
1552     QAction *action       = actionList.at(i);
1553     bool isPropertyLocked = prop->getValue();
1554     if (action->isChecked() != isPropertyLocked)
1555       action->setChecked(isPropertyLocked);
1556   }
1557 }
1558 
1559 //-----------------------------------------------------------------------------
1560 
onActionTriggered(QAction * action)1561 void PropertyMenuButton::onActionTriggered(QAction *action) {
1562   int currentPropertyIndex = action->data().toInt();
1563   TBoolProperty *prop      = m_properties.at(currentPropertyIndex);
1564   bool isChecked           = action->isChecked();
1565   if (isChecked == prop->getValue()) return;
1566   prop->setValue(isChecked);
1567   notifyTool();
1568 
1569   emit onPropertyChanged(QString(prop->getName().c_str()));
1570 }
1571 
1572 //=============================================================================
1573 namespace {
1574 // calculate maximum field size (once) with 10 pixels margin
getMaximumWidthForSelectionToolField(QWidget * widget)1575 int getMaximumWidthForSelectionToolField(QWidget *widget) {
1576   static int fieldMaxWidth = widget->fontMetrics().width("-000.00 %") + 10;
1577   return fieldMaxWidth;
1578 }
1579 }  // namespace
1580 
1581 // id == 0 Scale X
1582 // id == 0 Scale Y
SelectionScaleField(SelectionTool * tool,int id,QString name)1583 SelectionScaleField::SelectionScaleField(SelectionTool *tool, int id,
1584                                          QString name)
1585     : MeasuredValueField(0, name), m_tool(tool), m_id(id) {
1586   bool ret = connect(this, SIGNAL(measuredValueChanged(TMeasuredValue *, bool)),
1587                      SLOT(onChange(TMeasuredValue *, bool)));
1588   assert(ret);
1589   setMeasure("scale");
1590   updateStatus();
1591 
1592   setMaximumWidth(getMaximumWidthForSelectionToolField(this));
1593 }
1594 
1595 //-----------------------------------------------------------------------------
1596 
applyChange(bool addToUndo)1597 bool SelectionScaleField::applyChange(bool addToUndo) {
1598   if (!m_tool || (m_tool->isSelectionEmpty() && !m_tool->isLevelType()))
1599     return false;
1600   using namespace DragSelectionTool;
1601   DragTool *scaleTool = createNewScaleTool(m_tool, ScaleType::GLOBAL);
1602   double p            = getValue();
1603   if (p == 0) p       = 0.00001;
1604   FourPoints points   = m_tool->getBBox();
1605   TPointD center      = m_tool->getCenter();
1606   TPointD p0M         = points.getPoint(7);
1607   TPointD p1M         = points.getPoint(5);
1608   TPointD pM1         = points.getPoint(6);
1609   TPointD pM0         = points.getPoint(4);
1610   int pointIndex;
1611   TPointD sign(1, 1);
1612   TPointD scaleFactor = m_tool->m_deformValues.m_scaleValue;
1613   TPointD newPos;
1614   if (m_id == 0) {
1615     if (p1M == p0M) return false;
1616     pointIndex      = 7;
1617     TPointD v       = normalize(p1M - p0M);
1618     double currentD = tdistance(p1M, p0M);
1619     double startD   = currentD / scaleFactor.x;
1620     double d      = (currentD - startD * p) * tdistance(center, p0M) / currentD;
1621     newPos        = TPointD(p0M.x + d * v.x, p0M.y + d * v.y);
1622     scaleFactor.x = p;
1623   } else if (m_id == 1) {
1624     if (pM1 == pM0) return false;
1625     pointIndex      = 4;
1626     TPointD v       = normalize(pM1 - pM0);
1627     double currentD = tdistance(pM1, pM0);
1628     double startD   = currentD / scaleFactor.y;
1629     double d      = (currentD - startD * p) * tdistance(center, pM0) / currentD;
1630     newPos        = TPointD(pM0.x + d * v.x, pM0.y + d * v.y);
1631     scaleFactor.y = p;
1632   }
1633 
1634   m_tool->m_deformValues.m_scaleValue =
1635       scaleFactor;  // Instruction order is relevant
1636   scaleTool->transform(pointIndex,
1637                        newPos);  // This line invokes GUI update using the
1638                                  // value set above
1639   if (!m_tool->isLevelType() && addToUndo) scaleTool->addTransformUndo();
1640   setCursorPosition(0);
1641   return true;
1642 }
1643 
1644 //-----------------------------------------------------------------------------
1645 
onChange(TMeasuredValue * fld,bool addToUndo)1646 void SelectionScaleField::onChange(TMeasuredValue *fld, bool addToUndo) {
1647   if (!m_tool->isEnabled()) return;
1648   if (!applyChange(addToUndo)) return;
1649   emit valueChange(addToUndo);
1650 }
1651 
1652 //-----------------------------------------------------------------------------
1653 
updateStatus()1654 void SelectionScaleField::updateStatus() {
1655   if (!m_tool || !m_tool->isSelectionEditable() ||
1656       (m_tool->isSelectionEmpty() && !m_tool->isLevelType())) {
1657     setValue(0);
1658     setDisabled(true);
1659     return;
1660   }
1661   setDisabled(false);
1662   if (m_id == 0)
1663     setValue(m_tool->m_deformValues.m_scaleValue.x);
1664   else
1665     setValue(m_tool->m_deformValues.m_scaleValue.y);
1666   setCursorPosition(0);
1667 }
1668 
1669 //=============================================================================
1670 
SelectionRotationField(SelectionTool * tool,QString name)1671 SelectionRotationField::SelectionRotationField(SelectionTool *tool,
1672                                                QString name)
1673     : MeasuredValueField(0, name), m_tool(tool) {
1674   bool ret = connect(this, SIGNAL(measuredValueChanged(TMeasuredValue *, bool)),
1675                      SLOT(onChange(TMeasuredValue *, bool)));
1676   assert(ret);
1677   setMeasure("angle");
1678   updateStatus();
1679 
1680   setMaximumWidth(getMaximumWidthForSelectionToolField(this));
1681 }
1682 
1683 //-----------------------------------------------------------------------------
1684 
onChange(TMeasuredValue * fld,bool addToUndo)1685 void SelectionRotationField::onChange(TMeasuredValue *fld, bool addToUndo) {
1686   if (!m_tool || !m_tool->isEnabled() ||
1687       (m_tool->isSelectionEmpty() && !m_tool->isLevelType()))
1688     return;
1689 
1690   DragSelectionTool::DragTool *rotationTool = createNewRotationTool(m_tool);
1691 
1692   DragSelectionTool::DeformValues &deformValues = m_tool->m_deformValues;
1693   double p                                      = getValue();
1694 
1695   TAffine aff =
1696       TRotation(m_tool->getCenter(), p - deformValues.m_rotationAngle);
1697 
1698   deformValues.m_rotationAngle = p;  // Instruction order is relevant here
1699   rotationTool->transform(aff, p - deformValues.m_rotationAngle);  //
1700 
1701   if (!m_tool->isLevelType() && addToUndo) rotationTool->addTransformUndo();
1702 
1703   setCursorPosition(0);
1704 }
1705 
1706 //-----------------------------------------------------------------------------
1707 
updateStatus()1708 void SelectionRotationField::updateStatus() {
1709   if (!m_tool || !m_tool->isSelectionEditable() ||
1710       (m_tool->isSelectionEmpty() && !m_tool->isLevelType())) {
1711     setValue(0);
1712     setDisabled(true);
1713     return;
1714   }
1715   setDisabled(false);
1716   setValue(m_tool->m_deformValues.m_rotationAngle);
1717   setCursorPosition(0);
1718 }
1719 
1720 //=============================================================================
1721 // id == 0 Move X
1722 // id == 0 Move Y
SelectionMoveField(SelectionTool * tool,int id,QString name)1723 SelectionMoveField::SelectionMoveField(SelectionTool *tool, int id,
1724                                        QString name)
1725     : MeasuredValueField(0, name), m_tool(tool), m_id(id) {
1726   bool ret = connect(this, SIGNAL(measuredValueChanged(TMeasuredValue *, bool)),
1727                      SLOT(onChange(TMeasuredValue *, bool)));
1728   assert(ret);
1729   if (m_id == 0)
1730     setMeasure("length.x");
1731   else
1732     setMeasure("length.y");
1733   updateStatus();
1734 
1735   // for translation value field, use size for the Edit Tool as it needs more
1736   // estate
1737   setMaximumWidth(getMaximumWidthForEditToolField(this));
1738 }
1739 
1740 //-----------------------------------------------------------------------------
1741 
onChange(TMeasuredValue * fld,bool addToUndo)1742 void SelectionMoveField::onChange(TMeasuredValue *fld, bool addToUndo) {
1743   if (!m_tool || !m_tool->isEnabled() ||
1744       (m_tool->isSelectionEmpty() && !m_tool->isLevelType()))
1745     return;
1746 
1747   DragSelectionTool::DragTool *moveTool = createNewMoveSelectionTool(m_tool);
1748 
1749   double p        = getValue() * Stage::inch;
1750   TPointD delta   = (m_id == 0) ? TPointD(p, 1) : TPointD(1, p),
1751           oldMove = Stage::inch * m_tool->m_deformValues.m_moveValue,
1752           oldDelta =
1753               (m_id == 0) ? TPointD(oldMove.x, 1) : TPointD(1, oldMove.y),
1754           newMove = (m_id == 0) ? TPointD(delta.x, oldMove.y)
1755                                 : TPointD(oldMove.x, delta.y);
1756 
1757   TAffine aff = TTranslation(-oldDelta) * TTranslation(delta);
1758 
1759   m_tool->m_deformValues.m_moveValue =
1760       1 / Stage::inch * newMove;  // Instruction order relevant here
1761   moveTool->transform(aff);       //
1762 
1763   if (!m_tool->isLevelType() && addToUndo) moveTool->addTransformUndo();
1764 
1765   setCursorPosition(0);
1766 }
1767 
1768 //-----------------------------------------------------------------------------
1769 
updateStatus()1770 void SelectionMoveField::updateStatus() {
1771   if (!m_tool || !m_tool->isSelectionEditable() ||
1772       (m_tool->isSelectionEmpty() && !m_tool->isLevelType())) {
1773     setValue(0);
1774     setDisabled(true);
1775     return;
1776   }
1777   setDisabled(false);
1778 
1779   if (m_id == 0)
1780     setValue(m_tool->m_deformValues.m_moveValue.x);
1781   else
1782     setValue(m_tool->m_deformValues.m_moveValue.y);
1783 
1784   setCursorPosition(0);
1785 }
1786 
1787 //=============================================================================
1788 
ThickChangeField(SelectionTool * tool,QString name)1789 ThickChangeField::ThickChangeField(SelectionTool *tool, QString name)
1790     : MeasuredValueField(0, name), m_tool(tool) {
1791   bool ret = connect(this, SIGNAL(measuredValueChanged(TMeasuredValue *, bool)),
1792                      SLOT(onChange(TMeasuredValue *, bool)));
1793   assert(ret);
1794   setMeasure("");
1795   updateStatus();
1796 
1797   setMaximumWidth(getMaximumWidthForSelectionToolField(this));
1798 }
1799 
1800 //-----------------------------------------------------------------------------
1801 
onChange(TMeasuredValue * fld,bool addToUndo)1802 void ThickChangeField::onChange(TMeasuredValue *fld, bool addToUndo) {
1803   if (!m_tool || (m_tool->isSelectionEmpty() && !m_tool->isLevelType())) return;
1804 
1805   DragSelectionTool::VectorChangeThicknessTool *changeThickTool =
1806       new DragSelectionTool::VectorChangeThicknessTool(
1807           (VectorSelectionTool *)m_tool);
1808 
1809   TVectorImageP vi = (TVectorImageP)m_tool->getImage(true);
1810 
1811   double p            = 0.5 * getValue();
1812   double newThickness = p - m_tool->m_deformValues.m_maxSelectionThickness;
1813 
1814   changeThickTool->setThicknessChange(newThickness);
1815   changeThickTool->changeImageThickness(*vi, newThickness);
1816 
1817   // DragSelectionTool::DeformValues deformValues = m_tool->m_deformValues;
1818   // // Like when I found it - it's a noop.
1819   // deformValues.m_maxSelectionThickness = p;
1820   // // Seems that the actual update is performed inside
1821   // the above change..() instruction...   >_<
1822   if (addToUndo) {
1823     changeThickTool->addUndo();
1824   }
1825   m_tool->computeBBox();
1826   m_tool->invalidate();
1827   m_tool->notifyImageChanged(m_tool->getCurrentFid());
1828 }
1829 
1830 //-----------------------------------------------------------------------------
1831 
updateStatus()1832 void ThickChangeField::updateStatus() {
1833   if (!m_tool || !m_tool->isSelectionEditable() ||
1834       m_tool->m_deformValues.m_isSelectionModified ||
1835       (m_tool->isSelectionEmpty() && !m_tool->isLevelType())) {
1836     setValue(0);
1837     setDisabled(true);
1838     return;
1839   }
1840 
1841   setDisabled(false);
1842   setValue(2 * m_tool->m_deformValues.m_maxSelectionThickness);
1843   setCursorPosition(0);
1844 }
1845 
1846 //=============================================================================
1847 
ClickableLabel(const QString & text,QWidget * parent,Qt::WindowFlags f)1848 ClickableLabel::ClickableLabel(const QString &text, QWidget *parent,
1849                                Qt::WindowFlags f)
1850     : QLabel(text, parent, f) {}
1851 
1852 //-----------------------------------------------------------------------------
1853 
~ClickableLabel()1854 ClickableLabel::~ClickableLabel() {}
1855 
1856 //-----------------------------------------------------------------------------
1857 
mousePressEvent(QMouseEvent * event)1858 void ClickableLabel::mousePressEvent(QMouseEvent *event) {
1859   emit onMousePress(event);
1860 }
1861 
1862 //-----------------------------------------------------------------------------
1863 
mouseMoveEvent(QMouseEvent * event)1864 void ClickableLabel::mouseMoveEvent(QMouseEvent *event) {
1865   emit onMouseMove(event);
1866 }
1867 
1868 //-----------------------------------------------------------------------------
1869 
mouseReleaseEvent(QMouseEvent * event)1870 void ClickableLabel::mouseReleaseEvent(QMouseEvent *event) {
1871   emit onMouseRelease(event);
1872 }
1873