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 ¶m, 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