PropertyBox.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2006 Chris Cannam and QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #include "PropertyBox.h"
17 #include "PluginParameterDialog.h"
18 
19 #include "base/PropertyContainer.h"
20 #include "base/PlayParameters.h"
21 #include "base/PlayParameterRepository.h"
22 #include "layer/Layer.h"
23 #include "base/UnitDatabase.h"
24 #include "base/RangeMapper.h"
25 
26 #include "AudioDial.h"
27 #include "LEDButton.h"
28 #include "IconLoader.h"
29 #include "LevelPanWidget.h"
30 #include "LevelPanToolButton.h"
31 #include "WidgetScale.h"
32 #include "MenuTitle.h"
33 
34 #include "NotifyingCheckBox.h"
35 #include "NotifyingComboBox.h"
36 #include "NotifyingPushButton.h"
37 #include "NotifyingToolButton.h"
38 #include "ColourComboBox.h"
39 #include "ColourMapComboBox.h"
40 
41 #include <QGridLayout>
42 #include <QHBoxLayout>
43 #include <QVBoxLayout>
44 #include <QPushButton>
45 #include <QToolButton>
46 #include <QLabel>
47 #include <QFrame>
48 #include <QApplication>
49 #include <QColorDialog>
50 #include <QInputDialog>
51 #include <QDir>
52 #include <QMenu>
53 
54 #include <cassert>
55 #include <iostream>
56 #include <cmath>
57 
58 //#define DEBUG_PROPERTY_BOX 1
59 
60 PropertyBox::PropertyBox(PropertyContainer *container) :
61  m_container(container),
62  m_showButton(nullptr),
63  m_playButton(nullptr),
64  m_lastContextMenu(nullptr),
65  m_contextMenuOn(nullptr)
66 {
67 #ifdef DEBUG_PROPERTY_BOX
68  SVDEBUG << "PropertyBox[" << this << "(\"" <<
69  container->getPropertyContainerName() << "\" at " << container << ")]::PropertyBox" << endl;
70 #endif
71 
72  m_mainBox = new QVBoxLayout;
73  setLayout(m_mainBox);
74 
75 #ifdef Q_OS_MAC
76  QMargins mm = m_mainBox->contentsMargins();
77  QMargins mmhalf(mm.left()/2, mm.top()/3, mm.right()/2, mm.bottom()/3);
78  m_mainBox->setContentsMargins(mmhalf);
79 #endif
80 
81 // m_nameWidget = new QLabel;
82 // m_mainBox->addWidget(m_nameWidget);
83 // m_nameWidget->setText(container->objectName());
84 
85  m_mainWidget = new QWidget;
86  m_mainBox->addWidget(m_mainWidget);
87  m_mainBox->insertStretch(2, 10);
88 
89  m_viewPlayFrame = nullptr;
91 
92  m_layout = new QGridLayout;
93  m_layout->setMargin(0);
94  m_layout->setHorizontalSpacing(2);
95  m_layout->setVerticalSpacing(1);
96  m_mainWidget->setLayout(m_layout);
97 
98  PropertyContainer::PropertyList properties = m_container->getProperties();
99 
100  blockSignals(true);
101 
102  size_t i;
103 
104  for (i = 0; i < properties.size(); ++i) {
105  updatePropertyEditor(properties[i]);
106  }
107 
108  blockSignals(false);
109 
110  m_layout->setRowStretch(m_layout->rowCount(), 10);
111 
112  connect(UnitDatabase::getInstance(), SIGNAL(unitDatabaseChanged()),
113  this, SLOT(unitDatabaseChanged()));
114 
115 #ifdef DEBUG_PROPERTY_BOX
116  SVDEBUG << "PropertyBox[" << this << "]::PropertyBox returning" << endl;
117 #endif
118 }
119 
121 {
122 #ifdef DEBUG_PROPERTY_BOX
123  SVDEBUG << "PropertyBox[" << this << "]::~PropertyBox" << endl;
124 #endif
125  delete m_lastContextMenu;
126 }
127 
128 void
130 {
131 #ifdef DEBUG_PROPERTY_BOX
132  SVDEBUG << "PropertyBox[" << this << ":" << m_container << "]::populateViewPlayFrame" << endl;
133 #endif
134 
135  if (m_viewPlayFrame) {
136  delete m_viewPlayFrame;
137  m_viewPlayFrame = nullptr;
138  }
139 
140  if (!m_container) return;
141 
142  Layer *layer = dynamic_cast<Layer *>(m_container);
143  if (layer) {
144  disconnect(layer, SIGNAL(modelReplaced()),
145  this, SLOT(populateViewPlayFrame()));
146  connect(layer, SIGNAL(modelReplaced()),
147  this, SLOT(populateViewPlayFrame()));
148  }
149 
150  auto params = m_container->getPlayParameters();
151  if (!params && !layer) return;
152 
153  m_viewPlayFrame = new QFrame;
154  m_viewPlayFrame->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
155  m_mainBox->addWidget(m_viewPlayFrame);
156 
157  QGridLayout *layout = new QGridLayout;
158  m_viewPlayFrame->setLayout(layout);
159 
160  layout->setMargin(layout->margin() / 2);
161 
162 #ifdef DEBUG_PROPERTY_BOX
163  SVDEBUG << "PropertyBox::populateViewPlayFrame: container " << m_container << " (name " << m_container->getPropertyContainerName() << ") params " << params << endl;
164 #endif
165 
166  QSize buttonSize = WidgetScale::scaleQSize(QSize(26, 26));
167  int col = 0;
168 
169  if (params) {
170 
172  m_playButton->setCheckable(true);
173  m_playButton->setIcon(IconLoader().load("speaker"));
174  m_playButton->setToolTip(tr("Click to toggle playback"));
175  m_playButton->setChecked(!params->isPlayMuted());
176  m_playButton->setFixedSize(buttonSize);
177  connect(m_playButton, SIGNAL(toggled(bool)),
178  this, SLOT(playAudibleButtonChanged(bool)));
179  connect(m_playButton, SIGNAL(mouseEntered()),
180  this, SLOT(mouseEnteredWidget()));
181  connect(m_playButton, SIGNAL(mouseLeft()),
182  this, SLOT(mouseLeftWidget()));
183  connect(params.get(), SIGNAL(playAudibleChanged(bool)),
184  this, SLOT(playAudibleChanged(bool)));
185 
186  LevelPanToolButton *levelPan = new LevelPanToolButton;
187  levelPan->setFixedSize(buttonSize);
188  levelPan->setImageSize((buttonSize.height() * 3) / 4);
189  layout->addWidget(levelPan, 0, col++, Qt::AlignCenter);
190  connect(levelPan, SIGNAL(levelChanged(float)),
191  this, SLOT(playGainControlChanged(float)));
192  connect(levelPan, SIGNAL(panChanged(float)),
193  this, SLOT(playPanControlChanged(float)));
194  connect(params.get(), SIGNAL(playGainChanged(float)),
195  levelPan, SLOT(setLevel(float)));
196  connect(params.get(), SIGNAL(playPanChanged(float)),
197  levelPan, SLOT(setPan(float)));
198  connect(levelPan, SIGNAL(mouseEntered()),
199  this, SLOT(mouseEnteredWidget()));
200  connect(levelPan, SIGNAL(mouseLeft()),
201  this, SLOT(mouseLeftWidget()));
202 
203  layout->addWidget(m_playButton, 0, col++, Qt::AlignCenter);
204 
205  if (params->getPlayClipId() != "") {
206  NotifyingToolButton *playParamButton = new NotifyingToolButton;
207  playParamButton->setObjectName("playParamButton");
208  playParamButton->setIcon(IconLoader().load("faders"));
209  playParamButton->setFixedSize(buttonSize);
210  layout->addWidget(playParamButton, 0, col++, Qt::AlignCenter);
211  connect(playParamButton, SIGNAL(clicked()),
212  this, SLOT(editPlayParameters()));
213  connect(playParamButton, SIGNAL(mouseEntered()),
214  this, SLOT(mouseEnteredWidget()));
215  connect(playParamButton, SIGNAL(mouseLeft()),
216  this, SLOT(mouseLeftWidget()));
217  }
218  }
219 
220  layout->setColumnStretch(col++, 10);
221 
222  if (layer) {
223 
224  QLabel *showLabel = new QLabel(tr("Show"));
225  layout->addWidget(showLabel, 0, col++, Qt::AlignVCenter | Qt::AlignRight);
226 
227  m_showButton = new LEDButton(palette().highlight().color());
228  layout->addWidget(m_showButton, 0, col++, Qt::AlignVCenter | Qt::AlignLeft);
229  connect(m_showButton, SIGNAL(stateChanged(bool)),
230  this, SIGNAL(showLayer(bool)));
231  connect(m_showButton, SIGNAL(mouseEntered()),
232  this, SLOT(mouseEnteredWidget()));
233  connect(m_showButton, SIGNAL(mouseLeft()),
234  this, SLOT(mouseLeftWidget()));
235  }
236 }
237 
238 void
239 PropertyBox::updatePropertyEditor(PropertyContainer::PropertyName name,
240  bool rangeChanged)
241 {
242  PropertyContainer::PropertyType type = m_container->getPropertyType(name);
243  int row = m_layout->rowCount();
244 
245  int min = 0, max = 0, value = 0, deflt = 0;
246  value = m_container->getPropertyRangeAndValue(name, &min, &max, &deflt);
247 
248  bool have = (m_propertyControllers.find(name) !=
249  m_propertyControllers.end());
250 
251  QString groupName = m_container->getPropertyGroupName(name);
252  QString propertyLabel = m_container->getPropertyLabel(name);
253  QString iconName = m_container->getPropertyIconName(name);
254 
255 #ifdef DEBUG_PROPERTY_BOX
256  SVDEBUG << "PropertyBox[" << this
257  << "(\"" << m_container->getPropertyContainerName()
258  << "\")]";
259  SVDEBUG << "::updatePropertyEditor(\"" << name << "\", "
260  << rangeChanged << "):";
261  SVDEBUG << " type " << type << ", value " << value
262  << ", have " << have << ", group \"" << groupName << "\"" << endl;
263 #endif
264 
265  QString groupLabel = groupName;
266  if (groupName == QString()) {
267  groupName = "ungrouped: " + name; // not tr(), this is internal id
268  groupLabel = propertyLabel;
269  }
270 
271  if (!have) {
272  if (m_groupLayouts.find(groupName) == m_groupLayouts.end()) {
273  QWidget *labelWidget = new QLabel(groupLabel, m_mainWidget);
274  m_layout->addWidget(labelWidget, row, 0);
275  QWidget *frame = new QWidget(m_mainWidget);
276  frame->setMinimumSize(WidgetScale::scaleQSize(QSize(1, 24)));
277  m_groupLayouts[groupName] = new QGridLayout;
278 #ifdef Q_OS_MAC
279  // Seems to be plenty of whitespace already
280  m_groupLayouts[groupName]->setContentsMargins(0, 0, 0, 0);
281 #else
282  // Need a bit of padding on the left
283  m_groupLayouts[groupName]->setContentsMargins
284  (WidgetScale::scalePixelSize(10), 0, 0, 0);
285 #endif
286  frame->setLayout(m_groupLayouts[groupName]);
287  m_layout->addWidget(frame, row, 1, 1, 2);
288  m_layout->setColumnStretch(1, 10);
289  }
290  }
291 
292  QGridLayout *groupLayout = m_groupLayouts[groupName];
293 
294 #ifdef DEBUG_PROPERTY_BOX
295  SVDEBUG << "groupName becomes \"" << groupName << "\", groupLabel = \""
296  << groupLabel << "\", groupLayout = " << groupLayout << endl;
297 #endif
298 
299  assert(groupLayout);
300 
301  QWidget *existing = m_propertyControllers[name];
302 
303  switch (type) {
304 
305  case PropertyContainer::ToggleProperty:
306  {
307  QAbstractButton *button;
308 
309  if (!(button = qobject_cast<QAbstractButton *>(existing))) {
310 #ifdef DEBUG_PROPERTY_BOX
311  SVDEBUG << "PropertyBox: creating new checkbox" << endl;
312 #endif
313  if (iconName != "") {
314 #ifdef Q_OS_MAC
315  button = new NotifyingToolButton();
316 #else
317  button = new NotifyingPushButton();
318 #endif
319  button->setCheckable(true);
320  QIcon icon(IconLoader().load(iconName));
321  button->setIcon(icon);
322  button->setObjectName(name);
323  button->setFixedSize(WidgetScale::scaleQSize(QSize(18, 18)));
324  } else {
325  button = new NotifyingCheckBox();
326  button->setObjectName(name);
327  }
328  connect(button, SIGNAL(toggled(bool)),
329  this, SLOT(propertyControllerChanged(bool)));
330  connect(button, SIGNAL(mouseEntered()),
331  this, SLOT(mouseEnteredWidget()));
332  connect(button, SIGNAL(mouseLeft()),
333  this, SLOT(mouseLeftWidget()));
334  button->setToolTip(propertyLabel);
335 
336  button->setContextMenuPolicy(Qt::CustomContextMenu);
337  connect(button, SIGNAL(customContextMenuRequested(const QPoint &)),
338  this, SLOT(contextMenuRequested(const QPoint &)));
339 
340  if (existing) {
341  groupLayout->replaceWidget(existing, button);
342  delete existing;
343  } else {
344  groupLayout->addWidget(button, 0, groupLayout->columnCount());
345  }
346 
347  m_propertyControllers[name] = button;
348  }
349 
350  if (button->isChecked() != (value > 0)) {
351  button->blockSignals(true);
352  button->setChecked(value > 0);
353  button->blockSignals(false);
354  }
355  break;
356  }
357 
358  case PropertyContainer::RangeProperty:
359  {
360  AudioDial *dial;
361 
362  if ((dial = qobject_cast<AudioDial *>(existing))) {
363  if (rangeChanged) {
364  dial->blockSignals(true);
365  dial->setMinimum(min);
366  dial->setMaximum(max);
367  dial->setRangeMapper(m_container->getNewPropertyRangeMapper(name));
368  dial->blockSignals(false);
369  }
370  } else {
371 #ifdef DEBUG_PROPERTY_BOX
372  SVDEBUG << "PropertyBox: creating new dial" << endl;
373 #endif
374  dial = new AudioDial();
375  dial->setObjectName(name);
376  dial->setMinimum(min);
377  dial->setMaximum(max);
378  dial->setPageStep(1);
379  dial->setNotchesVisible((max - min) <= 12);
380  // important to set the range mapper before the default,
381  // because the range mapper is used to map the default
382  dial->setRangeMapper(m_container->getNewPropertyRangeMapper(name));
383  dial->setDefaultValue(deflt);
384  dial->setShowToolTip(true);
385  connect(dial, SIGNAL(valueChanged(int)),
386  this, SLOT(propertyControllerChanged(int)));
387  connect(dial, SIGNAL(mouseEntered()),
388  this, SLOT(mouseEnteredWidget()));
389  connect(dial, SIGNAL(mouseLeft()),
390  this, SLOT(mouseLeftWidget()));
391 
392  dial->setFixedWidth(WidgetScale::scalePixelSize(24));
393  dial->setFixedHeight(WidgetScale::scalePixelSize(24));
394 
395  if (existing) {
396  groupLayout->replaceWidget(existing, dial);
397  delete existing;
398  } else {
399  groupLayout->addWidget(dial, 0, groupLayout->columnCount());
400  }
401 
402  m_propertyControllers[name] = dial;
403  }
404 
405  if (dial->value() != value) {
406  dial->blockSignals(true);
407  dial->setValue(value);
408  dial->blockSignals(false);
409  }
410  break;
411  }
412 
413  case PropertyContainer::ColourProperty:
414  {
415  ColourComboBox *cb;
416 
417  if (!(cb = qobject_cast<ColourComboBox *>(existing))) {
418 
419 #ifdef DEBUG_PROPERTY_BOX
420  SVDEBUG << "PropertyBox: creating new colour combobox" << endl;
421 #endif
422  cb = new ColourComboBox(true);
423  cb->setObjectName(name);
424 
425  connect(cb, SIGNAL(colourChanged(int)),
426  this, SLOT(propertyControllerChanged(int)));
427  connect(cb, SIGNAL(mouseEntered()),
428  this, SLOT(mouseEnteredWidget()));
429  connect(cb, SIGNAL(mouseLeft()),
430  this, SLOT(mouseLeftWidget()));
431 
432  cb->setToolTip(propertyLabel);
433 
434  cb->setContextMenuPolicy(Qt::CustomContextMenu);
435  connect(cb, SIGNAL(customContextMenuRequested(const QPoint &)),
436  this, SLOT(contextMenuRequested(const QPoint &)));
437 
438  if (existing) {
439  groupLayout->replaceWidget(existing, cb);
440  delete existing;
441  } else {
442  groupLayout->addWidget(cb, 0, groupLayout->columnCount());
443  }
444 
445  m_propertyControllers[name] = cb;
446  }
447 
448  if (cb->currentIndex() != value) {
449  cb->blockSignals(true);
450  cb->setCurrentIndex(value);
451  cb->blockSignals(false);
452  }
453 
454  break;
455  }
456 
457  case PropertyContainer::ColourMapProperty:
458  {
459  ColourMapComboBox *cb;
460 
461  if (!(cb = qobject_cast<ColourMapComboBox *>(existing))) {
462 #ifdef DEBUG_PROPERTY_BOX
463  SVDEBUG << "PropertyBox: creating new colourmap combobox" << endl;
464 #endif
465  cb = new ColourMapComboBox(false);
466  cb->setObjectName(name);
467 
468  connect(cb, SIGNAL(colourMapChanged(int)),
469  this, SLOT(propertyControllerChanged(int)));
470  connect(cb, SIGNAL(mouseEntered()),
471  this, SLOT(mouseEnteredWidget()));
472  connect(cb, SIGNAL(mouseLeft()),
473  this, SLOT(mouseLeftWidget()));
474 
475  cb->setToolTip(propertyLabel);
476 
477  if (existing) {
478  groupLayout->replaceWidget(existing, cb);
479  delete existing;
480  } else {
481  groupLayout->addWidget(cb, 0, groupLayout->columnCount());
482  }
483 
484  m_propertyControllers[name] = cb;
485  }
486 
487  if (cb->currentIndex() != value) {
488  cb->blockSignals(true);
489  cb->setCurrentIndex(value);
490  cb->blockSignals(false);
491  }
492 
493  break;
494  }
495 
496  case PropertyContainer::ValueProperty:
497  case PropertyContainer::UnitsProperty:
498  {
499  NotifyingComboBox *cb;
500 
501  if (!(cb = qobject_cast<NotifyingComboBox *>(existing))) {
502 #ifdef DEBUG_PROPERTY_BOX
503  SVDEBUG << "PropertyBox: creating new combobox" << endl;
504 #endif
505  cb = new NotifyingComboBox();
506  cb->setObjectName(name);
507  cb->setDuplicatesEnabled(false);
508  }
509 
510  if (!have || rangeChanged) {
511 
512  cb->blockSignals(true);
513  cb->clear();
514  cb->setEditable(false);
515 
516  if (type == PropertyContainer::ValueProperty) {
517 
518  for (int i = min; i <= max; ++i) {
519 
520  QString label = m_container->getPropertyValueLabel(name, i);
521  QString iname = m_container->getPropertyValueIconName(name, i);
522 
523  if (iname != "") {
524  QIcon icon(IconLoader().load(iname));
525  cb->addItem(icon, label);
526  } else {
527  cb->addItem(label);
528  }
529  }
530 
531  } else { // PropertyContainer::UnitsProperty
532 
533  QStringList units = UnitDatabase::getInstance()->getKnownUnits();
534  for (int i = 0; i < units.size(); ++i) {
535  cb->addItem(units[i]);
536  }
537 
538  cb->setEditable(true);
539  }
540  }
541 
542  if (!have) {
543  connect(cb, SIGNAL(activated(int)),
544  this, SLOT(propertyControllerChanged(int)));
545  connect(cb, SIGNAL(mouseEntered()),
546  this, SLOT(mouseEnteredWidget()));
547  connect(cb, SIGNAL(mouseLeft()),
548  this, SLOT(mouseLeftWidget()));
549 
550  cb->setToolTip(propertyLabel);
551 
552  cb->setContextMenuPolicy(Qt::CustomContextMenu);
553  connect(cb, SIGNAL(customContextMenuRequested(const QPoint &)),
554  this, SLOT(contextMenuRequested(const QPoint &)));
555 
556  groupLayout->addWidget(cb, 0, groupLayout->columnCount());
557  m_propertyControllers[name] = cb;
558  } else if (existing != cb) {
559  groupLayout->replaceWidget(existing, cb);
560  delete existing;
561  }
562 
563  cb->blockSignals(true);
564  if (type == PropertyContainer::ValueProperty) {
565  if (cb->currentIndex() != value) {
566  cb->setCurrentIndex(value);
567  }
568  } else {
569  QString unit = UnitDatabase::getInstance()->getUnitById(value);
570  if (cb->currentText() != unit) {
571  for (int i = 0; i < cb->count(); ++i) {
572  if (cb->itemText(i) == unit) {
573  cb->setCurrentIndex(i);
574  break;
575  }
576  }
577  }
578  }
579  cb->blockSignals(false);
580 
581  break;
582  }
583 
584  case PropertyContainer::InvalidProperty:
585  default:
586  break;
587  }
588 }
589 
590 void
592 {
593  if (pc != m_container) return;
594 
595 #ifdef DEBUG_PROPERTY_BOX
596  SVDEBUG << "PropertyBox::propertyContainerPropertyChanged" << endl;
597 #endif
598 
599  PropertyContainer::PropertyList properties = m_container->getProperties();
600  size_t i;
601 
602  blockSignals(true);
603 
604  for (i = 0; i < properties.size(); ++i) {
605  updatePropertyEditor(properties[i]);
606  }
607 
608  blockSignals(false);
609 }
610 
611 void
613 {
614  blockSignals(true);
615 
616  PropertyContainer::PropertyList properties = m_container->getProperties();
617  for (size_t i = 0; i < properties.size(); ++i) {
618  updatePropertyEditor(properties[i], true);
619  }
620 
621  blockSignals(false);
622 }
623 
624 void
626 {
627 #ifdef DEBUG_PROPERTY_BOX
628  SVDEBUG << "PropertyBox[" << this << "]: unitDatabaseChanged" << endl;
629 #endif
630  blockSignals(true);
631 
632 // SVDEBUG << "my container is " << m_container << endl;
633 // SVDEBUG << "my container's name is... " << endl;
634 // SVDEBUG << m_container->objectName() << endl;
635 
636  PropertyContainer::PropertyList properties = m_container->getProperties();
637  for (size_t i = 0; i < properties.size(); ++i) {
638  if (m_container->getPropertyType(properties[i]) ==
639  PropertyContainer::UnitsProperty) {
640  updatePropertyEditor(properties[i]);
641  }
642  }
643 
644  blockSignals(false);
645 }
646 
647 void
649 {
650  propertyControllerChanged(on ? 1 : 0);
651 }
652 
653 void
655 {
656  QObject *obj = sender();
657  QString name = obj->objectName();
658 
659 #ifdef DEBUG_PROPERTY_BOX
660  SVDEBUG << "PropertyBox::propertyControllerChanged(" << name
661  << ", " << value << ")" << endl;
662 #endif
663 
664  PropertyContainer::PropertyType type = m_container->getPropertyType(name);
665 
666  Command *c = nullptr;
667 
668  if (type == PropertyContainer::UnitsProperty) {
669 
670  NotifyingComboBox *cb = qobject_cast<NotifyingComboBox *>(obj);
671  if (cb) {
672  QString unit = cb->currentText();
673  c = m_container->getSetPropertyCommand
674  (name, UnitDatabase::getInstance()->getUnitId(unit));
675  }
676 
677  } else if (type != PropertyContainer::InvalidProperty) {
678 
679  c = m_container->getSetPropertyCommand(name, value);
680  }
681 
682  if (c) CommandHistory::getInstance()->addCommand(c, true, true);
683 
684  updateContextHelp(obj);
685 }
686 
687 void
689 {
690  QObject *obj = sender();
691  QString name = obj->objectName();
692 
693  QString label = m_container->getPropertyLabel(name);
694  int min = 0, max = 0, value = 0, deflt = 0;
695  value = m_container->getPropertyRangeAndValue(name, &min, &max, &deflt);
696 
697  delete m_lastContextMenu;
698  QMenu *m = new QMenu;
699 
700  m_lastContextMenu = m;
701  m_contextMenuOn = obj;
702 
703  if (qobject_cast<QAbstractButton *>(obj)) {
704  if (value > 0) {
705  MenuTitle::addTitle(m, tr("%1: On").arg(label));
706  } else {
707  MenuTitle::addTitle(m, tr("%1: Off").arg(label));
708  }
709  m->addAction(tr("&Reset to Default"), this,
711 
712  } else if (auto cb = qobject_cast<QComboBox *>(obj)) {
713  MenuTitle::addTitle(m, tr("%1: %2").arg(label).arg(cb->itemText(value)));
714  m->addAction(tr("&Reset to Default"), this,
716 
717  } else {
718  // AudioDial has its own context menu, we don't handle it here
719  return;
720  }
721 
722  m->popup(qobject_cast<QWidget *>(sender())->mapToGlobal(pos));
723 }
724 
725 void
727 {
728  if (!m_contextMenuOn) return;
729 
730  QString name = m_contextMenuOn->objectName();
731 
732  QString label = m_container->getPropertyLabel(name);
733  int min = 0, max = 0, deflt = 0;
734  (void)m_container->getPropertyRangeAndValue(name, &min, &max, &deflt);
735 
736  if (auto button = qobject_cast<QAbstractButton *>(m_contextMenuOn)) {
737  button->setChecked(deflt > 0);
738  } else if (auto cb = qobject_cast<QComboBox *>(m_contextMenuOn)) {
739  cb->setCurrentIndex(deflt);
740  }
741 }
742 
743 void
745 {
746  m_playButton->setChecked(audible);
747 }
748 
749 void
751 {
752  auto params = m_container->getPlayParameters();
753  if (!params) return;
754 
755  if (params->isPlayAudible() != audible) {
756  PlayParameterRepository::EditCommand *command =
757  new PlayParameterRepository::EditCommand(params);
758  command->setPlayAudible(audible);
759  CommandHistory::getInstance()->addCommand(command, true, true);
760  }
761 }
762 
763 void
765 {
766  QObject *obj = sender();
767 
768  auto params = m_container->getPlayParameters();
769  if (!params) return;
770 
771  if (params->getPlayGain() != gain) {
772  PlayParameterRepository::EditCommand *command =
773  new PlayParameterRepository::EditCommand(params);
774  command->setPlayGain(gain);
775  CommandHistory::getInstance()->addCommand(command, true, true);
776  }
777 
778  updateContextHelp(obj);
779 }
780 
781 void
783 {
784  QObject *obj = sender();
785 
786  auto params = m_container->getPlayParameters();
787  if (!params) return;
788 
789  if (params->getPlayPan() != pan) {
790  PlayParameterRepository::EditCommand *command =
791  new PlayParameterRepository::EditCommand(params);
792  command->setPlayPan(pan);
793  CommandHistory::getInstance()->addCommand(command, true, true);
794  }
795 
796  updateContextHelp(obj);
797 }
798 
799 void
801 {
802  auto params = m_container->getPlayParameters();
803  if (!params) return;
804 
805  QString clip = params->getPlayClipId();
806 
807  PlayParameterRepository::EditCommand *command =
808  new PlayParameterRepository::EditCommand(params);
809 
810  QInputDialog *dialog = new QInputDialog(this);
811 
812  QDir dir(":/samples");
813  QStringList clipFiles = dir.entryList(QStringList() << "*.wav", QDir::Files);
814 
815  QStringList clips;
816  foreach (QString str, clipFiles) {
817  clips.push_back(str.replace(".wav", ""));
818  }
819  dialog->setComboBoxItems(clips);
820 
821  dialog->setLabelText(tr("Set playback clip:"));
822 
823  QComboBox *cb = dialog->findChild<QComboBox *>();
824  if (cb) {
825  for (int i = 0; i < cb->count(); ++i) {
826  if (cb->itemText(i) == clip) {
827  cb->setCurrentIndex(i);
828  }
829  }
830  }
831 
832  connect(dialog, SIGNAL(textValueChanged(QString)),
833  this, SLOT(playClipChanged(QString)));
834 
835  if (dialog->exec() == QDialog::Accepted) {
836  QString newClip = dialog->textValue();
837  command->setPlayClipId(newClip);
838  CommandHistory::getInstance()->addCommand(command, true);
839  } else {
840  delete command;
841  // restore in case we mucked about with the configuration
842  // as a consequence of signals from the dialog
843  params->setPlayClipId(clip);
844  }
845 
846  delete dialog;
847 }
848 
849 void
851 {
852  auto params = m_container->getPlayParameters();
853  if (!params) return;
854 
855  params->setPlayClipId(id);
856 }
857 
858 void
860 {
861  if (m_showButton) m_showButton->setState(visible);
862 }
863 
864 void
866 {
867  updateContextHelp(sender());
868 }
869 
870 void
872 {
873  QWidget *w = qobject_cast<QWidget *>(o);
874  if (!w) return;
875 
876  if (!m_container) return;
877  QString cname = m_container->getPropertyContainerName();
878  if (cname == "") return;
879 
880  QString help;
881  QString mainText;
882  QString extraText;
883  QString editText;
884 
885  QString wname = w->objectName();
886  QString propertyLabel;
887  if (wname != "") {
888  propertyLabel = m_container->getPropertyLabel(wname);
889  }
890 
891  LevelPanToolButton *lp = qobject_cast<LevelPanToolButton *>(w);
892  AudioDial *dial = qobject_cast<AudioDial *>(w);
893 
894  if (lp) {
895 
896  mainText = tr("Adjust playback level and pan of %1").arg(cname);
897  editText = tr("click then drag to adjust, ctrl+click to reset");
898 
899  } else if (wname == "playParamButton") {
900 
901  auto params = m_container->getPlayParameters();
902  if (params) {
903  help = tr("Change sound used for playback (currently \"%1\")")
904  .arg(params->getPlayClipId());
905  }
906 
907  } else if (dial) {
908 
909  double mv = dial->mappedValue();
910  QString unit = "";
911  if (dial->rangeMapper()) unit = dial->rangeMapper()->getUnit();
912  if (unit != "") {
913  extraText = tr(" (current value: %1%2)").arg(mv).arg(unit);
914  } else {
915  extraText = tr(" (current value: %1)").arg(mv);
916  }
917  editText = tr("drag up/down to adjust, ctrl+click to reset");
918 
919  } else if (w == m_showButton) {
920  help = tr("Toggle Visibility of %1").arg(cname);
921 
922  } else if (w == m_playButton) {
923  help = tr("Toggle Playback of %1").arg(cname);
924 
925  }
926 
927  if (help == "" && wname != "") {
928 
929  if (qobject_cast<QAbstractButton *>(w)) {
930  mainText = tr("Toggle %1 property of %2")
931  .arg(propertyLabel).arg(cname);
932 
933  } else {
934 
935  // Last param empty for historical reasons, to avoid
936  // changing tr() string
937  mainText = tr("Adjust %1 property of %2%3")
938  .arg(propertyLabel).arg(cname).arg("");
939  }
940  }
941 
942  if (help == "") {
943  if (mainText != "") {
944  if (editText != "") {
945  help = tr("%1%2: %3")
946  .arg(mainText).arg(extraText).arg(editText);
947  } else {
948  help = tr("%1%2")
949  .arg(mainText).arg(extraText);
950  }
951  }
952  }
953 
954  if (help != "") {
955  emit contextHelpChanged(help);
956  }
957 }
958 
959 void
961 {
962  if (!(QApplication::mouseButtons() & Qt::LeftButton)) {
963  emit contextHelpChanged("");
964  }
965 }
966 
967 
Very trivial enhancement to QComboBox to make it emit signals when the mouse enters and leaves (for c...
QGridLayout * m_layout
Definition: PropertyBox.h:82
void propertyContainerPropertyChanged(PropertyContainer *)
QFrame * m_viewPlayFrame
Definition: PropertyBox.h:84
Very trivial enhancement to QPushButton to make it emit signals when the mouse enters and leaves (for...
The base class for visual representations of the data found in a Model.
Definition: Layer.h:54
static void addTitle(QMenu *m, QString text)
Definition: MenuTitle.h:29
Very trivial enhancement to QToolButton to make it emit signals when the mouse enters and leaves (for...
void playPanControlChanged(float)
Very trivial enhancement to QCheckBox to make it emit signals when the mouse enters and leaves (for c...
void setDefaultValue(int defaultValue)
Definition: AudioDial.cpp:398
void mouseEnteredWidget()
Colour-picker combo box with swatches, optionally including "Add New Colour..." entry to invoke a QCo...
Colour map picker combo box with optional swatches.
std::map< QString, QWidget * > m_propertyControllers
Definition: PropertyBox.h:91
void updateContextHelp(QObject *o)
void propertyControllerChanged(int)
QVBoxLayout * m_mainBox
Definition: PropertyBox.h:85
void addCommand(Command *command)
Add a command to the command history.
static QSize scaleQSize(QSize size)
Definition: WidgetScale.h:58
static int scalePixelSize(int pixels)
Take a "design pixel" size and scale it for the actual display.
Definition: WidgetScale.h:31
void playGainControlChanged(float)
void updatePropertyEditor(PropertyContainer::PropertyName, bool rangeChanged=false)
LEDButton * m_showButton
Definition: PropertyBox.h:86
QWidget * m_mainWidget
Definition: PropertyBox.h:81
void contextHelpChanged(const QString &)
QToolButton * m_playButton
Definition: PropertyBox.h:87
void playAudibleChanged(bool)
QMenu * m_lastContextMenu
Definition: PropertyBox.h:88
AudioDial is a nicer-looking QDial that by default reacts to mouse movement on horizontal and vertica...
Definition: AudioDial.h:60
void layerVisibilityChanged(bool)
void showLayer(bool)
void propertyContainerPropertyRangeChanged(PropertyContainer *)
void propertyControllerResetRequested()
std::map< QString, QGridLayout * > m_groupLayouts
Definition: PropertyBox.h:90
void contextMenuRequested(const QPoint &)
PropertyContainer * m_container
Definition: PropertyBox.h:83
void editPlayParameters()
void setValue(int value)
Definition: AudioDial.cpp:406
static CommandHistory * getInstance()
void setState(bool)
Definition: LEDButton.cpp:219
void mouseLeftWidget()
PropertyBox(PropertyContainer *)
Definition: PropertyBox.cpp:60
void setImageSize(int pixels)
void setRangeMapper(RangeMapper *mapper)
Definition: AudioDial.cpp:126
void populateViewPlayFrame()
void playAudibleButtonChanged(bool)
void playClipChanged(QString)
void unitDatabaseChanged()
QObject * m_contextMenuOn
Definition: PropertyBox.h:89
void setShowToolTip(bool show)
Definition: AudioDial.cpp:440