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