1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2011-2013 Werner Schweer and others
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2
9 // as published by the Free Software Foundation and appearing in
10 // the file LICENSE.GPL
11 //=============================================================================
12
13 #include "libmscore/score.h"
14 #include "libmscore/element.h"
15 #include "libmscore/beam.h"
16 #include "libmscore/undo.h"
17 #include "musescore.h"
18 #include "inspectorBase.h"
19 #include "inspector.h"
20 #include "icons.h"
21 #include "preferences.h"
22 #include "offsetSelect.h"
23 #include "scaleSelect.h"
24 #include "sizeSelect.h"
25 #include "fontStyleSelect.h"
26 #include "scoreview.h"
27 #include "script/script.h"
28 #include "resetButton.h"
29 #include "telemetrymanager.h"
30 #include "tourhandler.h"
31
32 namespace Ms {
33
34 std::unique_ptr<InspectorEventObserver> InspectorEventObserver::i;
35
36 //---------------------------------------------------------
37 // InspectorBase
38 //---------------------------------------------------------
39
InspectorBase(QWidget * parent)40 InspectorBase::InspectorBase(QWidget* parent)
41 : QWidget(parent)
42 {
43 setObjectName("InspectorBase");
44 setAccessibleName(tr("Inspector"));
45 inspector = static_cast<Inspector*>(parent);
46 setParent(parent);
47 _layout = new QVBoxLayout(this);
48 _layout->setSpacing(0);
49 _layout->setContentsMargins(0, 10, 0, 0);
50 _layout->addStretch(100);
51 scrollPreventer = new InspectorScrollPreventer(this);
52 }
53
54 //---------------------------------------------------------
55 // getValue
56 // get value from gui element
57 //---------------------------------------------------------
58
getValue(const InspectorItem & ii) const59 QVariant InspectorBase::getValue(const InspectorItem& ii) const
60 {
61 QWidget* w = ii.w;
62
63 QVariant v;
64 if (qobject_cast<QDoubleSpinBox*>(w) || qobject_cast<QSpinBox*>(w))
65 v = w->property("value");
66 else if (qobject_cast<QFontComboBox*>(w))
67 v = static_cast<QFontComboBox*>(w)->currentFont().family();
68 else if (qobject_cast<QComboBox*>(w)) {
69 QComboBox* cb = qobject_cast<QComboBox*>(w);
70 int val = cb->currentIndex();
71 if (cb->itemData(val).isValid())
72 val = cb->itemData(val).toInt();
73 v = val;
74 }
75 else if (qobject_cast<Awl::ColorLabel*>(w))
76 v = static_cast<Awl::ColorLabel*>(w)->color();
77 else if (qobject_cast<QCheckBox*>(w) || qobject_cast<QPushButton*>(w) ||
78 qobject_cast<QToolButton*>(w) || qobject_cast<QRadioButton*>(w))
79 v = w->property("checked");
80 else if (qobject_cast<QLineEdit*>(w))
81 v = w->property("text");
82 else if (qobject_cast<Ms::AlignSelect*>(w))
83 v = int(static_cast<Ms::AlignSelect*>(w)->align());
84 else if (qobject_cast<Ms::FontStyleSelect*>(w))
85 v = int(static_cast<Ms::FontStyleSelect*>(w)->fontStyle());
86 else if (qobject_cast<Ms::OffsetSelect*>(w))
87 v = static_cast<Ms::OffsetSelect*>(w)->offset();
88 else if (qobject_cast<Ms::ScaleSelect*>(w))
89 v = static_cast<Ms::ScaleSelect*>(w)->scale();
90 else if (qobject_cast<Ms::SizeSelect*>(w))
91 v = static_cast<Ms::SizeSelect*>(w)->value();
92 else
93 qFatal("not supported widget %s", w->metaObject()->className());
94
95 switch (propertyType(ii.t)) {
96 case P_TYPE::POINT_SP:
97 v = v.toPointF() * inspector->element()->score()->spatium();
98 break;
99 case P_TYPE::POINT_SP_MM: {
100 Element* e = inspector->element();
101 if (e->offsetIsSpatiumDependent())
102 v = v.toPointF() * e->score()->spatium();
103 else
104 v = v.toPointF() * DPMM;
105 }
106 break;
107 case P_TYPE::SP_REAL:
108 v = v.toDouble() * inspector->element()->score()->spatium();
109 break;
110 case P_TYPE::TEMPO:
111 v = v.toDouble() / 60.0;
112 break;
113 case P_TYPE::ZERO_INT:
114 v = v.toInt() - 1;
115 break;
116 case P_TYPE::POINT_MM:
117 v = v.toPointF() * DPMM;
118 break;
119 case P_TYPE::SIZE_MM:
120 v = v.toSizeF() * DPMM;
121 break;
122 case P_TYPE::BARLINE_TYPE:
123 v = QVariant::fromValue(BarLineType(v.toInt()));
124 break;
125 case P_TYPE::DIRECTION:
126 v = QVariant::fromValue<Direction>(Direction(v.toInt()));
127 break;
128 case P_TYPE::INT_LIST: {
129 QStringList sl = v.toString().split(",", QString::SkipEmptyParts);
130 QList<int> il;
131 for (const QString& l : sl) {
132 int i = l.simplified().toInt();
133 il.append(i);
134 }
135 v = QVariant::fromValue(il);
136 }
137 break;
138 default:
139 break;
140 }
141 return v;
142 }
143
144 //---------------------------------------------------------
145 // setValue
146 // set gui element value
147 //---------------------------------------------------------
148
setValue(const InspectorItem & ii,QVariant val)149 void InspectorBase::setValue(const InspectorItem& ii, QVariant val)
150 {
151 QWidget* w = ii.w;
152
153 Pid id = ii.t;
154
155 switch (propertyType(id)) {
156 case P_TYPE::POINT_SP:
157 val = val.toPointF() / inspector->element()->score()->spatium();
158 break;
159 case P_TYPE::POINT_SP_MM: {
160 Element* e = inspector->element();
161 if (e->offsetIsSpatiumDependent())
162 val = val.toPointF() / e->score()->spatium();
163 else
164 val = val.toPointF() / DPMM;
165 }
166 break;
167 case P_TYPE::SP_REAL:
168 val = val.toDouble() / inspector->element()->score()->spatium();
169 break;
170 case P_TYPE::TEMPO:
171 val = val.toDouble() * 60.0;
172 break;
173 case P_TYPE::ZERO_INT:
174 val = val.toInt() + 1;
175 break;
176 case P_TYPE::POINT_MM:
177 val = val.toPointF() / DPMM;
178 break;
179 case P_TYPE::SIZE_MM:
180 val = val.toSizeF() / DPMM;
181 break;
182 case P_TYPE::DIRECTION:
183 val = int(val.value<Direction>());
184 break;
185 case P_TYPE::INT_LIST: {
186 QString s;
187 QList<int> il = val.value<QList<int>>();
188 for (int i : il) {
189 if (!s.isEmpty())
190 s += ", ";
191 s += QString("%1").arg(i);
192 }
193 val = s;
194 }
195 break;
196
197 default:
198 break;
199 }
200 if (qobject_cast<QDoubleSpinBox*>(w))
201 static_cast<QDoubleSpinBox*>(w)->setValue(val.toDouble());
202 else if (qobject_cast<QSpinBox*>(w))
203 static_cast<QSpinBox*>(w)->setValue(val.toInt());
204 else if (qobject_cast<QFontComboBox*>(w))
205 static_cast<QFontComboBox*>(w)->setCurrentFont(QFont(val.toString()));
206 else if (qobject_cast<QComboBox*>(w)) {
207 int ival = val.toInt();
208 bool found = false;
209 QComboBox* cb = qobject_cast<QComboBox*>(w);
210 if (cb->itemData(0).isValid()) {
211 for (int i = 0; i < cb->count(); ++i) {
212 if (cb->itemData(i).toInt() == ival) {
213 cb->setCurrentIndex(i);
214 found = true;
215 break;
216 }
217 }
218 if (!found)
219 qDebug("ComboBox item not found: pid <%s> data <%d>", propertyName(id), ival);
220 }
221 else
222 cb->setCurrentIndex(ival);
223 }
224 else if (qobject_cast<QCheckBox*>(w))
225 static_cast<QCheckBox*>(w)->setChecked(val.toBool());
226 else if (qobject_cast<Awl::ColorLabel*>(w))
227 static_cast<Awl::ColorLabel*>(w)->setColor(val.value<QColor>());
228 else if (qobject_cast<QRadioButton*>(w))
229 static_cast<QRadioButton*>(w)->setChecked(val.toBool());
230 else if (qobject_cast<QPushButton*>(w))
231 static_cast<QPushButton*>(w)->setChecked(val.toBool());
232 else if (qobject_cast<QToolButton*>(w))
233 static_cast<QToolButton*>(w)->setChecked(val.toBool());
234 else if (qobject_cast<QLineEdit*>(w))
235 static_cast<QLineEdit*>(w)->setText(val.toString());
236 else if (qobject_cast<Ms::AlignSelect*>(w))
237 static_cast<Ms::AlignSelect*>(w)->setAlign(Align(val.toInt()));
238 else if (qobject_cast<Ms::FontStyleSelect*>(w))
239 static_cast<Ms::FontStyleSelect*>(w)->setFontStyle(FontStyle(val.toInt()));
240 else if (qobject_cast<Ms::OffsetSelect*>(w))
241 static_cast<Ms::OffsetSelect*>(w)->setOffset(val.toPointF());
242 else if (qobject_cast<Ms::ScaleSelect*>(w))
243 static_cast<Ms::ScaleSelect*>(w)->setScale(val.toSizeF());
244 else if (qobject_cast<Ms::SizeSelect*>(w))
245 static_cast<Ms::SizeSelect*>(w)->setValue(val);
246 else
247 qFatal("not supported widget %s", w->metaObject()->className());
248 }
249
250 //---------------------------------------------------------
251 // effectiveElement
252 //---------------------------------------------------------
253
effectiveElement(const InspectorItem & ii) const254 Element* InspectorBase::effectiveElement(const InspectorItem& ii) const
255 {
256 Element* e = inspector->element();
257 for (int i = 0; i < ii.parent; ++i)
258 e = e->parent();
259 if (Element* ee = e->propertyDelegate(ii.t))
260 e = ee;
261 return e;
262 }
263
264 //---------------------------------------------------------
265 // isDefault
266 //---------------------------------------------------------
267
isDefault(const InspectorItem & ii)268 bool InspectorBase::isDefault(const InspectorItem& ii)
269 {
270 Element* e = effectiveElement(ii);
271 Pid id = ii.t;
272 QVariant val = e->getProperty(id);
273 QVariant def = e->propertyDefault(id);
274 return val == def;
275 }
276
277 //---------------------------------------------------------
278 // compareValues
279 //---------------------------------------------------------
280
compareValues(const InspectorItem & ii,QVariant a,QVariant b)281 bool InspectorBase::compareValues(const InspectorItem& ii, QVariant a, QVariant b)
282 {
283 Pid id = ii.t;
284 P_TYPE t = propertyType(id);
285 if (t == P_TYPE::SIZE) {
286 QSizeF s1 = a.toSizeF();
287 QSizeF s2 = b.toSizeF();
288 bool c = qFuzzyCompare(s1.width(), s2.width()) && qFuzzyCompare(s1.height(), s2.height());
289 return c;
290 }
291 return b == a;
292 }
293
294 //---------------------------------------------------------
295 // dirty
296 // return true if a property has changed
297 //---------------------------------------------------------
298
dirty() const299 bool InspectorBase::dirty() const
300 {
301 for (const InspectorItem& ii : iList) {
302 Element* e = effectiveElement(ii);
303 if (e->getProperty(ii.t) != getValue(ii))
304 return true;
305 }
306 return false;
307 }
308
309 //---------------------------------------------------------
310 // setElement
311 //---------------------------------------------------------
312
setElement()313 void InspectorBase::setElement()
314 {
315 for (const InspectorItem& ii : iList) {
316 Pid id = ii.t;
317 Element* e = effectiveElement(ii);
318 QVariant val = e->getProperty(id);
319 if (ii.w) {
320 ii.w->blockSignals(true);
321 setValue(ii, val);
322 ii.w->blockSignals(false);
323 }
324 checkDifferentValues(ii);
325 }
326 postInit();
327 }
328
329 //---------------------------------------------------------
330 // checkDifferentValues
331 // enable/disable reset button
332 // enable/disable value widget for multi selection
333 //---------------------------------------------------------
334
checkDifferentValues(const InspectorItem & ii)335 void InspectorBase::checkDifferentValues(const InspectorItem& ii)
336 {
337 bool valuesAreDifferent = false;
338 QColor c(Ms::preferences.getColor(preferences.isThemeDark() ? PREF_UI_INSPECTOR_STYLED_TEXT_COLOR_DARK : PREF_UI_INSPECTOR_STYLED_TEXT_COLOR_LIGHT));
339
340 if (inspector->el()->size() > 1) {
341 Pid id = ii.t;
342 QVariant val = getValue(ii);
343
344 for (Element* e : *inspector->el()) {
345 for (int k = 0; k < ii.parent; ++k)
346 e = e->parent();
347 if (Element* ee = e->propertyDelegate(id))
348 e = ee;
349
350 valuesAreDifferent = !compareValues(ii, e->getProperty(id), val);
351 if (valuesAreDifferent)
352 break;
353 }
354 ii.w->setStyleSheet(valuesAreDifferent ? QString("* { color: %1; } QToolTip { color: palette(tooltiptext); }").arg(c.name()) : " ");
355 }
356
357 //deal with reset if only one element, or if values are the same
358 bool enableReset = true;
359 if (!valuesAreDifferent) {
360 Element* e = effectiveElement(ii);
361 PropertyFlags styledValue = e->propertyFlags(ii.t);
362
363 switch (styledValue) {
364 case PropertyFlags::STYLED:
365 ii.w->setStyleSheet(QString("* { color: %1; } QToolTip { color: palette(tooltiptext); }").arg(c.name()));
366 enableReset = false;
367 break;
368 case PropertyFlags::UNSTYLED:
369 ii.w->setStyleSheet(" ");
370 enableReset = true;
371 break;
372 case PropertyFlags::NOSTYLE:
373 enableReset = !isDefault(ii);
374 ii.w->setStyleSheet(" ");
375 break;
376 }
377 }
378 if (ii.r)
379 ii.r->setEnabled(enableReset);
380 }
381
382 //---------------------------------------------------------
383 // valueChanged
384 //---------------------------------------------------------
385
valueChanged(int idx,bool reset)386 void InspectorBase::valueChanged(int idx, bool reset)
387 {
388 static bool recursion = false;
389
390 if (recursion)
391 return;
392 recursion = true;
393
394 const InspectorItem& ii = iList[idx];
395 Pid id = ii.t;
396 QVariant val2 = getValue(ii); // get new value from UI
397 Element* iElement = inspector->element();
398 Score* score = iElement->score();
399
400 #ifdef MSCORE_UNSTABLE
401 if (ScriptRecorder* rec = mscore->getScriptRecorder())
402 rec->recordInspectorValueChange(iElement, ii, val2);
403 #endif
404 InspectorEventObserver::instance()->event(reset ? InspectorEventObserver::PropertyReset : InspectorEventObserver::PropertyChange, ii, iElement);
405
406 if (ii.t == Pid::AUTOPLACE)
407 TourHandler::startTour("autoplace-tour");
408 else
409 TourHandler::startTour("inspector-tour");
410
411 score->startCmd();
412 for (Element* e : *inspector->el()) {
413 for (int i = 0; i < ii.parent; ++i)
414 e = e->parent();
415 if (Element* ee = e->propertyDelegate(id))
416 e = ee;
417
418 // reset sets property style UNSTYLED to STYLED
419
420 PropertyFlags ps = e->propertyFlags(id);
421 if (reset && ps == PropertyFlags::UNSTYLED)
422 ps = PropertyFlags::STYLED;
423 else if (ps == PropertyFlags::STYLED)
424 ps = PropertyFlags::UNSTYLED;
425 QVariant val1 = e->getProperty(id);
426 if (reset) {
427 val2 = e->propertyDefault(id);
428 if (!val2.isValid())
429 continue;
430 setValue(ii, val2); // set UI, this may call valueChanged()
431 }
432 e->undoChangeProperty(id, val2, ps);
433 if (e->isClef() && (id == Pid::SHOW_COURTESY)) {
434 // copy into 'other clef' the ShowCourtesy set for this clef
435 Clef* otherClef = toClef(e)->otherClef();
436 if (otherClef)
437 otherClef->undoChangeProperty(id, val2, ps);
438 }
439 }
440 inspector->setInspectorEdit(true);
441 checkDifferentValues(ii);
442 score->endCmd();
443 inspector->setInspectorEdit(false);
444
445 if (iElement != inspector->element()) {
446 // Something changed in selection as a result of value change.
447 recursion = false;
448 emit elementChanged();
449 return;
450 }
451
452 postInit();
453
454 // a subStyle change may change several other values:
455 if (id == Pid::SUB_STYLE)
456 setElement();
457 recursion = false;
458
459 ScoreView* cv = mscore->currentScoreView();
460 cv->updateGrips();
461 }
462
463 //---------------------------------------------------------
464 // resetClicked
465 //---------------------------------------------------------
466
resetClicked(int i)467 void InspectorBase::resetClicked(int i)
468 {
469 valueChanged(i, true);
470 }
471
472 //---------------------------------------------------------
473 // setStyleClicked
474 //---------------------------------------------------------
475
setStyleClicked(int i)476 void InspectorBase::setStyleClicked(int i)
477 {
478 const InspectorItem& ii = iList[i];
479 const Pid id = ii.t;
480 Element* e = inspector->element();
481 if (Element* delegate = e->propertyDelegate(id))
482 e = delegate;
483 Score* score = e->score();
484
485 InspectorEventObserver::instance()->event(InspectorEventObserver::PropertySetStyle, ii, e);
486
487 Sid sidx = e->getPropertyStyle(ii.t);
488 if (sidx == Sid::NOSTYLE)
489 return;
490 QVariant val = getValue(ii);
491 P_TYPE t = propertyType(id);
492 if (t == P_TYPE::SP_REAL)
493 val = val.toDouble() / score->spatium();
494 else if (t == P_TYPE::POINT_SP)
495 val = val.toPointF() / score->spatium();
496 else if (t == P_TYPE::POINT_SP_MM) {
497 if (e->offsetIsSpatiumDependent())
498 val = val.toPointF() / score->spatium();
499 else
500 val = val.toPointF() / DPMM;
501 }
502
503 score->startCmd();
504 for (Element* ee : *inspector->el()) {
505 if (Element* delegate = ee->propertyDelegate(ii.t))
506 ee = delegate;
507 ee->undoChangeProperty(ii.t, val, PropertyFlags::STYLED);
508 }
509 score->undo(new ChangeStyleVal(score, sidx, val));
510 checkDifferentValues(ii);
511 score->endCmd();
512 }
513
514 //---------------------------------------------------------
515 // mapSignals
516 // initialize inspector panel
517 //---------------------------------------------------------
518
mapSignals(const std::vector<InspectorItem> & il,const std::vector<InspectorPanel> & pl)519 void InspectorBase::mapSignals(const std::vector<InspectorItem>& il, const std::vector<InspectorPanel>& pl)
520 {
521 for (auto& p : pl)
522 pList.push_back(p);
523 for (auto& p : pList) {
524 QToolButton* title = p.title;
525 QWidget* panel = p.panel;
526 if (title) {
527 title->setCheckable(true);
528 title->setFocusPolicy(Qt::NoFocus);
529 connect(title, &QToolButton::clicked, this, [title, panel] (bool visible) {
530 if (panel)
531 panel->setVisible(visible);
532 if (title) {
533 title->setChecked(visible);
534 title->setArrowType(visible ? Qt::DownArrow : Qt::RightArrow);
535 QString key = title->parent()->objectName();
536 QSettings s;
537 s.setValue(QString("inspector/%1_visible").arg(key), visible);
538 }});
539 title->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
540 title->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
541 title->setStyleSheet("font: bold;");
542 QSettings s;
543 QString key = title->parent()->objectName();
544 bool visible = s.value(QString("inspector/%1_visible").arg(key), true).toBool();
545 title->setArrowType(visible ? Qt::DownArrow : Qt::RightArrow);
546 title->setChecked(visible);
547 if (panel)
548 panel->setVisible(visible);
549 }
550 }
551 for (auto& i : il)
552 iList.push_back(i);
553 int i = 0;
554 for (const InspectorItem& ii : iList) {
555 QWidget* rw = ii.r;
556 if (rw) {
557 if (qobject_cast<QToolButton*>(rw)) {
558 QToolButton* resetButton = qobject_cast<QToolButton*>(rw);
559 resetButton->setIcon(*icons[int(Icons::reset_ICON)]);
560 connect(resetButton, &QToolButton::clicked, [=] { resetClicked(i); });
561 Sid sidx = inspector->element()->getPropertyStyle(ii.t);
562 // S button for fingering placement is bugged and proposed to be hidden
563 // it can be brought back once the relevant design is fixed,
564 // for example changing values of fingering placement to "Auto, Above, Below", "Auto" as default
565 // See https://musescore.org/en/node/288372 and https://musescore.org/en/node/297092
566 if (sidx != Sid::NOSTYLE && sidx != Sid::fingeringPlacement) {
567 QMenu* menu = new QMenu(this);
568 resetButton->setMenu(menu);
569 resetButton->setPopupMode(QToolButton::MenuButtonPopup);
570 QAction* a = menu->addAction(tr("Set as style"));
571 connect(a, &QAction::triggered, [=] { setStyleClicked(i); });
572 }
573 }
574 else {
575 ResetButton* b = qobject_cast<ResetButton*>(rw);
576 connect(b, &ResetButton::resetClicked, [=] { resetClicked(i); });
577 Sid sidx = inspector->element()->getPropertyStyle(ii.t);
578 // Same, see comment above
579 if (sidx != Sid::NOSTYLE && sidx != Sid::fingeringPlacement) {
580 b->enableSetStyle(true);
581 connect(b, &ResetButton::setStyleClicked, [=] { setStyleClicked(i); });
582 }
583 }
584 }
585 QWidget* w = ii.w;
586 if (!w)
587 continue;
588
589 if (qobject_cast<QAbstractSpinBox*>(w)
590 || qobject_cast<QComboBox*>(w)) {
591 w->setFocusPolicy(Qt::StrongFocus);
592 w->installEventFilter(scrollPreventer);
593 }
594 else if (qobject_cast<OffsetSelect*>(w)) {
595 qobject_cast<OffsetSelect*>(w)->installScrollPreventer(scrollPreventer);
596 }
597
598 if (qobject_cast<QDoubleSpinBox*>(w))
599 connect(qobject_cast<QDoubleSpinBox*>(w), QOverload<double>::of(&QDoubleSpinBox::valueChanged), [=] { valueChanged(i); });
600 else if (qobject_cast<QSpinBox*>(w))
601 connect(qobject_cast<QSpinBox*>(w), QOverload<int>::of(&QSpinBox::valueChanged), [=] { valueChanged(i); });
602 else if (qobject_cast<QFontComboBox*>(w))
603 connect(qobject_cast<QFontComboBox*>(w), QOverload<const QFont&>::of(&QFontComboBox::currentFontChanged), [=] { valueChanged(i); });
604 else if (qobject_cast<QComboBox*>(w))
605 connect(qobject_cast<QComboBox*>(w), QOverload<int>::of(&QComboBox::currentIndexChanged), [=] { valueChanged(i); });
606 else if (qobject_cast<QCheckBox*>(w))
607 connect(qobject_cast<QCheckBox*>(w), QOverload<bool>::of(&QCheckBox::toggled), [=] { valueChanged(i); });
608 else if (qobject_cast<Awl::ColorLabel*>(w))
609 connect(qobject_cast<Awl::ColorLabel*>(w), QOverload<QColor>::of(&Awl::ColorLabel::colorChanged), [=] { valueChanged(i); });
610 else if (qobject_cast<QRadioButton*>(w))
611 connect(qobject_cast<QRadioButton*>(w), QOverload<bool>::of(&QRadioButton::toggled), [=] { valueChanged(i); });
612 else if (qobject_cast<QPushButton*>(w))
613 connect(qobject_cast<QPushButton*>(w), QOverload<bool>::of(&QPushButton::toggled), [=] { valueChanged(i); });
614 else if (qobject_cast<QToolButton*>(w))
615 connect(qobject_cast<QToolButton*>(w), QOverload<bool>::of(&QToolButton::toggled), [=] { valueChanged(i); });
616 else if (qobject_cast<QLineEdit*>(w))
617 connect(qobject_cast<QLineEdit*>(w), QOverload<const QString&>::of(&QLineEdit::textChanged), [=] { valueChanged(i); });
618 else if (qobject_cast<Ms::AlignSelect*>(w))
619 connect(qobject_cast<Ms::AlignSelect*>(w), QOverload<Align>::of(&Ms::AlignSelect::alignChanged), [=] { valueChanged(i); });
620 else if (qobject_cast<Ms::FontStyleSelect*>(w))
621 connect(qobject_cast<Ms::FontStyleSelect*>(w), QOverload<FontStyle>::of(&Ms::FontStyleSelect::fontStyleChanged), [=] { valueChanged(i); });
622 else if (qobject_cast<Ms::OffsetSelect*>(w))
623 connect(qobject_cast<Ms::OffsetSelect*>(w), QOverload<const QPointF&>::of(&Ms::OffsetSelect::offsetChanged), [=] { valueChanged(i); });
624 else if (qobject_cast<Ms::ScaleSelect*>(w))
625 connect(qobject_cast<Ms::ScaleSelect*>(w), QOverload<const QSizeF&>::of(&Ms::ScaleSelect::scaleChanged), [=] { valueChanged(i); });
626 else if (qobject_cast<Ms::SizeSelect*>(w))
627 connect(qobject_cast<Ms::SizeSelect*>(w), QOverload<const QVariant&>::of(&Ms::SizeSelect::valueChanged), [=] { valueChanged(i); });
628 else
629 qFatal("not supported widget %s", w->metaObject()->className());
630 ++i;
631 }
632 }
633
634 //---------------------------------------------------------
635 // valueChanged
636 //---------------------------------------------------------
637
valueChanged(int idx)638 void InspectorBase::valueChanged(int idx)
639 {
640 valueChanged(idx, false);
641 }
642
643 //---------------------------------------------------------
644 // addWidget
645 //---------------------------------------------------------
646
addWidget()647 QWidget* InspectorBase::addWidget()
648 {
649 QWidget* w = new QWidget;
650 _layout->insertWidget(_layout->count()-1, w);
651 _layout->insertSpacing(_layout->count()-1, 5);
652 return w;
653 }
654
655 //---------------------------------------------------------
656 // setupLineStyle
657 //---------------------------------------------------------
658
setupLineStyle(QComboBox * cb)659 void InspectorBase::setupLineStyle(QComboBox* cb)
660 {
661 cb->setItemData(0, int(Qt::SolidLine));
662 cb->setItemData(1, int(Qt::DashLine));
663 cb->setItemData(2, int(Qt::DotLine));
664 cb->setItemData(3, int(Qt::DashDotLine));
665 cb->setItemData(4, int(Qt::DashDotDotLine));
666 cb->setItemData(5, int(Qt::CustomDashLine));
667 }
668
669 //---------------------------------------------------------
670 // resetToStyle
671 //---------------------------------------------------------
672
resetToStyle()673 void InspectorBase::resetToStyle()
674 {
675 Score* score = inspector->element()->score();
676 score->startCmd();
677 for (Element* e : *inspector->el()) { // TODO: ??
678 TextBase* text = toTextBase(e);
679 // Preserve <sym> tags
680 text->undoChangeProperty(Pid::TEXT, text->plainText().toHtmlEscaped().replace("<sym>","<sym>").replace("</sym>","</sym>"));
681 }
682 score->endCmd();
683 }
684
685 //---------------------------------------------------------
686 // eventFilter
687 /// This blocks scrolling on a scrollable thing when not in focus.
688 /// `watched` should be a QComboBox or QAbstractSpinBox.
689 /// If this event filter is on any non-QWidget, it will crash.
690 //---------------------------------------------------------
691
eventFilter(QObject * watched,QEvent * event)692 bool InspectorScrollPreventer::eventFilter(QObject* watched, QEvent* event)
693 {
694 if (event->type() != QEvent::Wheel)
695 return QObject::eventFilter(watched, event);
696
697 if (!qobject_cast<QWidget*>(watched)->hasFocus())
698 return true;
699
700 return QObject::eventFilter(watched, event);
701 }
702
703 //---------------------------------------------------------
704 // event
705 //---------------------------------------------------------
706
event(EventType evtType,const InspectorItem & ii,const Element * e)707 void InspectorEventObserver::event(EventType evtType, const InspectorItem& ii, const Element* e)
708 {
709 #ifndef TELEMETRY_DISABLED
710 //if inspector data IS the enabled telemetry data
711 if (Ms::enabledTelemetryDataTypes & Ms::TelemetryDataCollectionType::COLLECT_INSPECTOR_DATA) {
712 QString evtCategory;
713 switch (evtType) {
714 case EventType::PropertyChange:
715 evtCategory = QStringLiteral("inspector-property-change");
716 break;
717 case EventType::PropertyReset:
718 evtCategory = QStringLiteral("inspector-property-reset");
719 break;
720 case EventType::PropertySetStyle:
721 evtCategory = QStringLiteral("inspector-property-set-style");
722 break;
723 }
724
725 const QObject* w = ii.w;
726 const QObject* p = w->parent();
727 while (p && !qobject_cast<const InspectorBase*>(p)) {
728 w = p;
729 p = p->parent();
730 }
731 const QString inspectorName = w->objectName();
732
733 const QString evtAction = QStringLiteral("%1/%2").arg(inspectorName).arg(propertyName(ii.t));
734 const QString evtLabel = e ? e->name() : "null";
735 TelemetryManager::telemetryService()->sendEvent(evtCategory, evtAction, evtLabel);
736 }
737 #else
738 Q_UNUSED(evtType);
739 Q_UNUSED(ii);
740 Q_UNUSED(e);
741 #endif
742 }
743 }
744
745