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 matrix 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 "[MatrixView]"
19 
20 #include "MatrixView.h"
21 
22 #include "MatrixCommandRegistry.h"
23 #include "MatrixWidget.h"
24 #include "MatrixElement.h"
25 #include "MatrixViewSegment.h"
26 #include "MatrixScene.h"
27 #include "PianoKeyboard.h"
28 
29 #include "misc/Debug.h"
30 #include "misc/Strings.h"
31 
32 #include "misc/ConfigGroups.h"
33 #include "document/RosegardenDocument.h"
34 #include "document/CommandHistory.h"
35 
36 #include "gui/dialogs/AboutDialog.h"
37 #include "gui/dialogs/QuantizeDialog.h"
38 #include "gui/dialogs/EventFilterDialog.h"
39 #include "gui/dialogs/EventParameterDialog.h"
40 #include "gui/dialogs/TriggerSegmentDialog.h"
41 #include "gui/dialogs/PitchBendSequenceDialog.h"
42 #include "gui/dialogs/KeySignatureDialog.h"
43 
44 #include "commands/edit/ChangeVelocityCommand.h"
45 #include "commands/edit/ClearTriggersCommand.h"
46 #include "commands/edit/CollapseNotesCommand.h"
47 #include "commands/edit/CopyCommand.h"
48 #include "commands/edit/CutCommand.h"
49 #include "commands/edit/EraseCommand.h"
50 #include "commands/edit/EventQuantizeCommand.h"
51 #include "commands/edit/EventUnquantizeCommand.h"
52 #include "commands/edit/PasteEventsCommand.h"
53 #include "commands/edit/SelectionPropertyCommand.h"
54 #include "commands/edit/SetTriggerCommand.h"
55 
56 #include "commands/edit/InvertCommand.h"
57 #include "commands/edit/MoveCommand.h"
58 #include "commands/edit/PlaceControllersCommand.h"
59 #include "commands/edit/RescaleCommand.h"
60 #include "commands/edit/RetrogradeCommand.h"
61 #include "commands/edit/RetrogradeInvertCommand.h"
62 #include "commands/edit/TransposeCommand.h"
63 
64 #include "commands/segment/AddTempoChangeCommand.h"
65 #include "commands/segment/AddTimeSignatureAndNormalizeCommand.h"
66 #include "commands/segment/AddTimeSignatureCommand.h"
67 
68 #include "commands/matrix/MatrixInsertionCommand.h"
69 
70 #include "commands/notation/KeyInsertionCommand.h"
71 #include "commands/notation/MultiKeyInsertionCommand.h"
72 
73 #include "gui/editors/notation/NotationStrings.h"
74 #include "gui/editors/notation/NotePixmapFactory.h"
75 
76 #include "gui/rulers/ControlRulerWidget.h"
77 
78 #include "gui/general/ThornStyle.h"
79 
80 #include "base/Quantizer.h"
81 #include "base/BasicQuantizer.h"
82 #include "base/LegatoQuantizer.h"
83 #include "base/BaseProperties.h"
84 #include "base/SnapGrid.h"
85 #include "base/Clipboard.h"
86 #include "base/AnalysisTypes.h"
87 #include "base/CompositionTimeSliceAdapter.h"
88 #include "base/NotationTypes.h"
89 #include "base/Controllable.h"
90 #include "base/Studio.h"
91 #include "base/Instrument.h"
92 #include "base/Device.h"
93 #include "base/MidiDevice.h"
94 #include "base/SoftSynthDevice.h"
95 #include "base/MidiTypes.h"
96 #include "base/parameterpattern/ParameterPattern.h"
97 
98 #include "gui/dialogs/RescaleDialog.h"
99 #include "gui/dialogs/TempoDialog.h"
100 #include "gui/dialogs/IntervalDialog.h"
101 #include "gui/dialogs/TimeSignatureDialog.h"
102 
103 #include "gui/general/IconLoader.h"
104 
105 #include <QWidget>
106 #include <QAction>
107 #include <QActionGroup>
108 #include <QMenu>
109 #include <QLabel>
110 #include <QToolBar>
111 #include <QSettings>
112 #include <QComboBox>
113 #include <QHBoxLayout>
114 #include <QInputDialog>
115 #include <QMessageBox>
116 #include <QToolButton>
117 #include <QStatusBar>
118 #include <QDesktopServices>
119 
120 
121 namespace Rosegarden
122 {
123 
MatrixView(RosegardenDocument * doc,std::vector<Segment * > segments,bool drumMode,QWidget * parent)124 MatrixView::MatrixView(RosegardenDocument *doc,
125                  std::vector<Segment *> segments,
126                  bool drumMode,
127                  QWidget *parent) :
128     EditViewBase(doc, segments, parent),
129     m_tracking(true),
130     m_quantizations(BasicQuantizer::getStandardQuantizations()),
131     m_drumMode(drumMode),
132     m_inChordMode(false)
133 {
134     m_document = doc;
135     m_matrixWidget = new MatrixWidget(m_drumMode);
136     setCentralWidget(m_matrixWidget);
137     m_matrixWidget->setSegments(doc, segments);
138 
139     // Many actions are created here
140     // ??? MEMORY LEAK (confirmed)
141     m_commandRegistry = new MatrixCommandRegistry(this);
142 
143     setupActions();
144 
145     createMenusAndToolbars("matrix.rc");
146 
147     findToolbar("General Toolbar");
148 
149     m_Thorn = ThornStyle::isEnabled();
150 
151     initActionsToolbar();
152     initRulersToolbar();
153     initStatusBar();
154 
155     connect(m_matrixWidget, &MatrixWidget::editTriggerSegment,
156             this, &MatrixView::editTriggerSegment);
157 
158     connect(m_matrixWidget, &MatrixWidget::showContextHelp,
159             this, &MatrixView::slotShowContextHelp);
160 
161     slotUpdateMenuStates();
162     slotTestClipboard();
163 
164     connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
165             this, SLOT(slotUpdateMenuStates()));
166 
167     connect(m_matrixWidget, &MatrixWidget::selectionChanged,
168             this, &MatrixView::slotUpdateMenuStates);
169 
170     // Toggle the desired tool off and then trigger it on again, to
171     // make sure its signal is called at least once (as would not
172     // happen if the tool was on by default otherwise)
173     QAction *toolAction = nullptr;
174     if (!m_matrixWidget->segmentsContainNotes()) {
175         toolAction = findAction("draw");
176     } else {
177         toolAction = findAction("select");
178     }
179     if (toolAction) {
180         MATRIX_DEBUG << "initial state for action '" << toolAction->objectName() << "' is " << toolAction->isChecked();
181         if (toolAction->isChecked()) toolAction->toggle();
182         MATRIX_DEBUG << "newer state for action '" << toolAction->objectName() << "' is " << toolAction->isChecked();
183         toolAction->trigger();
184         MATRIX_DEBUG << "newest state for action '" << toolAction->objectName() << "' is " << toolAction->isChecked();
185     }
186 
187     m_matrixWidget->setScrollToFollowPlayback(m_tracking);
188 
189     slotUpdateWindowTitle();
190     connect(m_document, &RosegardenDocument::documentModified,
191             this, &MatrixView::slotUpdateWindowTitle);
192 
193     // Set initial visibility ...
194     bool view;
195 
196     QSettings settings;
197     settings.beginGroup(MatrixViewConfigGroup);
198 
199     // ... of chord name ruler ...
200     view = settings.value("Chords ruler shown",
201                           findAction("show_chords_ruler")->isChecked()
202                          ).toBool();
203     findAction("show_chords_ruler")->setChecked(view);
204     m_matrixWidget->setChordNameRulerVisible(view);
205 
206     // ... and tempo ruler
207     view = settings.value("Tempo ruler shown",
208                           findAction("show_tempo_ruler")->isChecked()
209                          ).toBool();
210     findAction("show_tempo_ruler")->setChecked(view);
211     m_matrixWidget->setTempoRulerVisible(view);
212 
213     settings.endGroup();
214 
215     if (segments.size() > 1) {
216         enterActionState("have_multiple_segments");
217     } else {
218         leaveActionState("have_multiple_segments");
219     }
220 
221     if (m_drumMode) {
222         enterActionState("in_percussion_matrix");
223     } else {
224         enterActionState("in_standard_matrix");
225     }
226 
227     // Restore window geometry and toolbar/dock state
228     settings.beginGroup(WindowGeometryConfigGroup);
229     QString modeStr = (m_drumMode ? "Percussion_Matrix_View_Geometry" : "Matrix_View_Geometry");
230     this->restoreGeometry(settings.value(modeStr).toByteArray());
231     modeStr = (m_drumMode ? "Percussion_Matrix_View_State" : "Matrix_View_State");
232     this->restoreState(settings.value(modeStr).toByteArray());
233     settings.endGroup();
234 
235     connect(m_matrixWidget, SIGNAL(segmentDeleted(Segment *)),
236             this, SLOT(slotSegmentDeleted(Segment *)));
237     connect(m_matrixWidget, &MatrixWidget::sceneDeleted,
238             this, &MatrixView::slotSceneDeleted);
239 
240     connect(this, &MatrixView::noteInsertedFromKeyboard,
241             m_matrixWidget, &MatrixWidget::slotPlayPreviewNote);
242 
243     // Set the rewind and fast-forward buttons for auto-repeat.
244     enableAutoRepeat("Transport Toolbar", "playback_pointer_back_bar");
245     enableAutoRepeat("Transport Toolbar", "playback_pointer_forward_bar");
246     enableAutoRepeat("Transport Toolbar", "cursor_back");
247     enableAutoRepeat("Transport Toolbar", "cursor_forward");
248 
249     // Show the pointer as soon as matrix editor opens (update pointer position,
250     // but don't scroll)
251     m_matrixWidget->showInitialPointer();
252 
253     readOptions();
254 }
255 
~MatrixView()256 MatrixView::~MatrixView()
257 {
258     MATRIX_DEBUG << "MatrixView::~MatrixView()";
259 }
260 
261 void
closeEvent(QCloseEvent * event)262 MatrixView::closeEvent(QCloseEvent *event)
263 {
264     // Save window geometry and toolbar/dock state
265     QSettings settings;
266     settings.beginGroup(WindowGeometryConfigGroup);
267     QString modeStr = (m_drumMode ? "Percussion_Matrix_View_Geometry" : "Matrix_View_Geometry");
268     settings.setValue(modeStr, this->saveGeometry());
269     modeStr = (m_drumMode ? "Percussion_Matrix_View_State" : "Matrix_View_State");
270     settings.setValue(modeStr, this->saveState());
271     settings.endGroup();
272 
273     QWidget::closeEvent(event);
274 }
275 
276 void
slotSegmentDeleted(Segment * s)277 MatrixView::slotSegmentDeleted(Segment *s)
278 {
279     MATRIX_DEBUG << "MatrixView::slotSegmentDeleted: " << s;
280 
281     // remove from vector
282     for (std::vector<Segment *>::iterator i = m_segments.begin();
283          i != m_segments.end(); ++i) {
284         if (*i == s) {
285             m_segments.erase(i);
286             NOTATION_DEBUG << "MatrixView::slotSegmentDeleted: Erased segment from vector, have " << m_segments.size() << " segment(s) remaining";
287             return;
288         }
289     }
290 }
291 
292 void
slotSceneDeleted()293 MatrixView::slotSceneDeleted()
294 {
295     NOTATION_DEBUG << "MatrixView::slotSceneDeleted";
296 
297     m_segments.clear();
298     close();
299 }
300 
301 void
slotUpdateWindowTitle(bool)302 MatrixView::slotUpdateWindowTitle(bool)
303 {
304     // Set client label
305     //
306     QString view = tr("Matrix");
307     //&&&if (isDrumMode())
308     //    view = tr("Percussion");
309 
310     if (m_segments.empty()) return;
311 
312     setWindowTitle(getTitle(view));
313 
314     setWindowIcon(IconLoader::loadPixmap("window-matrix"));
315 }
316 
317 void
setupActions()318 MatrixView::setupActions()
319 {
320 
321     setupBaseActions(true);
322 
323     createAction("select", SLOT(slotSetSelectTool()));
324     createAction("draw", SLOT(slotSetPaintTool()));
325     createAction("erase", SLOT(slotSetEraseTool()));
326     createAction("move", SLOT(slotSetMoveTool()));
327     createAction("resize", SLOT(slotSetResizeTool()));
328     createAction("velocity", SLOT(slotSetVelocityTool()));
329     createAction("chord_mode", SLOT(slotToggleChordMode()));
330     createAction("toggle_step_by_step", SLOT(slotToggleStepByStep()));
331     createAction("quantize", SLOT(slotQuantize()));
332     createAction("repeat_quantize", SLOT(slotRepeatQuantize()));
333     createAction("collapse_notes", SLOT(slotCollapseNotes()));
334     createAction("legatoize", SLOT(slotLegato()));
335     createAction("velocity_up", SLOT(slotVelocityUp()));
336     createAction("velocity_down", SLOT(slotVelocityDown()));
337     createAction("set_to_current_velocity", SLOT(slotSetVelocitiesToCurrent()));
338     createAction("set_velocities", SLOT(slotSetVelocities()));
339     createAction("trigger_segment", SLOT(slotTriggerSegment()));
340     createAction("remove_trigger", SLOT(slotRemoveTriggers()));
341     createAction("select_all", SLOT(slotSelectAll()));
342     createAction("delete", SLOT(slotEditDelete()));
343     createAction("cursor_back", SLOT(slotStepBackward()));
344     createAction("cursor_forward", SLOT(slotStepForward()));
345     createAction("extend_selection_backward", SLOT(slotExtendSelectionBackward()));
346     createAction("extend_selection_forward", SLOT(slotExtendSelectionForward()));
347     createAction("extend_selection_backward_bar", SLOT(slotExtendSelectionBackwardBar()));
348     createAction("extend_selection_forward_bar", SLOT(slotExtendSelectionForwardBar()));
349     //&&& NB Play has two shortcuts (Enter and Ctrl+Return) -- need to
350     // ensure both get carried across somehow
351     createAction("play", SIGNAL(play()));
352     createAction("stop", SIGNAL(stop()));
353     createAction("playback_pointer_back_bar", SIGNAL(rewindPlayback()));
354     createAction("playback_pointer_forward_bar", SIGNAL(fastForwardPlayback()));
355     createAction("playback_pointer_start", SIGNAL(rewindPlaybackToBeginning()));
356     createAction("playback_pointer_end", SIGNAL(fastForwardPlaybackToEnd()));
357     createAction("cursor_prior_segment", SLOT(slotCurrentSegmentPrior()));
358     createAction("cursor_next_segment", SLOT(slotCurrentSegmentNext()));
359     createAction("toggle_solo", SLOT(slotToggleSolo()));
360     createAction("toggle_tracking", SLOT(slotToggleTracking()));
361     createAction("panic", SIGNAL(panic()));
362     createAction("preview_selection", SLOT(slotPreviewSelection()));
363     createAction("clear_loop", SLOT(slotClearLoop()));
364     createAction("clear_selection", SLOT(slotClearSelection()));
365     createAction("filter_selection", SLOT(slotFilterSelection()));
366 
367     createAction("pitch_bend_sequence", SLOT(slotPitchBendSequence()));
368 
369     //"controllers" Menubar menu
370     createAction("controller_sequence", SLOT(slotControllerSequence()));
371     createAction("copy_controllers",  SLOT(slotEditCopyControllers()));
372     createAction("cut_controllers",   SLOT(slotEditCutControllers()));
373     createAction("set_controllers",   SLOT(slotSetControllers()));
374     createAction("place_controllers", SLOT(slotPlaceControllers()));
375 
376     createAction("show_chords_ruler", SLOT(slotToggleChordsRuler()));
377     createAction("show_tempo_ruler", SLOT(slotToggleTempoRuler()));
378 
379     createAction("toggle_velocity_ruler", SLOT(slotToggleVelocityRuler()));
380     createAction("toggle_pitchbend_ruler", SLOT(slotTogglePitchbendRuler()));
381     createAction("add_control_ruler", "");
382 
383     createAction("add_tempo_change", SLOT(slotAddTempo()));
384     createAction("add_time_signature", SLOT(slotAddTimeSignature()));
385     createAction("add_key_signature", SLOT(slotEditAddKeySignature()));
386 
387     createAction("halve_durations", SLOT(slotHalveDurations()));
388     createAction("double_durations", SLOT(slotDoubleDurations()));
389     createAction("rescale", SLOT(slotRescale()));
390     createAction("transpose_up", SLOT(slotTransposeUp()));
391     createAction("transpose_up_octave", SLOT(slotTransposeUpOctave()));
392     createAction("transpose_down", SLOT(slotTransposeDown()));
393     createAction("transpose_down_octave", SLOT(slotTransposeDownOctave()));
394     createAction("general_transpose", SLOT(slotTranspose()));
395     createAction("general_diatonic_transpose", SLOT(slotDiatonicTranspose()));
396     createAction("invert", SLOT(slotInvert()));
397     createAction("retrograde", SLOT(slotRetrograde()));
398     createAction("retrograde_invert", SLOT(slotRetrogradeInvert()));
399     createAction("jog_left", SLOT(slotJogLeft()));
400     createAction("jog_right", SLOT(slotJogRight()));
401 
402     QMenu *addControlRulerMenu = new QMenu;
403     Controllable *c =
404         dynamic_cast<MidiDevice *>(getCurrentDevice());
405     if (!c) {
406         c = dynamic_cast<SoftSynthDevice *>(getCurrentDevice());
407     }
408 
409     if (c) {
410 
411         const ControlList &list = c->getControlParameters();
412 
413         QString itemStr;
414 
415         for (ControlList::const_iterator it = list.begin();
416              it != list.end(); ++it) {
417 
418             // Pitch Bend is treated separately now, and there's no
419             // point in adding "unsupported" controllers to the menu,
420             // so skip everything else
421             if (it->getType() != Controller::EventType) continue;
422 
423             const QString hexValue =
424                 QString::asprintf("(0x%x)", it->getControllerNumber());
425 
426             // strings extracted from data files must be QObject::tr()
427             itemStr = QObject::tr("%1 Controller %2 %3")
428                 .arg(QObject::tr(it->getName().c_str()))
429                 .arg(it->getControllerNumber())
430                 .arg(hexValue);
431 
432             addControlRulerMenu->addAction(itemStr);
433         }
434     }
435 
436     connect(addControlRulerMenu, &QMenu::triggered,
437             this, &MatrixView::slotAddControlRuler);
438 
439     findAction("add_control_ruler")->setMenu(addControlRulerMenu);
440 
441     // (ported from NotationView)
442     //JAS insert note section is a rewrite
443     //JAS from EditView::createInsertPitchActionMenu()
444     for (int octave = 0; octave <= 2; ++octave) {
445         QString octaveSuffix;
446         if (octave == 1) octaveSuffix = "_high";
447         else if (octave == 2) octaveSuffix = "_low";
448 
449         createAction(QString("insert_0%1").arg(octaveSuffix),
450                      SLOT(slotInsertNoteFromAction()));
451         createAction(QString("insert_0_sharp%1").arg(octaveSuffix),
452                      SLOT(slotInsertNoteFromAction()));
453         createAction(QString("insert_1_flat%1").arg(octaveSuffix),
454                      SLOT(slotInsertNoteFromAction()));
455         createAction(QString("insert_1%1").arg(octaveSuffix),
456                      SLOT(slotInsertNoteFromAction()));
457         createAction(QString("insert_1_sharp%1").arg(octaveSuffix),
458                      SLOT(slotInsertNoteFromAction()));
459         createAction(QString("insert_2_flat%1").arg(octaveSuffix),
460                      SLOT(slotInsertNoteFromAction()));
461         createAction(QString("insert_2%1").arg(octaveSuffix),
462                      SLOT(slotInsertNoteFromAction()));
463         createAction(QString("insert_3%1").arg(octaveSuffix),
464                      SLOT(slotInsertNoteFromAction()));
465         createAction(QString("insert_3_sharp%1").arg(octaveSuffix),
466                      SLOT(slotInsertNoteFromAction()));
467         createAction(QString("insert_4_flat%1").arg(octaveSuffix),
468                      SLOT(slotInsertNoteFromAction()));
469         createAction(QString("insert_4%1").arg(octaveSuffix),
470                      SLOT(slotInsertNoteFromAction()));
471         createAction(QString("insert_4_sharp%1").arg(octaveSuffix),
472                      SLOT(slotInsertNoteFromAction()));
473         createAction(QString("insert_5_flat%1").arg(octaveSuffix),
474                      SLOT(slotInsertNoteFromAction()));
475         createAction(QString("insert_5%1").arg(octaveSuffix),
476                      SLOT(slotInsertNoteFromAction()));
477         createAction(QString("insert_5_sharp%1").arg(octaveSuffix),
478                      SLOT(slotInsertNoteFromAction()));
479         createAction(QString("insert_6_flat%1").arg(octaveSuffix),
480                      SLOT(slotInsertNoteFromAction()));
481         createAction(QString("insert_6%1").arg(octaveSuffix),
482                      SLOT(slotInsertNoteFromAction()));
483     }
484 
485     createAction("options_show_toolbar", SLOT(slotToggleGeneralToolBar()));
486     createAction("show_tools_toolbar", SLOT(slotToggleToolsToolBar()));
487     createAction("show_transport_toolbar", SLOT(slotToggleTransportToolBar()));
488     createAction("show_actions_toolbar", SLOT(slotToggleActionsToolBar()));
489     createAction("show_rulers_toolbar", SLOT(slotToggleRulersToolBar()));
490 
491 
492     createAction("manual", SLOT(slotHelp()));
493     createAction("tutorial", SLOT(slotTutorial()));
494     createAction("guidelines", SLOT(slotBugGuidelines()));
495     createAction("help_about_app", SLOT(slotHelpAbout()));
496     createAction("help_about_qt", SLOT(slotHelpAboutQt()));
497     createAction("donate", SLOT(slotDonate()));
498 
499     // grid snap values
500     timeT crotchetDuration = Note(Note::Crotchet).getDuration();
501     m_snapValues.clear();
502     m_snapValues.push_back(SnapGrid::NoSnap);
503     m_snapValues.push_back(SnapGrid::SnapToUnit);
504     m_snapValues.push_back(crotchetDuration / 16);
505     m_snapValues.push_back(crotchetDuration / 12);
506     m_snapValues.push_back(crotchetDuration / 8);
507     m_snapValues.push_back(crotchetDuration / 6);
508     m_snapValues.push_back(crotchetDuration / 4);
509     m_snapValues.push_back(crotchetDuration / 3);
510     m_snapValues.push_back(crotchetDuration / 2);
511     m_snapValues.push_back((crotchetDuration * 3) / 4);
512     m_snapValues.push_back(crotchetDuration);
513     m_snapValues.push_back((crotchetDuration * 3) / 2);
514     m_snapValues.push_back(crotchetDuration * 2);
515     m_snapValues.push_back(SnapGrid::SnapToBeat);
516     m_snapValues.push_back(SnapGrid::SnapToBar);
517 
518     for (unsigned int i = 0; i < m_snapValues.size(); i++) {
519 
520         timeT d = m_snapValues[i];
521 
522         if (d == SnapGrid::NoSnap) {
523             createAction("snap_none", SLOT(slotSetSnapFromAction()));
524         } else if (d == SnapGrid::SnapToUnit) {
525         } else if (d == SnapGrid::SnapToBeat) {
526             createAction("snap_beat", SLOT(slotSetSnapFromAction()));
527         } else if (d == SnapGrid::SnapToBar) {
528             createAction("snap_bar", SLOT(slotSetSnapFromAction()));
529         } else {
530             QString actionName = QString("snap_%1").arg(int((crotchetDuration * 4) / d));
531             if (d == (crotchetDuration * 3) / 4) actionName = "snap_dotted_8";
532             if (d == (crotchetDuration * 3) / 2) actionName = "snap_dotted_4";
533             createAction(actionName, SLOT(slotSetSnapFromAction()));
534         }
535     }
536 }
537 
538 
539 void
initActionsToolbar()540 MatrixView::initActionsToolbar()
541 {
542     MATRIX_DEBUG << "MatrixView::initActionsToolbar";
543 
544     QToolBar *actionsToolbar = findToolbar("Actions Toolbar");
545 //    QToolBar *actionsToolbar = m_actionsToolBar;
546     //actionsToolbar->setLayout(new QHBoxLayout(actionsToolbar));
547 
548     if (!actionsToolbar) {
549         MATRIX_DEBUG << "MatrixView::initActionsToolbar - "
550         << "tool bar not found";
551         return ;
552     }
553 
554     // The SnapGrid combo and Snap To... menu items
555     //
556     QLabel *sLabel = new QLabel(tr(" Grid: "), actionsToolbar);
557     sLabel->setIndent(10);
558     actionsToolbar->addWidget(sLabel);
559 
560     QPixmap noMap = NotePixmapFactory::makeToolbarPixmap("menu-no-note");
561 
562     m_snapGridCombo = new QComboBox(actionsToolbar);
563     actionsToolbar->addWidget(m_snapGridCombo);
564 
565     for (unsigned int i = 0; i < m_snapValues.size(); i++) {
566 
567         timeT d = m_snapValues[i];
568 
569         if (d == SnapGrid::NoSnap) {
570             m_snapGridCombo->addItem(tr("None"));
571         } else if (d == SnapGrid::SnapToUnit) {
572             m_snapGridCombo->addItem(tr("Unit"));
573         } else if (d == SnapGrid::SnapToBeat) {
574             m_snapGridCombo->addItem(tr("Beat"));
575         } else if (d == SnapGrid::SnapToBar) {
576             m_snapGridCombo->addItem(tr("Bar"));
577         } else {
578             timeT err = 0;
579             QString label = NotationStrings::makeNoteMenuLabel(d, true, err);
580             QPixmap pixmap = NotePixmapFactory::makeNoteMenuPixmap(d, err);
581             m_snapGridCombo->addItem((err ? noMap : pixmap), label);
582         }
583 
584         if (getSnapGrid() && d == getSnapGrid()->getSnapSetting()) {
585             m_snapGridCombo->setCurrentIndex(m_snapGridCombo->count() - 1);
586         }
587     }
588 
589     connect(m_snapGridCombo, SIGNAL(activated(int)),
590             this, SLOT(slotSetSnapFromIndex(int)));
591 
592     // Velocity combo.  Not a spin box, because the spin box is too
593     // slow to use unless we make it typeable into, and then it takes
594     // focus away from our more important widgets
595 
596     QLabel *vlabel = new QLabel(tr(" Velocity: "), actionsToolbar);
597     vlabel->setIndent(10);
598     actionsToolbar->addWidget(vlabel);
599 
600     m_velocityCombo = new QComboBox(actionsToolbar);
601     actionsToolbar->addWidget(m_velocityCombo);
602 
603     for (int i = 0; i <= 127; ++i) {
604         m_velocityCombo->addItem(QString("%1").arg(i));
605     }
606     m_velocityCombo->setCurrentIndex(100); //!!! associate with segment
607     connect(m_velocityCombo, SIGNAL(activated(int)),
608             m_matrixWidget, SLOT(slotSetCurrentVelocity(int)));
609 
610     // Quantize combo
611     //
612     QLabel *qLabel = new QLabel(tr(" Quantize: "), actionsToolbar);
613     qLabel->setIndent(10);
614     actionsToolbar->addWidget(qLabel);
615 
616     m_quantizeCombo = new QComboBox(actionsToolbar);
617     actionsToolbar->addWidget(m_quantizeCombo);
618 
619     for (unsigned int i = 0; i < m_quantizations.size(); ++i) {
620 
621         timeT time = m_quantizations[i];
622         timeT error = 0;
623         QString label = NotationStrings::makeNoteMenuLabel(time, true, error);
624         QPixmap pmap = NotePixmapFactory::makeNoteMenuPixmap(time, error);
625         m_quantizeCombo->addItem(error ? noMap : pmap, label);
626     }
627 
628     m_quantizeCombo->addItem(noMap, tr("Off"));
629 
630     // default to Off to mirror Classic behavior
631     m_quantizeCombo->setCurrentIndex(m_quantizeCombo->count() - 1);
632 
633     m_quantizeCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
634 
635     connect(m_quantizeCombo, SIGNAL(activated(int)),
636             this, SLOT(slotQuantizeSelection(int)));
637 }
638 
639 void
initRulersToolbar()640 MatrixView::initRulersToolbar()
641 {
642     QToolBar *rulersToolbar = findToolbar("Rulers Toolbar");
643     if (!rulersToolbar) {
644         RG_WARNING << "MatrixView::initRulersToolbar() - rulers toolbar not found!";
645         return;
646     }
647 
648     // set the "ruler n" tool button to pop up its menu instantly
649     QToolButton *tb = dynamic_cast<QToolButton *>(findToolbar("Rulers Toolbar")->widgetForAction(findAction("add_control_ruler")));
650     tb->setPopupMode(QToolButton::InstantPopup);
651 }
652 
653 void
readOptions()654 MatrixView::readOptions()
655 {
656     EditViewBase::readOptions();
657 
658     setCheckBoxState("options_show_toolbar", "General Toolbar");
659     setCheckBoxState("show_tools_toolbar", "Tools Toolbar");
660     setCheckBoxState("show_transport_toolbar", "Transport Toolbar");
661     setCheckBoxState("show_actions_toolbar", "Actions Toolbar");
662     setCheckBoxState("show_rulers_toolbar", "Rulers Toolbar");
663 }
664 
665 void
initStatusBar()666 MatrixView::initStatusBar()
667 {
668     statusBar();
669 }
670 
671 void
slotShowContextHelp(const QString & help)672 MatrixView::slotShowContextHelp(const QString &help)
673 {
674     statusBar()->showMessage(help, 10000);
675 }
676 
677 void
slotUpdateMenuStates()678 MatrixView::slotUpdateMenuStates()
679 {
680     EventSelection *s = getSelection();
681     if (s && !s->getSegmentEvents().empty()) {
682         enterActionState("have_selection");
683     } else {
684         leaveActionState("have_selection");
685     }
686     conformRulerSelectionState();
687 }
688 
689 void
690 MatrixView::
conformRulerSelectionState()691 conformRulerSelectionState()
692 {
693     ControlRulerWidget * cr = m_matrixWidget->getControlsWidget();
694     if (cr->isAnyRulerVisible())
695         {
696             cr->slotSelectionChanged(getSelection());
697 
698             enterActionState("have_control_ruler");
699             if (cr->hasSelection())
700                 { enterActionState("have_controller_selection"); }
701             else
702                 { leaveActionState("have_controller_selection"); }
703         }
704     else {
705         leaveActionState("have_control_ruler");
706         // No ruler implies no controller selection
707         leaveActionState("have_controller_selection");
708     }
709  }
710 
711 void
slotSetPaintTool()712 MatrixView::slotSetPaintTool()
713 {
714     if (m_matrixWidget)
715         m_matrixWidget->setDrawTool();
716 }
717 
718 void
slotSetEraseTool()719 MatrixView::slotSetEraseTool()
720 {
721     if (m_matrixWidget)
722         m_matrixWidget->setEraseTool();
723 }
724 
725 void
slotSetSelectTool()726 MatrixView::slotSetSelectTool()
727 {
728     if (m_matrixWidget)
729         m_matrixWidget->setSelectAndEditTool();
730 }
731 
732 void
slotSetMoveTool()733 MatrixView::slotSetMoveTool()
734 {
735     if (m_matrixWidget)
736         m_matrixWidget->setMoveTool();
737 }
738 
739 void
slotSetResizeTool()740 MatrixView::slotSetResizeTool()
741 {
742     if (m_matrixWidget)
743         m_matrixWidget->setResizeTool();
744 }
745 
746 void
slotSetVelocityTool()747 MatrixView::slotSetVelocityTool()
748 {
749     if (m_matrixWidget)
750         m_matrixWidget->setVelocityTool();
751 }
752 
753 Segment *
getCurrentSegment()754 MatrixView::getCurrentSegment()
755 {
756     if (m_matrixWidget) return m_matrixWidget->getCurrentSegment();
757     else return nullptr;
758 }
759 
760 EventSelection *
getSelection() const761 MatrixView::getSelection() const
762 {
763     if (m_matrixWidget) return m_matrixWidget->getSelection();
764     else return nullptr;
765 }
766 
767 void
setSelection(EventSelection * s,bool preview)768 MatrixView::setSelection(EventSelection *s, bool preview)
769 {
770     if (m_matrixWidget) m_matrixWidget->setSelection(s, preview);
771 }
772 
773 timeT
getInsertionTime() const774 MatrixView::getInsertionTime() const
775 {
776     if (!m_document) return 0;
777     return m_document->getComposition().getPosition();
778 }
779 
780 const SnapGrid *
getSnapGrid() const781 MatrixView::getSnapGrid() const
782 {
783     if (m_matrixWidget) return m_matrixWidget->getSnapGrid();
784     else return nullptr;
785 }
786 
787 void
slotSetSnapFromIndex(int s)788 MatrixView::slotSetSnapFromIndex(int s)
789 {
790     slotSetSnap(m_snapValues[s]);
791 }
792 
793 void
slotSetSnapFromAction()794 MatrixView::slotSetSnapFromAction()
795 {
796     const QObject *s = sender();
797     QString name = s->objectName();
798 
799     if (name.left(5) == "snap_") {
800         int snap = name.right(name.length() - 5).toInt();
801         if (snap > 0) {
802             slotSetSnap(Note(Note::Semibreve).getDuration() / snap);
803         } else if (name.left(12) == "snap_dotted_") {
804             snap = name.right(name.length() - 12).toInt();
805             slotSetSnap((3*Note(Note::Semibreve).getDuration()) / (2*snap));
806         } else if (name == "snap_none") {
807             slotSetSnap(SnapGrid::NoSnap);
808         } else if (name == "snap_beat") {
809             slotSetSnap(SnapGrid::SnapToBeat);
810         } else if (name == "snap_bar") {
811             slotSetSnap(SnapGrid::SnapToBar);
812         } else if (name == "snap_unit") {
813             slotSetSnap(SnapGrid::SnapToUnit);
814         } else {
815             MATRIX_DEBUG << "Warning: MatrixView::slotSetSnapFromAction: unrecognised action " << name;
816         }
817     }
818 }
819 
820 void
slotSetSnap(timeT t)821 MatrixView::slotSetSnap(timeT t)
822 {
823     m_matrixWidget->setSnap(t);
824 
825     for (unsigned int i = 0; i < m_snapValues.size(); ++i) {
826         if (m_snapValues[i] == t) {
827             m_snapGridCombo->setCurrentIndex(i);
828             break;
829         }
830     }
831 }
832 
833 void
slotEditCut()834 MatrixView::slotEditCut()
835 {
836     EventSelection *selection = getSelection();
837     if (!selection) return;
838     CommandHistory::getInstance()->addCommand
839         (new CutCommand(*selection, getClipboard()));
840 }
841 
842 void
slotEditCopy()843 MatrixView::slotEditCopy()
844 {
845     EventSelection *selection = getSelection();
846     if (!selection) return;
847     CommandHistory::getInstance()->addCommand
848         (new CopyCommand(*selection, getClipboard()));
849 //    emit usedSelection();//!!!
850 }
851 
852 void
slotEditPaste()853 MatrixView::slotEditPaste()
854 {
855     if (getClipboard()->isEmpty()) return;
856 
857     PasteEventsCommand *command = new PasteEventsCommand
858         (*m_matrixWidget->getCurrentSegment(),
859          getClipboard(),
860          getInsertionTime(),
861          PasteEventsCommand::MatrixOverlay);
862 
863     if (!command->isPossible()) {
864         return;
865     } else {
866         CommandHistory::getInstance()->addCommand(command);
867         setSelection(command->getSubsequentSelection(), false);
868     }
869 }
870 
871 void
slotEditDelete()872 MatrixView::slotEditDelete()
873 {
874     EventSelection *selection = getSelection();
875     if (!selection) return;
876     CommandHistory::getInstance()->addCommand(new EraseCommand(*selection));
877 }
878 
879 void
slotQuantizeSelection(int q)880 MatrixView::slotQuantizeSelection(int q)
881 {
882     MATRIX_DEBUG << "MatrixView::slotQuantizeSelection\n";
883 
884     timeT unit =
885         ((unsigned int)q < m_quantizations.size() ? m_quantizations[q] : 0);
886 
887     Quantizer *quant =
888         new BasicQuantizer
889         (unit ? unit :
890          Note(Note::Shortest).getDuration(), false);
891 
892     EventSelection *selection = getSelection();
893     if (!selection) return;
894 
895     if (unit) {
896         if (selection && selection->getAddedEvents()) {
897             CommandHistory::getInstance()->addCommand
898                 (new EventQuantizeCommand(*selection, quant));
899         } else {
900             Segment *s = m_matrixWidget->getCurrentSegment();
901             if (s) {
902                 CommandHistory::getInstance()->addCommand
903                     (new EventQuantizeCommand
904                      (*s, s->getStartTime(), s->getEndMarkerTime(), quant));
905             }
906         }
907     } else {
908         if (selection && selection->getAddedEvents()) {
909             CommandHistory::getInstance()->addCommand
910                 (new EventUnquantizeCommand(*selection, quant));
911         } else {
912             Segment *s = m_matrixWidget->getCurrentSegment();
913             if (s) {
914                 CommandHistory::getInstance()->addCommand
915                     (new EventUnquantizeCommand
916                      (*s, s->getStartTime(), s->getEndMarkerTime(), quant));
917             }
918         }
919     }
920 }
921 
922 void
slotQuantize()923 MatrixView::slotQuantize()
924 {
925     if (!getSelection()) return;
926 
927     QuantizeDialog dialog(this);
928 
929     if (dialog.exec() == QDialog::Accepted) {
930         CommandHistory::getInstance()->addCommand
931             (new EventQuantizeCommand
932              (*getSelection(),
933               dialog.getQuantizer()));
934     }
935 }
936 
937 void
slotRepeatQuantize()938 MatrixView::slotRepeatQuantize()
939 {
940     if (!getSelection()) return;
941     CommandHistory::getInstance()->addCommand
942         (new EventQuantizeCommand
943          (*getSelection(),
944           "Quantize Dialog Grid", // no tr (config group name)
945           EventQuantizeCommand::QUANTIZE_NORMAL));
946 }
947 
948 void
slotCollapseNotes()949 MatrixView::slotCollapseNotes()
950 {
951     if (!getSelection()) return;
952     CommandHistory::getInstance()->addCommand
953         (new CollapseNotesCommand(*getSelection()));
954 }
955 
956 void
slotLegato()957 MatrixView::slotLegato()
958 {
959     if (!getSelection()) return;
960     CommandHistory::getInstance()->addCommand
961         (new EventQuantizeCommand
962          (*getSelection(),
963           new LegatoQuantizer(0))); // no quantization
964 }
965 
966 void
slotVelocityUp()967 MatrixView::slotVelocityUp()
968 {
969     if (!getSelection()) return;
970 
971     CommandHistory::getInstance()->addCommand
972         (new ChangeVelocityCommand(10, *getSelection()));
973 
974     slotSetCurrentVelocityFromSelection();
975 }
976 
977 void
slotVelocityDown()978 MatrixView::slotVelocityDown()
979 {
980     if (!getSelection()) return;
981 
982     CommandHistory::getInstance()->addCommand
983         (new ChangeVelocityCommand(-10, *getSelection()));
984 
985     slotSetCurrentVelocityFromSelection();
986 }
987 
988 void
slotSetVelocities()989 MatrixView::slotSetVelocities()
990 {
991     ParameterPattern::
992         setVelocities(this, getSelection(), getCurrentVelocity());
993 }
994 
995 void
slotSetVelocitiesToCurrent()996 MatrixView::slotSetVelocitiesToCurrent()
997 {
998     ParameterPattern::
999         setVelocitiesFlat(getSelection(), getCurrentVelocity());
1000 }
1001 
1002 void
slotEditCopyControllers()1003 MatrixView::slotEditCopyControllers()
1004 {
1005     ControlRulerWidget *cr = m_matrixWidget->getControlsWidget();
1006     EventSelection *selection = cr->getSelection();
1007     if (!selection) return;
1008     CommandHistory::getInstance()->addCommand
1009         (new CopyCommand(*selection, getClipboard()));
1010 }
1011 
1012 void
slotEditCutControllers()1013 MatrixView::slotEditCutControllers()
1014 {
1015     ControlRulerWidget *cr = m_matrixWidget->getControlsWidget();
1016     EventSelection *selection = cr->getSelection();
1017     if (!selection) return;
1018     CommandHistory::getInstance()->addCommand
1019         (new CutCommand(*selection, getClipboard()));
1020 }
1021 
1022 void
slotSetControllers()1023 MatrixView::slotSetControllers()
1024 {
1025     ControlRulerWidget * cr = m_matrixWidget->getControlsWidget();
1026     ParameterPattern::setProperties(
1027             this,
1028             tr("Set Controller Values"),
1029             cr->getSituation(),
1030             &ParameterPattern::VelocityPatterns);
1031 }
1032 
1033 void
slotPlaceControllers()1034 MatrixView::slotPlaceControllers()
1035 {
1036     EventSelection *selection = getSelection();
1037     if (!selection) { return; }
1038 
1039     ControlRulerWidget *cr = m_matrixWidget->getControlsWidget();
1040     if (!cr) { return; }
1041 
1042     ControlParameter *cp = cr->getControlParameter();
1043     if (!cp) { return; }
1044 
1045     const Instrument *instrument =
1046         getDocument()->getInstrument(getCurrentSegment());
1047     if (!instrument) { return; }
1048 
1049     PlaceControllersCommand *command =
1050         new PlaceControllersCommand(*selection,
1051                                     instrument,
1052                                     cp);
1053     CommandHistory::getInstance()->addCommand(command);
1054 }
1055 
1056 
1057 void
slotTriggerSegment()1058 MatrixView::slotTriggerSegment()
1059 {
1060     if (!getSelection()) return;
1061 
1062     TriggerSegmentDialog dialog(this, &m_document->getComposition());
1063     if (dialog.exec() != QDialog::Accepted) return;
1064 
1065     CommandHistory::getInstance()->addCommand
1066         (new SetTriggerCommand(*getSelection(),
1067                                dialog.getId(),
1068                                true,
1069                                dialog.getRetune(),
1070                                dialog.getTimeAdjust(),
1071                                Marks::NoMark,
1072                                tr("Trigger Segment")));
1073 }
1074 
1075 void
slotRemoveTriggers()1076 MatrixView::slotRemoveTriggers()
1077 {
1078     if (!getSelection()) return;
1079 
1080     CommandHistory::getInstance()->addCommand
1081         (new ClearTriggersCommand(*getSelection(),
1082                                   tr("Remove Triggers")));
1083 }
1084 
1085 void
slotSelectAll()1086 MatrixView::slotSelectAll()
1087 {
1088     if (m_matrixWidget)
1089         m_matrixWidget->selectAll();
1090 }
1091 
1092 void
slotCurrentSegmentPrior()1093 MatrixView::slotCurrentSegmentPrior()
1094 {
1095     if (m_matrixWidget)
1096         m_matrixWidget->previousSegment();
1097 }
1098 
1099 void
slotCurrentSegmentNext()1100 MatrixView::slotCurrentSegmentNext()
1101 {
1102     if (m_matrixWidget)
1103         m_matrixWidget->nextSegment();
1104 }
1105 
1106 void
slotPreviewSelection()1107 MatrixView::slotPreviewSelection()
1108 {
1109     if (!getSelection()) {
1110         return;
1111     }
1112 
1113     m_document->slotSetLoop(getSelection()->getStartTime(),
1114                             getSelection()->getEndTime());
1115 }
1116 
1117 void
slotClearLoop()1118 MatrixView::slotClearLoop()
1119 {
1120     m_document->slotSetLoop(0, 0);
1121 }
1122 
1123 void
slotClearSelection()1124 MatrixView::slotClearSelection()
1125 {
1126     if (m_matrixWidget) m_matrixWidget->clearSelection();
1127 }
1128 
1129 void
slotFilterSelection()1130 MatrixView::slotFilterSelection()
1131 {
1132     MATRIX_DEBUG << "MatrixView::slotFilterSelection";
1133 
1134     if (!m_matrixWidget) return;
1135 
1136     Segment *segment = m_matrixWidget->getCurrentSegment();
1137     EventSelection *existingSelection = getSelection();
1138     if (!segment || !existingSelection) return;
1139 
1140     EventFilterDialog dialog(this);
1141     if (dialog.exec() == QDialog::Accepted) {
1142         MATRIX_DEBUG << "slotFilterSelection- accepted";
1143 
1144         bool haveEvent = false;
1145 
1146         EventSelection *newSelection = new EventSelection(*segment);
1147         EventSelection::eventcontainer &ec =
1148             existingSelection->getSegmentEvents();
1149         for (EventSelection::eventcontainer::iterator i =
1150                     ec.begin(); i != ec.end(); ++i) {
1151             if (dialog.keepEvent(*i)) {
1152                 haveEvent = true;
1153                 newSelection->addEvent(*i);
1154             }
1155         }
1156 
1157         if (haveEvent) setSelection(newSelection, false);
1158         else setSelection(nullptr, false);
1159     }
1160 }
1161 
1162 int
getCurrentVelocity() const1163 MatrixView::getCurrentVelocity() const
1164 {
1165     return m_velocityCombo->currentIndex();
1166 }
1167 
1168 void
slotSetCurrentVelocity(int value)1169 MatrixView::slotSetCurrentVelocity(int value)
1170 {
1171     m_velocityCombo->setCurrentIndex(value);
1172 }
1173 
1174 void
slotSetCurrentVelocityFromSelection()1175 MatrixView::slotSetCurrentVelocityFromSelection()
1176 {
1177     if (!getSelection()) return;
1178 
1179     float totalVelocity = 0;
1180     int count = 0;
1181 
1182     for (EventSelection::eventcontainer::iterator i =
1183              getSelection()->getSegmentEvents().begin();
1184          i != getSelection()->getSegmentEvents().end(); ++i) {
1185 
1186         if ((*i)->has(BaseProperties::VELOCITY)) {
1187             totalVelocity += (*i)->get<Int>(BaseProperties::VELOCITY);
1188             ++count;
1189         }
1190     }
1191 
1192     if (count > 0) {
1193         slotSetCurrentVelocity((totalVelocity / count) + 0.5);
1194     }
1195 }
1196 
1197 void
slotToggleTracking()1198 MatrixView::slotToggleTracking()
1199 {
1200     m_tracking = !m_tracking;
1201     m_matrixWidget->setScrollToFollowPlayback(m_tracking);
1202 }
1203 
1204 void
slotToggleChordsRuler()1205 MatrixView::slotToggleChordsRuler()
1206 {
1207     bool view = findAction("show_chords_ruler")->isChecked();
1208 
1209     m_matrixWidget->setChordNameRulerVisible(view);
1210 
1211     QSettings settings;
1212     settings.beginGroup(MatrixViewConfigGroup);
1213     settings.setValue("Chords ruler shown", view);
1214     settings.endGroup();
1215 }
1216 
1217 void
slotToggleVelocityRuler()1218 MatrixView::slotToggleVelocityRuler()
1219 {
1220     m_matrixWidget->showVelocityRuler();
1221     conformRulerSelectionState();
1222 }
1223 
1224 void
slotTogglePitchbendRuler()1225 MatrixView::slotTogglePitchbendRuler()
1226 {
1227     m_matrixWidget->showPitchBendRuler();
1228     conformRulerSelectionState();
1229 }
1230 
1231 void
slotAddControlRuler(QAction * action)1232 MatrixView::slotAddControlRuler(QAction *action)
1233 {
1234     m_matrixWidget->addControlRuler(action);
1235     conformRulerSelectionState();
1236 }
1237 
1238 void
slotToggleTempoRuler()1239 MatrixView::slotToggleTempoRuler()
1240 {
1241     bool view = findAction("show_tempo_ruler")->isChecked();
1242 
1243     m_matrixWidget->setTempoRulerVisible(view);
1244 
1245     QSettings settings;
1246     settings.beginGroup(MatrixViewConfigGroup);
1247     settings.setValue("Tempo ruler shown", view);
1248     settings.endGroup();
1249 }
1250 
1251 // start of code formerly located in EditView.cpp
1252 // --
1253 
slotAddTempo()1254 void MatrixView::slotAddTempo()
1255 {
1256     timeT insertionTime = getInsertionTime();
1257 
1258     TempoDialog tempoDlg(this, getDocument());
1259 
1260     connect(&tempoDlg,
1261              SIGNAL(changeTempo(timeT,
1262                     tempoT,
1263                     tempoT,
1264                     TempoDialog::TempoDialogAction)),
1265                     this,
1266                     SIGNAL(changeTempo(timeT,
1267                            tempoT,
1268                            tempoT,
1269                            TempoDialog::TempoDialogAction)));
1270 
1271     tempoDlg.setTempoPosition(insertionTime);
1272     tempoDlg.exec();
1273 }
1274 
slotAddTimeSignature()1275 void MatrixView::slotAddTimeSignature()
1276 {
1277     Segment *segment = getCurrentSegment();
1278     if (!segment)
1279         return ;
1280     Composition *composition = segment->getComposition();
1281     timeT insertionTime = getInsertionTime();
1282 
1283     TimeSignatureDialog *dialog = nullptr;
1284     int timeSigNo = composition->getTimeSignatureNumberAt(insertionTime);
1285 
1286     if (timeSigNo >= 0) {
1287 
1288         dialog = new TimeSignatureDialog
1289                 (this, composition, insertionTime,
1290                  composition->getTimeSignatureAt(insertionTime));
1291 
1292     } else {
1293 
1294         timeT endTime = composition->getDuration();
1295         if (composition->getTimeSignatureCount() > 0) {
1296             endTime = composition->getTimeSignatureChange(0).first;
1297         }
1298 
1299         CompositionTimeSliceAdapter adapter
1300                 (composition, insertionTime, endTime);
1301         AnalysisHelper helper;
1302         TimeSignature timeSig = helper.guessTimeSignature(adapter);
1303 
1304         dialog = new TimeSignatureDialog
1305                 (this, composition, insertionTime, timeSig, false,
1306                  tr("Estimated time signature shown"));
1307     }
1308 
1309     if (dialog->exec() == QDialog::Accepted) {
1310 
1311         insertionTime = dialog->getTime();
1312 
1313         if (dialog->shouldNormalizeRests()) {
1314 
1315             CommandHistory::getInstance()->addCommand(new AddTimeSignatureAndNormalizeCommand
1316                     (composition, insertionTime,
1317                      dialog->getTimeSignature()));
1318 
1319         } else {
1320 
1321             CommandHistory::getInstance()->addCommand(new AddTimeSignatureCommand
1322                     (composition, insertionTime,
1323                      dialog->getTimeSignature()));
1324         }
1325     }
1326 
1327     delete dialog;
1328 }
1329 
1330 
1331 
slotHalveDurations()1332 void MatrixView::slotHalveDurations()
1333 {
1334     EventSelection *selection = getSelection();
1335     if (!selection) return;
1336 
1337     CommandHistory::getInstance()->addCommand( new RescaleCommand
1338                             (*selection,
1339                             selection->getTotalDuration() / 2,
1340                             false)
1341                        );
1342 }
1343 
slotDoubleDurations()1344 void MatrixView::slotDoubleDurations()
1345 {
1346     EventSelection *selection = getSelection();
1347     if (!selection) return;
1348     CommandHistory::getInstance()->addCommand(new RescaleCommand(*selection,
1349                                             selection->getTotalDuration() * 2,
1350                                                     false)
1351                        );
1352 }
1353 
slotRescale()1354 void MatrixView::slotRescale()
1355 {
1356     EventSelection *selection = getSelection();
1357     if (!selection) return;
1358 
1359     RescaleDialog dialog(this,
1360                          &getDocument()->getComposition(),
1361                          selection->getStartTime(),
1362                          selection->getEndTime() -
1363                              selection->getStartTime(),
1364                          1,
1365                          true,
1366                          true
1367                         );
1368 
1369     if (dialog.exec() == QDialog::Accepted) {
1370         CommandHistory::getInstance()->addCommand(new RescaleCommand
1371                 (*selection,
1372                   dialog.getNewDuration(),
1373                                         dialog.shouldCloseGap()));
1374     }
1375 }
1376 
slotTranspose()1377 void MatrixView::slotTranspose()
1378 {
1379     EventSelection *selection = getSelection();
1380     if (!selection) {
1381         RG_WARNING << "Hint: selection is nullptr in slotTranpose()";
1382         return;
1383     }
1384 
1385     QSettings settings;
1386     settings.beginGroup(MatrixViewConfigGroup);
1387 
1388     int dialogDefault = settings.value("lasttransposition", 0).toInt() ;
1389 
1390     bool ok = false;
1391     int min = -127;
1392     int max = 127;
1393     int step = 1;
1394     int semitones = QInputDialog::getInt(
1395             this,
1396             tr("Transpose"),
1397             tr("By number of semitones: "),
1398             dialogDefault,
1399             min,
1400             max,
1401             step,
1402             &ok);
1403 
1404     if (!ok || semitones == 0) return;
1405 
1406     settings.setValue("lasttransposition", semitones);
1407 
1408     CommandHistory::getInstance()->addCommand(new TransposeCommand
1409             (semitones, *selection));
1410 
1411     settings.endGroup();
1412 }
1413 
slotDiatonicTranspose()1414 void MatrixView::slotDiatonicTranspose()
1415 {
1416     EventSelection *selection = getSelection();
1417     if (!selection) return;
1418 
1419     QSettings settings;
1420     settings.beginGroup(MatrixViewConfigGroup);
1421 
1422     IntervalDialog intervalDialog(this);
1423     int ok = intervalDialog.exec();
1424     //int dialogDefault = settings.value("lasttransposition", 0).toInt() ;
1425     int semitones = intervalDialog.getChromaticDistance();
1426     int steps = intervalDialog.getDiatonicDistance();
1427     settings.endGroup();
1428 
1429     if (!ok || (semitones == 0 && steps == 0)) return;
1430 
1431     if (intervalDialog.getChangeKey())
1432     {
1433         RG_WARNING << "Transposing changing keys is not currently supported on selections";
1434     }
1435     else
1436     {
1437     // Transpose within key
1438         //std::cout << "Transposing semitones, steps: " << semitones << ", " << steps;
1439         CommandHistory::getInstance()->addCommand(new TransposeCommand
1440                 (semitones, steps, *selection));
1441     }
1442 }
1443 
slotTransposeUp()1444 void MatrixView::slotTransposeUp()
1445 {
1446     EventSelection *selection = getSelection();
1447     if (!selection) return ;
1448     CommandHistory::getInstance()->addCommand(new TransposeCommand(1, *selection));
1449 }
1450 
slotTransposeUpOctave()1451 void MatrixView::slotTransposeUpOctave()
1452 {
1453     EventSelection *selection = getSelection();
1454     if (!selection) return ;
1455     CommandHistory::getInstance()->addCommand(new TransposeCommand(12, *selection));
1456 }
1457 
slotTransposeDown()1458 void MatrixView::slotTransposeDown()
1459 {
1460     EventSelection *selection = getSelection();
1461     if (!selection) return ;
1462     CommandHistory::getInstance()->addCommand(new TransposeCommand( -1, *selection));
1463 }
1464 
slotTransposeDownOctave()1465 void MatrixView::slotTransposeDownOctave()
1466 {
1467     EventSelection *selection = getSelection();
1468     if (!selection) return ;
1469     CommandHistory::getInstance()->addCommand(new TransposeCommand( -12, *selection));
1470 }
1471 
slotInvert()1472 void MatrixView::slotInvert()
1473 {
1474     RG_DEBUG << "slotInvert() called";
1475 
1476     EventSelection *selection = getSelection();
1477     if (!selection) {
1478         RG_WARNING << "Hint: selection is nullptr in slotInvert()";
1479         return;
1480     }
1481 
1482     int semitones = 0;
1483     CommandHistory::getInstance()->addCommand(new InvertCommand
1484             (semitones, *selection));
1485 }
1486 
slotRetrograde()1487 void MatrixView::slotRetrograde()
1488 {
1489     EventSelection *selection = getSelection();
1490     if (!selection) return ;
1491 
1492     int semitones = 0;
1493     CommandHistory::getInstance()->addCommand(new RetrogradeCommand
1494             (semitones, *selection));
1495 }
1496 
slotRetrogradeInvert()1497 void MatrixView::slotRetrogradeInvert()
1498 {
1499     EventSelection *selection = getSelection();
1500     if (!selection) return ;
1501 
1502     int semitones = 0;
1503     CommandHistory::getInstance()->addCommand(new RetrogradeInvertCommand
1504             (semitones, *selection));
1505 }
1506 
1507 void
slotHelp()1508 MatrixView::slotHelp()
1509 {
1510     // TRANSLATORS: if the manual is translated into your language, you can
1511     // change the two-letter language code in this URL to point to your language
1512     // version, eg. "http://rosegardenmusic.com/wiki/doc:matrix-es" for the
1513     // Spanish version. If your language doesn't yet have a translation, feel
1514     // free to create one.
1515     QString helpURL = tr("http://rosegardenmusic.com/wiki/doc:matrix-en");
1516     QDesktopServices::openUrl(QUrl(helpURL));
1517 }
1518 
1519 void
slotTutorial()1520 MatrixView::slotTutorial()
1521 {
1522     QString tutorialURL = tr("http://www.rosegardenmusic.com/tutorials/en/chapter-0.html");
1523     QDesktopServices::openUrl(QUrl(tutorialURL));
1524 }
1525 
1526 void
slotBugGuidelines()1527 MatrixView::slotBugGuidelines()
1528 {
1529     QString glURL = tr("http://rosegarden.sourceforge.net/tutorial/bug-guidelines.html");
1530      QDesktopServices::openUrl(QUrl(glURL));
1531 }
1532 
1533 void
slotHelpAbout()1534 MatrixView::slotHelpAbout()
1535 {
1536     new AboutDialog(this);
1537 }
1538 
1539 void
slotHelpAboutQt()1540 MatrixView::slotHelpAboutQt()
1541 {
1542     QMessageBox::aboutQt(this, tr("Rosegarden"));
1543 }
1544 
1545 void
slotDonate()1546 MatrixView::slotDonate()
1547 {
1548     QDesktopServices::openUrl(QUrl(
1549             "https://www.rosegardenmusic.com/wiki/donations"));
1550 }
1551 
1552 void
slotStepBackward()1553 MatrixView::slotStepBackward()
1554 {
1555     Segment *segment = getCurrentSegment();
1556     if (!segment) return;
1557 
1558     // Sanity check.  Move postion marker inside segmet if not
1559     timeT time = getInsertionTime();  // Un-checked current insertion time
1560 
1561     timeT segmentEndTime = segment->getEndMarkerTime();
1562     if (time > segmentEndTime) {
1563         // Move to inside the current segment
1564         time = segment->getStartTime();
1565     }
1566 
1567     time = getSnapGrid()->snapTime(time - 1, SnapGrid::SnapLeft);
1568 
1569     if (time < segment->getStartTime()){
1570         m_document->slotSetPointerPosition(segment->getStartTime());
1571     } else {
1572         m_document->slotSetPointerPosition(time);
1573     }
1574 }
1575 
1576 void
slotStepForward(bool force)1577 MatrixView::slotStepForward(bool force)
1578 {
1579     Segment *segment = getCurrentSegment();
1580     if (!segment) return;
1581 
1582     // Sanity check.  Move postion marker inside segmet if not
1583     timeT time = getInsertionTime();  // Un-checked current insertion time
1584 
1585     timeT segmentStartTime = segment->getStartTime();
1586 
1587     if (!force && ((time < segmentStartTime) ||
1588             (time > segment->getEndMarkerTime()))) {
1589         // Move to inside the current segment
1590         time = segmentStartTime;
1591     }
1592 
1593     time = getSnapGrid()->snapTime(time + 1, SnapGrid::SnapRight);
1594 
1595     if (!force && (time > segment->getEndMarkerTime())){
1596         m_document->slotSetPointerPosition(segment->getEndMarkerTime());
1597     } else {
1598         m_document->slotSetPointerPosition(time);
1599     }
1600 }
1601 
1602 void
slotInsertableNoteEventReceived(int pitch,int velocity,bool noteOn)1603 MatrixView::slotInsertableNoteEventReceived(int pitch, int velocity, bool noteOn)
1604 {
1605     QAction *action = findAction("toggle_step_by_step");
1606 
1607     if (!action) {
1608         MATRIX_DEBUG << "WARNING: No toggle_step_by_step action";
1609         return ;
1610     }
1611 
1612     // return if not in step recording mode
1613     if (!action->isChecked()) return;
1614 
1615     // return if this window isn't active, to avoid filling a forgotten edit
1616     // view with garbage while banging away in some other window.
1617     //
1618     // NOTE: This prevents using something like VMPK for step recording, but I
1619     // see no better alternative.
1620     if (!isActiveWindow()) return;
1621 
1622 
1623     Segment *segment = getCurrentSegment();
1624 
1625     // If the segment is transposed, we want to take that into
1626     // account.  But the note has already been played back to the user
1627     // at its untransposed pitch, because that's done by the MIDI THRU
1628     // code in the sequencer which has no way to know whether a note
1629     // was intended for step recording.  So rather than adjust the
1630     // pitch for playback according to the transpose setting, we have
1631     // to adjust the stored pitch in the opposite direction.
1632 
1633     pitch -= segment->getTranspose();
1634 
1635 //    TmpStatusMsg msg(tr("Inserting note"), this);
1636 
1637     static int numberOfNotesOn = 0;
1638     static timeT insertionTime = getInsertionTime();
1639     static time_t lastInsertionTime = 0;
1640 
1641     if (!noteOn) {
1642         numberOfNotesOn--;
1643         return ;
1644     }
1645     // Rules:
1646     //
1647     // * If no other note event has turned up within half a
1648     //   second, insert this note and advance.
1649     //
1650     // * Relatedly, if this note is within half a second of
1651     //   the previous one, they're chords.  Insert the previous
1652     //   one, don't advance, and use the same rules for this.
1653     //
1654     // * If a note event turns up before that time has elapsed,
1655     //   we need to wait for the note-off events: if the second
1656     //   note happened less than half way through the first,
1657     //   it's a chord.
1658     //
1659     // We haven't implemented these yet... For now:
1660     //
1661     // Rules (hjj):
1662     //
1663     // * The overlapping notes are always included in to a chord.
1664     //   This is the most convenient for step inserting of chords.
1665     //
1666     // * The timer resets the numberOfNotesOn, if noteOff signals were
1667     //   drop out for some reason (which has not been encountered yet).
1668     time_t now;
1669     time (&now);
1670     double elapsed = difftime(now, lastInsertionTime);
1671     time (&lastInsertionTime);
1672 
1673     if (numberOfNotesOn <= 0 || elapsed > 10.0 ) {
1674         numberOfNotesOn = 0;
1675         insertionTime = getInsertionTime();
1676     }
1677     numberOfNotesOn++;
1678 
1679 
1680     MATRIX_DEBUG << "Inserting note at pitch " << pitch;
1681 
1682     Event modelEvent(Note::EventType, 0, 1);
1683     modelEvent.set<Int>(BaseProperties::PITCH, pitch);
1684     modelEvent.set<Int>(BaseProperties::VELOCITY, velocity);
1685 
1686     timeT segStartTime = segment->getStartTime();
1687     if ((insertionTime < segStartTime) ||
1688             (insertionTime > segment->getEndMarkerTime())) {
1689         MATRIX_DEBUG << "WARNING: off of segment -- "
1690                      <<"moving to start of segment";
1691         insertionTime = segStartTime;
1692     }
1693 
1694     timeT endTime(insertionTime + getSnapGrid()->getSnapTime(insertionTime));
1695 
1696     if (endTime <= insertionTime) {
1697         // Fail silently, as in notation view
1698         return;
1699     }
1700 
1701     MatrixInsertionCommand* command =
1702         new MatrixInsertionCommand(*segment, insertionTime,
1703                                    endTime, &modelEvent);
1704 
1705     CommandHistory::getInstance()->addCommand(command);
1706 
1707     if (!m_inChordMode) {
1708         m_document->slotSetPointerPosition(endTime);
1709     }
1710 }
1711 
1712 void
slotInsertableNoteOnReceived(int pitch,int velocity)1713 MatrixView::slotInsertableNoteOnReceived(int pitch, int velocity)
1714 {
1715     MATRIX_DEBUG << "MatrixView::slotInsertableNoteOnReceived: " << pitch;
1716     slotInsertableNoteEventReceived(pitch, velocity, true);
1717 }
1718 
1719 void
slotInsertableNoteOffReceived(int pitch,int velocity)1720 MatrixView::slotInsertableNoteOffReceived(int pitch, int velocity)
1721 {
1722     MATRIX_DEBUG << "MatrixView::slotInsertableNoteOffReceived: " << pitch;
1723     slotInsertableNoteEventReceived(pitch, velocity, false);
1724 }
1725 
1726 void
slotPitchBendSequence()1727 MatrixView::slotPitchBendSequence()
1728 {
1729     insertControllerSequence(ControlParameter::getPitchBend());
1730 }
1731 
1732 void
slotControllerSequence()1733 MatrixView::slotControllerSequence()
1734 {
1735     ControlRulerWidget *cr = m_matrixWidget->getControlsWidget();
1736     if (!cr)
1737         return;
1738 
1739     const ControlParameter *cp = cr->getControlParameter();
1740     if (!cp)
1741     {
1742         QMessageBox::information(
1743                 this,
1744                 tr("Rosegarden"),
1745                 tr("Please select a control ruler first."));
1746 
1747         return;
1748     }
1749 
1750     insertControllerSequence(*cp);
1751 }
1752 
1753 void
1754 MatrixView::
insertControllerSequence(const ControlParameter & controlParameter)1755 insertControllerSequence(const ControlParameter &controlParameter)
1756 {
1757     EventSelection *selection = getSelection();
1758 
1759     // No selection?  Bail.
1760     if (!selection)
1761         return;
1762 
1763     const timeT startTime = selection->getStartTime();
1764     const timeT endTime = selection->getEndTime();
1765 
1766     // Times make no sense?  Bail.
1767     if (startTime >= endTime)
1768         return;
1769 
1770     PitchBendSequenceDialog dialog(
1771             this,  // parent
1772             getCurrentSegment(),
1773             controlParameter,
1774             startTime,
1775             endTime);
1776 
1777     dialog.exec();
1778 }
1779 
1780 void
slotInsertNoteFromAction()1781 MatrixView::slotInsertNoteFromAction()
1782 {
1783     const QObject *s = sender();
1784     QString name = s->objectName();
1785 
1786     Segment *segment = getCurrentSegment();
1787     if (!segment) return;
1788 
1789     int pitch = 0;
1790 
1791     Accidental accidental =
1792         Accidentals::NoAccidental;
1793 
1794     timeT time(getInsertionTime());
1795     if (time >= segment->getEndMarkerTime()) {
1796         MATRIX_DEBUG << "WARNING: off end of segment";
1797         return ;
1798     }
1799     ::Rosegarden::Key key = segment->getKeyAtTime(time);
1800     Clef clef = segment->getClefAtTime(time);
1801 
1802     try {
1803 
1804         pitch = getPitchFromNoteInsertAction(name, accidental, clef, key);
1805 
1806     } catch (...) {
1807 
1808         QMessageBox::warning(this, tr("Rosegarden"), tr("Unknown note insert action %1").arg(name));
1809         return ;
1810     }
1811 
1812 //    TmpStatusMsg msg(tr("Inserting note"), this);
1813 
1814     MATRIX_DEBUG << "Inserting note at pitch " << pitch;
1815 
1816     Event modelEvent(Note::EventType, 0, 1);
1817     modelEvent.set<Int>(BaseProperties::PITCH, pitch);
1818     modelEvent.set<String>(BaseProperties::ACCIDENTAL, accidental);
1819     timeT endTime(time + getSnapGrid()->getSnapTime(time));
1820 
1821     MatrixInsertionCommand* command =
1822         new MatrixInsertionCommand(*segment, time, endTime, &modelEvent);
1823 
1824     CommandHistory::getInstance()->addCommand(command);
1825 
1826     if (!m_inChordMode) {
1827         m_document->slotSetPointerPosition(endTime);
1828     }
1829 
1830     emit noteInsertedFromKeyboard(segment, pitch);
1831     //  ==> MatrixWidget::slotPlayPreviewNote() ==> MatrixScene::playNote()
1832 }
1833 
1834 void
slotToggleChordMode()1835 MatrixView::slotToggleChordMode()
1836 {
1837     m_inChordMode = !m_inChordMode;
1838 
1839     // bits to update status bar if/when we ever have one again
1840 }
1841 
1842 
1843 int
getPitchFromNoteInsertAction(QString name,Accidental & accidental,const Clef & clef,const Rosegarden::Key & key)1844 MatrixView::getPitchFromNoteInsertAction(QString name,
1845                                               Accidental &accidental,
1846                                               const Clef &clef,
1847                                               const Rosegarden::Key &key)
1848 {
1849     using namespace Accidentals;
1850 
1851     accidental = NoAccidental;
1852 
1853     if (name.left(7) == "insert_") {
1854 
1855         name = name.right(name.length() - 7);
1856 
1857         // int modify = 0;
1858         int octave = 0;
1859 
1860         if (name.right(5) == "_high") {
1861 
1862             octave = 1;
1863             name = name.left(name.length() - 5);
1864 
1865         } else if (name.right(4) == "_low") {
1866 
1867             octave = -1;
1868             name = name.left(name.length() - 4);
1869         }
1870 
1871         if (name.right(6) == "_sharp") {
1872 
1873             // modify = 1;
1874             accidental = Sharp;
1875             name = name.left(name.length() - 6);
1876 
1877         } else if (name.right(5) == "_flat") {
1878 
1879             // modify = -1;
1880             accidental = Flat;
1881             name = name.left(name.length() - 5);
1882         }
1883 
1884         int scalePitch = name.toInt();
1885 
1886         if (scalePitch < 0 || scalePitch > 7) {
1887             NOTATION_DEBUG << "MatrixView::getPitchFromNoteInsertAction: pitch "
1888             << scalePitch << " out of range, using 0";
1889             scalePitch = 0;
1890         }
1891 
1892         Pitch clefPitch(clef.getAxisHeight(), clef, key, NoAccidental);
1893 
1894         int pitchOctave = clefPitch.getOctave() + octave;
1895 
1896         MATRIX_DEBUG << "MatrixView::getPitchFromNoteInsertAction:"
1897                   << " key = " << key.getName()
1898                   << ", clef = " << clef.getClefType()
1899                   << ", octaveoffset = " << clef.getOctaveOffset();
1900         MATRIX_DEBUG << "MatrixView::getPitchFromNoteInsertAction: octave = " << pitchOctave;
1901 
1902         // We want still to make sure that when (i) octave = 0,
1903         //  (ii) one of the noteInScale = 0..6 is
1904         //  (iii) at the same heightOnStaff than the heightOnStaff of the key.
1905         int lowestNoteInScale = 0;
1906         Pitch lowestPitch(lowestNoteInScale, clefPitch.getOctave(), key, NoAccidental);
1907 
1908         int heightToAdjust = (clefPitch.getHeightOnStaff(clef, key) - lowestPitch.getHeightOnStaff(clef, key));
1909         for (; heightToAdjust < 0; heightToAdjust += 7) pitchOctave++;
1910         for (; heightToAdjust > 6; heightToAdjust -= 7) pitchOctave--;
1911 
1912         MATRIX_DEBUG << "MatrixView::getPitchFromNoteInsertAction: octave = " << pitchOctave << " (adjusted)";
1913 
1914         Pitch pitch(scalePitch, pitchOctave, key, accidental);
1915         return pitch.getPerformancePitch();
1916 
1917     } else {
1918 
1919         throw Exception("Not an insert action",
1920                         __FILE__, __LINE__);
1921     }
1922 }
1923 
1924 
1925 void
toggleNamedToolBar(const QString & toolBarName,bool * force)1926 MatrixView::toggleNamedToolBar(const QString& toolBarName, bool* force)
1927 {
1928     QToolBar *namedToolBar = findChild<QToolBar*>(toolBarName);
1929 
1930     if (!namedToolBar) {
1931         MATRIX_DEBUG << "MatrixView::toggleNamedToolBar() : toolBar "
1932                        << toolBarName << " not found";
1933         return ;
1934     }
1935 
1936     if (!force) {
1937 
1938         if (namedToolBar->isVisible())
1939             namedToolBar->hide();
1940         else
1941             namedToolBar->show();
1942     } else {
1943 
1944         if (*force)
1945             namedToolBar->show();
1946         else
1947             namedToolBar->hide();
1948     }
1949 }
1950 
1951 void
slotToggleGeneralToolBar()1952 MatrixView::slotToggleGeneralToolBar()
1953 {
1954     toggleNamedToolBar("General Toolbar");
1955 }
1956 
1957 void
slotToggleToolsToolBar()1958 MatrixView::slotToggleToolsToolBar()
1959 {
1960     toggleNamedToolBar("Tools Toolbar");
1961 }
1962 
1963 void
slotToggleTransportToolBar()1964 MatrixView::slotToggleTransportToolBar()
1965 {
1966     toggleNamedToolBar("Transport Toolbar");
1967 }
1968 
1969 void
slotToggleActionsToolBar()1970 MatrixView::slotToggleActionsToolBar()
1971 {
1972     toggleNamedToolBar("Actions Toolbar");
1973 }
1974 
1975 void
slotToggleRulersToolBar()1976 MatrixView::slotToggleRulersToolBar()
1977 {
1978     toggleNamedToolBar("Rulers Toolbar");
1979 }
1980 
1981 void
slotToggleStepByStep()1982 MatrixView::slotToggleStepByStep()
1983 {
1984     QAction *action = findAction("toggle_step_by_step");
1985 
1986     if (!action) {
1987         MATRIX_DEBUG << "WARNING: No toggle_step_by_step action";
1988         return ;
1989     }
1990     if (action->isChecked()) { // after toggling, that is
1991         emit stepByStepTargetRequested(this);
1992     } else {
1993         emit stepByStepTargetRequested(nullptr);
1994     }
1995 }
1996 
1997 void
slotStepByStepTargetRequested(QObject * obj)1998 MatrixView::slotStepByStepTargetRequested(QObject *obj)
1999 {
2000     QAction *action = findAction("toggle_step_by_step");
2001 
2002     if (!action) {
2003         MATRIX_DEBUG << "WARNING: No toggle_step_by_step action";
2004         return ;
2005     }
2006     action->setChecked(obj == this);
2007 }
2008 
2009 Device *
getCurrentDevice()2010 MatrixView::getCurrentDevice()
2011 {
2012     if (m_matrixWidget) return m_matrixWidget->getCurrentDevice();
2013     else return nullptr;
2014 }
2015 
2016 void
slotExtendSelectionBackward()2017 MatrixView::slotExtendSelectionBackward()
2018 {
2019     slotExtendSelectionBackward(false);
2020 }
2021 
2022 void
slotExtendSelectionBackwardBar()2023 MatrixView::slotExtendSelectionBackwardBar()
2024 {
2025     slotExtendSelectionBackward(true);
2026 }
2027 
2028 void
slotExtendSelectionBackward(bool bar)2029 MatrixView::slotExtendSelectionBackward(bool bar)
2030 {
2031     // If there is no current selection, or the selection is entirely
2032     // to the right of the cursor, move the cursor left and add to the
2033     // selection
2034 
2035     timeT oldTime = getInsertionTime();
2036 
2037     if (bar) emit rewindPlayback();
2038     else slotStepBackward();
2039 
2040     timeT newTime = getInsertionTime();
2041 
2042     Segment *segment = getCurrentSegment();
2043     if (!segment) return;
2044 
2045     MatrixViewSegment *vs = m_matrixWidget->getScene()->getCurrentViewSegment();
2046     ViewElementList *vel = vs->getViewElementList();
2047     EventSelection *s = getSelection();
2048     EventSelection *es = new EventSelection(*segment);
2049 
2050     if (s && &s->getSegment() == segment) es->addFromSelection(s);
2051 
2052     if (!s || &s->getSegment() != segment
2053            || s->getSegmentEvents().size() == 0
2054            || s->getStartTime() >= oldTime) {
2055 
2056         ViewElementList::iterator extendFrom = vel->findTime(oldTime);
2057 
2058         while (extendFrom != vel->begin() &&
2059                 (*--extendFrom)->getViewAbsoluteTime() >= newTime) {
2060 
2061             //!!! This should actually grab every sort of event, and not just
2062             // notes, but making that change makes the selection die every time
2063             // the endpoint of an indication is encountered, and I'm just not
2064             // seeing why, so I'm giving up on that and leaving it in the same
2065             // stupid state I found it in (and it's probably in this state
2066             // because the last guy had the same problem with indications.)
2067             //
2068             // I don't like this, because it makes it very easy to go along and
2069             // orphan indications, text events, controllers, and all sorts of
2070             // whatnot.  However, I have to call it quits for today, and have no
2071             // idea if I'll ever remember to come back to this, so I'm leaving a
2072             // reminder to someone that all of this is stupid.
2073             //
2074             // Note that here in the matrix, we still wouldn't want to orphan
2075             // indications, etc., even though they're not visible from here.
2076 
2077             if ((*extendFrom)->event()->isa(Note::EventType)) {
2078                 es->addEvent((*extendFrom)->event());
2079             }
2080         }
2081 
2082     } else { // remove an event
2083 
2084         EventSelection::eventcontainer::iterator i =
2085             es->getSegmentEvents().end();
2086 
2087         std::vector<Event *> toErase;
2088 
2089         while (i != es->getSegmentEvents().begin() &&
2090                 (*--i)->getAbsoluteTime() >= newTime) {
2091             toErase.push_back(*i);
2092         }
2093 
2094         for (unsigned int j = 0; j < toErase.size(); ++j) {
2095             es->removeEvent(toErase[j]);
2096         }
2097     }
2098 
2099     setSelection(es, true);
2100 }
2101 
2102 void
slotExtendSelectionForward()2103 MatrixView::slotExtendSelectionForward()
2104 {
2105     slotExtendSelectionForward(false);
2106 }
2107 
2108 void
slotExtendSelectionForwardBar()2109 MatrixView::slotExtendSelectionForwardBar()
2110 {
2111     slotExtendSelectionForward(true);
2112 }
2113 
2114 void
slotExtendSelectionForward(bool bar)2115 MatrixView::slotExtendSelectionForward(bool bar)
2116 {
2117     // If there is no current selection, or the selection is entirely
2118     // to the left of the cursor, move the cursor right and add to the
2119     // selection
2120 
2121     timeT oldTime = getInsertionTime();
2122 
2123     if (bar) emit fastForwardPlayback();
2124     else slotStepForward(true);
2125 
2126     timeT newTime = getInsertionTime();
2127 
2128     Segment *segment = getCurrentSegment();
2129     if (!segment) return;
2130 
2131     MatrixViewSegment *vs = m_matrixWidget->getScene()->getCurrentViewSegment();
2132     ViewElementList *vel = vs->getViewElementList();
2133     EventSelection *s = getSelection();
2134     EventSelection *es = new EventSelection(*segment);
2135 
2136     if (s && &s->getSegment() == segment) es->addFromSelection(s);
2137 
2138     if (!s || &s->getSegment() != segment
2139            || s->getSegmentEvents().size() == 0
2140            || s->getEndTime() <= oldTime) {
2141 
2142         ViewElementList::iterator extendFrom = vel->findTime(oldTime);
2143 
2144         while (extendFrom != vel->end() &&
2145                 (*extendFrom)->getViewAbsoluteTime() < newTime)  {
2146             if ((*extendFrom)->event()->isa(Note::EventType)) {
2147                 es->addEvent((*extendFrom)->event());
2148             }
2149             ++extendFrom;
2150         }
2151 
2152     } else { // remove an event
2153 
2154         EventSelection::eventcontainer::iterator i =
2155             es->getSegmentEvents().begin();
2156 
2157         std::vector<Event *> toErase;
2158 
2159         while (i != es->getSegmentEvents().end() &&
2160                 (*i)->getAbsoluteTime() < newTime) {
2161             toErase.push_back(*i);
2162             ++i;
2163         }
2164 
2165         for (unsigned int j = 0; j < toErase.size(); ++j) {
2166             es->removeEvent(toErase[j]);
2167         }
2168     }
2169 
2170     setSelection(es, true);
2171 }
2172 
2173 
2174 void
slotEditAddKeySignature()2175 MatrixView::slotEditAddKeySignature()
2176 {
2177     Segment *segment = getCurrentSegment();
2178     timeT insertionTime = getInsertionTime();
2179     Clef clef = segment->getClefAtTime(insertionTime);
2180     Key key = AnalysisHelper::guessKeyForSegment(insertionTime, segment);
2181 
2182     MatrixScene *scene = m_matrixWidget->getScene();
2183     if (!scene) return;
2184 
2185     NotePixmapFactory npf;
2186 
2187     KeySignatureDialog dialog(this,
2188                               &npf,
2189                               clef,
2190                               key,
2191                               true,
2192                               true,
2193                               tr("Estimated key signature shown"));
2194 
2195     if (dialog.exec() == QDialog::Accepted &&
2196         dialog.isValid()) {
2197 
2198         KeySignatureDialog::ConversionType conversion =
2199             dialog.getConversionType();
2200 
2201         bool transposeKey = dialog.shouldBeTransposed();
2202         bool applyToAll = dialog.shouldApplyToAll();
2203         bool ignorePercussion = dialog.shouldIgnorePercussion();
2204 
2205         if (applyToAll) {
2206             CommandHistory::getInstance()->addCommand(
2207                     new MultiKeyInsertionCommand(
2208                             getDocument(),
2209                             insertionTime, dialog.getKey(),
2210                             conversion == KeySignatureDialog::Convert,
2211                             conversion == KeySignatureDialog::Transpose,
2212                             transposeKey,
2213                             ignorePercussion));
2214         } else {
2215             CommandHistory::getInstance()->addCommand(
2216                     new KeyInsertionCommand(*segment,
2217                                             insertionTime,
2218                                             dialog.getKey(),
2219                                             conversion == KeySignatureDialog::Convert,
2220                                             conversion == KeySignatureDialog::Transpose,
2221                                             transposeKey,
2222                                             false));
2223         }
2224     }
2225 }
2226 
2227 void
slotJogLeft()2228 MatrixView::slotJogLeft()
2229 {
2230     EventSelection *selection = getSelection();
2231     if (!selection) return ;
2232 
2233     MATRIX_DEBUG << "MatrixView::slotJogLeft";
2234 
2235     bool useNotationTimings = false;
2236 
2237     CommandHistory::getInstance()->addCommand(new MoveCommand
2238                                               (*getCurrentSegment(),
2239                                               -Note(Note::Demisemiquaver).getDuration(),
2240                                               useNotationTimings,
2241                                               *selection));
2242 }
2243 
2244 void
slotJogRight()2245 MatrixView::slotJogRight()
2246 {
2247     EventSelection *selection = getSelection();
2248     if (!selection) return ;
2249 
2250     MATRIX_DEBUG << "MatrixView::slotJogRight";
2251 
2252     bool useNotationTimings = false;
2253 
2254     CommandHistory::getInstance()->addCommand(new MoveCommand
2255                                               (*getCurrentSegment(),
2256                                               Note(Note::Demisemiquaver).getDuration(),
2257                                               useNotationTimings,
2258                                               *selection));
2259 }
2260 
2261 
2262 }
2263