1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Rosegarden
5 A MIDI and audio sequencer and musical notation editor.
6 Copyright 2000-2021 the Rosegarden development team.
7
8 Other copyrights also apply to some parts of this work. Please
9 see the AUTHORS file and individual file headers for details.
10
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License as
13 published by the Free Software Foundation; either version 2 of the
14 License, or (at your option) any later version. See the file
15 COPYING included with this distribution for more information.
16 */
17
18 #define RG_MODULE_STRING "[ControlRulerWidget]"
19
20 #include "ControlRulerWidget.h"
21
22 #include "ControlRuler.h"
23 #include "ControlRulerTabBar.h"
24 #include "ControllerEventsRuler.h"
25 #include "PropertyControlRuler.h"
26
27 #include "base/BaseProperties.h"
28 #include "base/Composition.h"
29 #include "base/ControlParameter.h"
30 #include "base/Controllable.h"
31 #include "base/Device.h"
32 #include "base/Instrument.h"
33 #include "base/MidiDevice.h"
34 #include "base/MidiTypes.h" // for PitchBend::EventType
35 #include "base/PropertyName.h"
36 #include "document/RosegardenDocument.h"
37 #include "gui/application/RosegardenMainWindow.h"
38 #include "base/Segment.h"
39 #include "base/Selection.h" // for EventSelection
40 #include "base/parameterpattern/SelectionSituation.h"
41 #include "base/SoftSynthDevice.h"
42 #include "base/Studio.h"
43 #include "base/Track.h"
44
45 #include "misc/Debug.h"
46
47 #include <QVBoxLayout>
48 #include <QStackedWidget>
49
50
51 namespace Rosegarden
52 {
53
54
ControlRulerWidget()55 ControlRulerWidget::ControlRulerWidget() :
56 m_viewSegment(nullptr),
57 m_controlRulerList(),
58 m_scale(nullptr),
59 m_leftMargin(0),
60 m_currentToolName(),
61 m_pannedRect(),
62 m_selectedElements()
63 {
64 QVBoxLayout *layout = new QVBoxLayout;
65 layout->setContentsMargins(0, 0, 0, 0);
66 layout->setSpacing(0);
67 setLayout(layout);
68
69 // Stacked Widget
70 m_stackedWidget = new QStackedWidget;
71 layout->addWidget(m_stackedWidget);
72
73 // Tab Bar
74 m_tabBar = new ControlRulerTabBar;
75
76 // sizeHint() is the maximum allowed, and the widget is still useful if made
77 // smaller than this, but should never grow larger
78 m_tabBar->setSizePolicy(
79 QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum));
80
81 m_tabBar->setDrawBase(false);
82 m_tabBar->setShape(QTabBar::RoundedSouth);
83
84 layout->addWidget(m_tabBar);
85
86 connect(m_tabBar, &QTabBar::currentChanged,
87 m_stackedWidget, &QStackedWidget::setCurrentIndex);
88
89 connect(m_tabBar, &ControlRulerTabBar::tabCloseRequest,
90 this, &ControlRulerWidget::slotRemoveRuler);
91 }
92
93 void
setViewSegment(ViewSegment * viewSegment)94 ControlRulerWidget::setViewSegment(ViewSegment *viewSegment)
95 {
96 // No change? Bail.
97 if (viewSegment == m_viewSegment)
98 return;
99
100 // Disconnect the previous Segment from updates.
101 // ??? Why doesn't the ruler's setViewSegment() do this for us?
102 if (m_viewSegment) {
103 Segment *segment = &(m_viewSegment->getSegment());
104 if (segment) {
105 disconnect(segment, &Segment::contentsChanged,
106 this, &ControlRulerWidget::slotUpdateRulers);
107 }
108 }
109
110 m_viewSegment = viewSegment;
111
112 // For each ruler, set the ViewSegment.
113 for (ControlRuler *ruler : m_controlRulerList) {
114 ruler->setViewSegment(viewSegment);
115 }
116
117 // Connect current Segment for updates.
118 // ??? Why doesn't the ruler's setViewSegment() do this for us?
119 if (viewSegment) {
120 Segment *segment = &(viewSegment->getSegment());
121 if (segment) {
122 connect(segment, &Segment::contentsChanged,
123 this, &ControlRulerWidget::slotUpdateRulers);
124 }
125 }
126 }
127
slotSetCurrentViewSegment(ViewSegment * viewSegment)128 void ControlRulerWidget::slotSetCurrentViewSegment(ViewSegment *viewSegment)
129 {
130 setViewSegment(viewSegment);
131 }
132
133 void
setRulerScale(RulerScale * scale)134 ControlRulerWidget::setRulerScale(RulerScale *scale)
135 {
136 setRulerScale(scale, 0);
137 }
138
139 void
setRulerScale(RulerScale * scale,int leftMargin)140 ControlRulerWidget::setRulerScale(RulerScale *scale, int leftMargin)
141 {
142 m_scale = scale;
143 m_leftMargin = leftMargin;
144
145 // For each ruler, set the ruler scale.
146 for (ControlRuler *ruler : m_controlRulerList) {
147 ruler->setRulerScale(scale);
148 }
149 }
150
151 const ControlParameter *
getControlParameter2(const Segment & segment,int ccNumber)152 getControlParameter2(const Segment &segment, int ccNumber)
153 {
154 // Get the Device for the Segment.
155
156 RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
157 if (!doc)
158 return nullptr;
159
160 Instrument *instrument = doc->getStudio().getInstrumentFor(&segment);
161 if (!instrument)
162 return nullptr;
163
164 Device *device = instrument->getDevice();
165 if (!device)
166 return nullptr;
167
168 // Get the ControlParameter for the ccNumber.
169
170 Controllable *controllable = device->getControllable();
171 if (!controllable)
172 return nullptr;
173
174 return controllable->getControlParameter(Controller::EventType, ccNumber);
175 }
176
177 void
launchRulers()178 ControlRulerWidget::launchRulers()
179 {
180 if (!m_viewSegment)
181 RG_WARNING << "launchMatrixRulers(): WARNING: No view segment.";
182 if (!m_scale)
183 RG_WARNING << "launchMatrixRulers(): WARNING: No ruler scale.";
184
185 std::set<Segment::Ruler> rulers;
186
187 // For each segment ruler set, compute the union of the ruler sets.
188 for (std::shared_ptr<const Segment::RulerSet> segmentRulerSet :
189 m_segmentRulerSets) {
190 rulers.insert(segmentRulerSet->cbegin(),
191 segmentRulerSet->cend());
192 }
193
194 // For each ruler, bring up that ruler.
195 for (const Segment::Ruler &ruler : rulers) {
196 if (ruler.type == Controller::EventType) {
197 const ControlParameter *cp = getControlParameter2(
198 m_viewSegment->getSegment(), ruler.ccNumber);
199 addControlRuler(*cp);
200 } else if (ruler.type == PitchBend::EventType) {
201 RG_DEBUG << "launchInitialRulers(): Launching pitchbend ruler";
202 addControlRuler(ControlParameter::getPitchBend());
203 } else if (ruler.type == BaseProperties::VELOCITY.getName()) {
204 RG_DEBUG << "launchInitialRulers(): Launching velocity ruler";
205 addPropertyRuler(ruler.type);
206 } else {
207 RG_WARNING << "launchInitialRulers(): WARNING: Unexpected ruler in Segment.";
208 }
209 }
210 }
211
212 void
launchMatrixRulers(std::vector<Segment * > segments)213 ControlRulerWidget::launchMatrixRulers(std::vector<Segment *> segments)
214 {
215 // Launch all rulers from the Segments' ruler lists.
216 // As a separate routine this can be called by the parent after everything
217 // is in place.
218
219 // For each Segment, get the ruler lists.
220 for (Segment *segment : segments) {
221 if (segment->matrixRulers)
222 m_segmentRulerSets.push_back(segment->matrixRulers);
223 }
224
225 launchRulers();
226 }
227
228 void
launchNotationRulers(std::vector<Segment * > segments)229 ControlRulerWidget::launchNotationRulers(std::vector<Segment *> segments)
230 {
231 // For each Segment, get the ruler lists.
232 for (Segment *segment : segments) {
233 if (segment->notationRulers)
234 m_segmentRulerSets.push_back(segment->notationRulers);
235 }
236
237 launchRulers();
238 }
239
240 void
togglePropertyRuler(const PropertyName & propertyName)241 ControlRulerWidget::togglePropertyRuler(const PropertyName &propertyName)
242 {
243 // Toggle the *velocity* ruler. As of 2021 there is only one property
244 // ruler, the velocity ruler.
245
246 // For each ruler...
247 for (ControlRuler *ruler : m_controlRulerList) {
248 PropertyControlRuler *propruler =
249 dynamic_cast <PropertyControlRuler *> (ruler);
250 // Not a property ruler? Try the next one.
251 if (!propruler)
252 continue;
253
254 // Found it? Remove and bail.
255 if (propruler->getPropertyName() == propertyName)
256 {
257 removeRuler(ruler);
258 return;
259 }
260 }
261
262 // Not found, add it.
263 addPropertyRuler(propertyName);
264 }
265
266 namespace
267 {
hasPitchBend(Segment * segment)268 bool hasPitchBend(Segment *segment)
269 {
270 RosegardenDocument *document = RosegardenMainWindow::self()->getDocument();
271
272 Track *track =
273 document->getComposition().getTrackById(segment->getTrack());
274
275 Instrument *instrument = document->getStudio().
276 getInstrumentById(track->getInstrument());
277
278 if (!instrument)
279 return false;
280
281 Controllable *controllable = instrument->getDevice()->getControllable();
282
283 if (!controllable)
284 return false;
285
286 // Check whether the device has a pitchbend controller
287 // ??? Why not use
288 // Controllable::getControlParameter(type, controllerNumber)?
289 for (const ControlParameter &cp : controllable->getControlParameters()) {
290 if (cp.getType() == PitchBend::EventType)
291 return true;
292 }
293
294 return false;
295 }
296 }
297
298 void
togglePitchBendRuler()299 ControlRulerWidget::togglePitchBendRuler()
300 {
301 // No pitch bend? Bail.
302 // ??? Rude. We should gray the menu item instead of this.
303 if (!hasPitchBend(&(m_viewSegment->getSegment())))
304 return;
305
306 // Check whether we already have a pitchbend ruler
307
308 // For each ruler...
309 for (ControlRuler *ruler : m_controlRulerList) {
310 ControllerEventsRuler *eventRuler =
311 dynamic_cast<ControllerEventsRuler*>(ruler);
312
313 // Not a ControllerEventsRuler? Try the next one.
314 if (!eventRuler)
315 continue;
316
317 // If we already have a pitchbend ruler, remove it.
318 if (eventRuler->getControlParameter()->getType() ==
319 PitchBend::EventType)
320 {
321 removeRuler(ruler);
322 return;
323 }
324 }
325
326 // We don't already have a pitchbend ruler, make one now.
327 addControlRuler(ControlParameter::getPitchBend());
328 }
329
330 namespace
331 {
332 // Non-O-O approach. This could be pushed into ControlRuler
333 // and its derivers as getSegmentRuler().
getSegmentRuler(const ControlRuler * controlRuler)334 Segment::Ruler getSegmentRuler(const ControlRuler *controlRuler)
335 {
336 Segment::Ruler segmentRuler;
337
338 // Is it a PropertyControlRuler?
339 const PropertyControlRuler *pcr =
340 dynamic_cast<const PropertyControlRuler *>(controlRuler);
341 if (pcr) {
342 segmentRuler.type = BaseProperties::VELOCITY.getName();
343 return segmentRuler;
344 }
345
346 // Is it a ControllerEventsRuler?
347 const ControllerEventsRuler *cer =
348 dynamic_cast<const ControllerEventsRuler *>(controlRuler);
349 if (cer) {
350 const ControlParameter *cp = cer->getControlParameter();
351
352 // Handle pitchbend and CCs (and everything else).
353 segmentRuler.type = cp->getType();
354 if (cp->getType() == Controller::EventType)
355 segmentRuler.ccNumber = cp->getControllerNumber();
356 return segmentRuler;
357 }
358
359 return segmentRuler;
360 }
361 }
362
363 void
removeRuler(ControlRuler * ruler)364 ControlRulerWidget::removeRuler(ControlRuler *ruler)
365 {
366 // Remove from the stacked widget.
367 int index = m_stackedWidget->indexOf(ruler);
368 m_stackedWidget->removeWidget(ruler);
369
370 // Remove from the tabs.
371 m_tabBar->removeTab(index);
372
373 // Remove from the list.
374 m_controlRulerList.remove(ruler);
375
376 Segment::Ruler segmentRuler = getSegmentRuler(ruler);
377
378 // Remove from all Segment ruler sets.
379 for (std::shared_ptr<Segment::RulerSet> segmentRulerSet :
380 m_segmentRulerSets) {
381 segmentRulerSet->erase(segmentRuler);
382 }
383
384 // Close the ruler window.
385 delete ruler;
386 }
387
388 void
slotRemoveRuler(int index)389 ControlRulerWidget::slotRemoveRuler(int index)
390 {
391 ControlRuler *ruler =
392 dynamic_cast<ControlRuler *>(m_stackedWidget->widget(index));
393
394 removeRuler(ruler);
395 }
396
397 void
addRuler(ControlRuler * controlRuler,QString name)398 ControlRulerWidget::addRuler(ControlRuler *controlRuler, QString name)
399 {
400 // Add to the stacked widget.
401 m_stackedWidget->addWidget(controlRuler);
402
403 // Add to tabs.
404 // (Controller names, if translatable, come from AutoLoadStrings.cpp and are
405 // in the QObject context/namespace/whatever.)
406 const int index = m_tabBar->addTab(QObject::tr(name.toStdString().c_str()));
407 m_tabBar->setCurrentIndex(index);
408
409 // Add to ruler list.
410 m_controlRulerList.push_back(controlRuler);
411
412 // Configure the ruler.
413 controlRuler->slotSetPannedRect(m_pannedRect);
414 slotSetTool(m_currentToolName);
415
416 Segment::Ruler segmentRuler = getSegmentRuler(controlRuler);
417
418 // Add to all Segment ruler sets.
419 for (std::shared_ptr<Segment::RulerSet> segmentRulerSet :
420 m_segmentRulerSets) {
421 segmentRulerSet->insert(segmentRuler);
422 }
423
424 }
425
426 void
addControlRuler(const ControlParameter & controlParameter)427 ControlRulerWidget::addControlRuler(const ControlParameter &controlParameter)
428 {
429 // If we're not editing a ViewSegment, bail.
430 if (!m_viewSegment)
431 return;
432
433 ControlRuler *controlRuler = new ControllerEventsRuler(
434 m_viewSegment, m_scale, this, &controlParameter);
435
436 controlRuler->setXOffset(m_leftMargin);
437
438 // Mouse signals. Forward them from the current ControlRuler.
439 connect(controlRuler, &ControlRuler::mousePress,
440 this, &ControlRulerWidget::mousePress);
441 connect(controlRuler, &ControlRuler::mouseMove,
442 this, &ControlRulerWidget::mouseMove);
443 connect(controlRuler, &ControlRuler::mouseRelease,
444 this, &ControlRulerWidget::mouseRelease);
445
446 connect(controlRuler, &ControlRuler::rulerSelectionChanged,
447 this, &ControlRulerWidget::slotChildRulerSelectionChanged);
448
449 addRuler(controlRuler, QString::fromStdString(controlParameter.getName()));
450
451 // ??? This is required or else we crash. But we already passed this in
452 // in the ctor call. Can we fix this so that we only pass it once?
453 // Preferably in the ctor call. PropertyControlRuler appears to do
454 // this successfully. See if we can follow its example.
455 controlRuler->setViewSegment(m_viewSegment);
456 }
457
458 void
addPropertyRuler(const PropertyName & propertyName)459 ControlRulerWidget::addPropertyRuler(const PropertyName &propertyName)
460 {
461 // Note that as of 2021 there is only one property ruler, the
462 // velocity ruler.
463
464 // If we're not editing a ViewSegment, bail.
465 if (!m_viewSegment)
466 return;
467
468 PropertyControlRuler *controlRuler = new PropertyControlRuler(
469 propertyName,
470 m_viewSegment, // viewSegment
471 m_scale, // scale
472 this); // parent
473
474 // ??? The velocity ruler does not yet support selection, so this
475 // actually does nothing right now.
476 connect(controlRuler, &ControlRuler::rulerSelectionChanged,
477 this, &ControlRulerWidget::slotChildRulerSelectionChanged);
478
479 connect(controlRuler, &ControlRuler::showContextHelp,
480 this, &ControlRulerWidget::showContextHelp);
481
482 controlRuler->setXOffset(m_leftMargin);
483 controlRuler->updateSelection(m_selectedElements);
484
485 // Little kludge here. We only have the one property ruler (velocity),
486 // and the string "velocity" wasn't already in a context (any context)
487 // where it could be translated, and "velocity" doesn't look good with
488 // "PitchBend" or "Reverb", so we ask for an explicit tr() here.
489 QString name = QString::fromStdString(propertyName.getName());
490 if (name == "velocity")
491 name = tr("Velocity");
492
493 addRuler(controlRuler, name);
494
495 // Update selection drawing in matrix view.
496 emit childRulerSelectionChanged(nullptr);
497 }
498
499 void
slotSetPannedRect(QRectF pannedRect)500 ControlRulerWidget::slotSetPannedRect(QRectF pannedRect)
501 {
502 m_pannedRect = pannedRect;
503
504 // For each ruler, pass on the panned rect.
505 for (ControlRuler *ruler : m_controlRulerList) {
506 ruler->slotSetPannedRect(pannedRect);
507 }
508
509 update();
510 }
511
512 void
slotSelectionChanged(EventSelection * eventSelection)513 ControlRulerWidget::slotSelectionChanged(EventSelection *eventSelection)
514 {
515 m_selectedElements.clear();
516
517 if (eventSelection) {
518 // Convert the EventSelection into a vector of ViewElement *.
519
520 // For each event in the new EventSelection...
521 for (Event *event : eventSelection->getSegmentEvents()) {
522 // Find the corresponding ViewElement.
523 // TODO check if this code is necessary for some reason
524 // It seems there abundant work done here
525 // ??? Performance: Search within for loop.
526 ViewElementList::iterator viewElementIter =
527 m_viewSegment->findEvent(event);
528 // Add it to m_selectedElements.
529 m_selectedElements.push_back(*viewElementIter);
530 }
531 }
532
533 // Send new selection to all PropertyControlRulers. IOW the velocity ruler.
534 // For each ruler...
535 for (ControlRuler *ruler : m_controlRulerList) {
536 PropertyControlRuler *propertyRuler =
537 dynamic_cast<PropertyControlRuler *>(ruler);
538 // Is this the velocity ruler? Then pass on the selection.
539 if (propertyRuler)
540 propertyRuler->updateSelection(m_selectedElements);
541 }
542 }
543
544 void
slotHoveredOverNoteChanged(int,bool,timeT)545 ControlRulerWidget::slotHoveredOverNoteChanged(int /* evPitch */, bool /* haveEvent */, timeT /* evTime */)
546 {
547 // ??? Since all parameters are unused, can we change the signal we
548 // connect to (MatrixMover::hoveredOverNoteChanged) to have no
549 // parameters? I think we can.
550
551 // RG_DEBUG << "slotHoveredOverNoteChanged()";
552
553 // ??? What does this routine even do. At first I thought it made sure
554 // that the velocity bars would move as the notes are dragged around
555 // on the matrix. But it does not. And that makes sense since the
556 // dragging is a temporary change to the view that doesn't change
557 // the underlying Segment until the mouse is released. So we
558 // wouldn't expect to see the velocity bars moving around during
559 // the drag.
560 //
561 // I've removed the code below for now to see if anyone notices.
562
563 #if 0
564 // ??? This code doesn't appear to do anything. Removing to see if
565 // anyone notices.
566 if (m_controlRulerList.size()) {
567 ControlRulerList::iterator it;
568 for (it = m_controlRulerList.begin(); it != m_controlRulerList.end(); ++it) {
569 PropertyControlRuler *pr = dynamic_cast <PropertyControlRuler *> (*it);
570 if (pr) pr->updateSelectedItems();
571 }
572 }
573 #endif
574 }
575
576 void
slotUpdateRulers(timeT startTime,timeT endTime)577 ControlRulerWidget::slotUpdateRulers(timeT startTime, timeT endTime)
578 {
579 // For each ruler, ask for an update.
580 for (ControlRuler *ruler : m_controlRulerList) {
581 ruler->notationLayoutUpdated(startTime, endTime);
582 }
583 }
584
585 void
slotSetTool(const QString & toolName)586 ControlRulerWidget::slotSetTool(const QString &toolName)
587 {
588 QString rulerToolName = toolName;
589
590 // Translate Notation tool names to ruler tool names.
591 if (toolName == "notationselector")
592 rulerToolName = "selector";
593 if (toolName == "notationselectornoties")
594 rulerToolName = "selector";
595 if (toolName == "noterestinserter")
596 rulerToolName = "painter";
597 if (toolName == "notationeraser")
598 rulerToolName = "eraser";
599
600 m_currentToolName = rulerToolName;
601
602 // Dispatch to all rulers.
603 for (ControlRuler *ruler : m_controlRulerList) {
604 ruler->setTool(rulerToolName);
605 }
606 }
607
608 void
slotChildRulerSelectionChanged(EventSelection * s)609 ControlRulerWidget::slotChildRulerSelectionChanged(EventSelection *s)
610 {
611 emit childRulerSelectionChanged(s);
612 }
613
614 bool
isAnyRulerVisible()615 ControlRulerWidget::isAnyRulerVisible()
616 {
617 return !m_controlRulerList.empty();
618 }
619
620 ControllerEventsRuler *
getActiveRuler()621 ControlRulerWidget::getActiveRuler()
622 {
623 return dynamic_cast <ControllerEventsRuler *>(
624 m_stackedWidget->currentWidget());
625 }
626
627 PropertyControlRuler *
getActivePropertyRuler()628 ControlRulerWidget::getActivePropertyRuler()
629 {
630 return dynamic_cast <PropertyControlRuler *>(
631 m_stackedWidget->currentWidget());
632 }
633
634 bool
hasSelection()635 ControlRulerWidget::hasSelection()
636 {
637 ControllerEventsRuler *ruler = getActiveRuler();
638 if (!ruler)
639 return false;
640
641 return (ruler->getEventSelection() != nullptr);
642 }
643
644 EventSelection *
getSelection()645 ControlRulerWidget::getSelection()
646 {
647 ControllerEventsRuler *ruler = getActiveRuler();
648 if (!ruler)
649 return nullptr;
650
651 return ruler->getEventSelection();
652 }
653
654 ControlParameter *
getControlParameter()655 ControlRulerWidget::getControlParameter()
656 {
657 ControllerEventsRuler *ruler = getActiveRuler();
658 if (!ruler)
659 return nullptr;
660
661 return ruler->getControlParameter();
662 }
663
664 SelectionSituation *
getSituation()665 ControlRulerWidget::getSituation()
666 {
667 ControllerEventsRuler *ruler = getActiveRuler();
668 if (!ruler)
669 return nullptr;
670
671 EventSelection *selection = ruler->getEventSelection();
672 if (!selection)
673 return nullptr;
674
675 ControlParameter *cp = ruler->getControlParameter();
676 if (!cp)
677 return nullptr;
678
679 return new SelectionSituation(cp->getType(), selection);
680 }
681
682
683 }
684