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("&lt;sym&gt;","<sym>").replace("&lt;/sym&gt;","</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