1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A MIDI and audio sequencer and musical notation editor.
6     Copyright 2000-2021 the Rosegarden development team.
7 
8     Other copyrights also apply to some parts of this work.  Please
9     see the AUTHORS file and individual file headers for details.
10 
11     This program is free software; you can redistribute it and/or
12     modify it under the terms of the GNU General Public License as
13     published by the Free Software Foundation; either version 2 of the
14     License, or (at your option) any later version.  See the file
15     COPYING included with this distribution for more information.
16 */
17 
18 #define RG_MODULE_STRING "[NotationView]"
19 #define RG_NO_DEBUG_PRINT 1
20 
21 #include "NotationView.h"
22 
23 #include "NotationWidget.h"
24 #include "NotationScene.h"
25 #include "NotationCommandRegistry.h"
26 #include "NoteStyleFactory.h"
27 #include "NoteFontFactory.h"
28 #include "NotationStrings.h"
29 #include "NoteRestInserter.h"
30 #include "NotationSelector.h"
31 #include "HeadersGroup.h"
32 #include "NotationHLayout.h"
33 #include "NotationStaff.h"
34 #include "NotationElement.h"
35 #include "NotePixmapFactory.h"
36 
37 #include "document/RosegardenDocument.h"
38 #include "document/CommandHistory.h"
39 
40 #include "misc/ConfigGroups.h"
41 #include "misc/Debug.h"
42 
43 #include "base/AnalysisTypes.h"
44 #include "base/BaseProperties.h"
45 #include "base/Clipboard.h"
46 #include "base/CompositionTimeSliceAdapter.h"
47 #include "base/Controllable.h"
48 #include "base/Device.h"
49 #include "base/Event.h"
50 #include "base/Instrument.h"
51 #include "base/MidiDevice.h"
52 #include "base/MidiTypes.h"
53 #include "base/NotationQuantizer.h"
54 #include "base/NotationRules.h"
55 #include "base/NotationTypes.h"
56 #include "base/Selection.h"
57 #include "base/SoftSynthDevice.h"
58 #include "base/Studio.h"
59 #include "base/TriggerSegment.h"
60 #include "base/parameterpattern/ParameterPattern.h"
61 
62 #include "commands/edit/CopyCommand.h"
63 #include "commands/edit/CutCommand.h"
64 #include "commands/edit/CutAndCloseCommand.h"
65 #include "commands/edit/EraseCommand.h"
66 #include "commands/edit/PasteEventsCommand.h"
67 #include "commands/edit/InsertTriggerNoteCommand.h"
68 #include "commands/edit/SetTriggerCommand.h"
69 #include "commands/edit/ClearTriggersCommand.h"
70 #include "commands/edit/ChangeVelocityCommand.h"
71 #include "commands/edit/RescaleCommand.h"
72 #include "commands/edit/TransposeCommand.h"
73 #include "commands/edit/InvertCommand.h"
74 #include "commands/edit/RetrogradeCommand.h"
75 #include "commands/edit/RetrogradeInvertCommand.h"
76 #include "commands/edit/MoveCommand.h"
77 #include "commands/edit/EventQuantizeCommand.h"
78 #include "commands/edit/SetLyricsCommand.h"
79 #include "commands/edit/EventEditCommand.h"
80 #include "commands/edit/CollapseNotesCommand.h"
81 #include "commands/edit/AddDotCommand.h"
82 #include "commands/edit/SetNoteTypeCommand.h"
83 #include "commands/edit/SelectAddEvenNotesCommand.h"
84 #include "commands/edit/MaskTriggerCommand.h"
85 #include "commands/edit/PlaceControllersCommand.h"
86 
87 #include "commands/notation/AdoptSegmentCommand.h"
88 #include "commands/notation/InterpretCommand.h"
89 #include "commands/notation/ClefInsertionCommand.h"
90 #include "commands/notation/GeneratedRegionInsertionCommand.h"
91 #include "commands/notation/KeyInsertionCommand.h"
92 #include "commands/notation/MultiKeyInsertionCommand.h"
93 #include "commands/notation/SustainInsertionCommand.h"
94 #include "commands/notation/TupletCommand.h"
95 #include "commands/notation/TextInsertionCommand.h"
96 #include "commands/notation/KeyInsertionCommand.h"
97 #include "commands/notation/EraseEventCommand.h"
98 #include "commands/notation/NormalizeRestsCommand.h"
99 #include "commands/notation/CycleSlashesCommand.h"
100 
101 #include "commands/segment/AddTempoChangeCommand.h"
102 #include "commands/segment/AddTimeSignatureAndNormalizeCommand.h"
103 #include "commands/segment/AddTimeSignatureCommand.h"
104 #include "commands/segment/AddLayerCommand.h"
105 #include "commands/segment/CutToTriggerSegmentCommand.h"
106 #include "commands/segment/SegmentTransposeCommand.h"
107 #include "commands/segment/SegmentSyncCommand.h"
108 
109 #include "gui/dialogs/PasteNotationDialog.h"
110 #include "gui/dialogs/InterpretDialog.h"
111 #include "gui/dialogs/MakeOrnamentDialog.h"
112 #include "gui/dialogs/UseOrnamentDialog.h"
113 #include "gui/dialogs/ClefDialog.h"
114 #include "gui/dialogs/GeneratedRegionDialog.h"
115 #include "gui/dialogs/LilyPondOptionsDialog.h"
116 #include "gui/dialogs/SelectDialog.h"
117 #include "gui/dialogs/EventFilterDialog.h"
118 #include "gui/dialogs/EventParameterDialog.h"
119 #include "gui/dialogs/PitchBendSequenceDialog.h"
120 #include "gui/dialogs/KeySignatureDialog.h"
121 #include "gui/dialogs/IntervalDialog.h"
122 #include "gui/dialogs/TupletDialog.h"
123 #include "gui/dialogs/InsertTupletDialog.h"
124 #include "gui/dialogs/RescaleDialog.h"
125 #include "gui/dialogs/TempoDialog.h"
126 #include "gui/dialogs/TimeSignatureDialog.h"
127 #include "gui/dialogs/QuantizeDialog.h"
128 #include "gui/dialogs/LyricEditDialog.h"
129 #include "gui/dialogs/AboutDialog.h"
130 #include "gui/dialogs/EventEditDialog.h"
131 #include "gui/dialogs/TextEventDialog.h"
132 #include "gui/dialogs/SimpleEventEditDialog.h"
133 #include "gui/dialogs/ConfigureDialog.h"
134 
135 #include "gui/dialogs/CheckForParallelsDialog.h"
136 
137 #include "gui/general/EditTempoController.h"
138 #include "gui/general/IconLoader.h"
139 #include "gui/general/LilyPondProcessor.h"
140 #include "gui/general/PresetHandlerDialog.h"
141 #include "gui/general/ClefIndex.h"
142 #include "gui/general/ThornStyle.h"
143 #include "gui/rulers/ControlRulerWidget.h"
144 #include "gui/widgets/TmpStatusMsg.h"
145 
146 #include "gui/application/RosegardenMainWindow.h"
147 #include "gui/application/RosegardenMainViewWidget.h"
148 
149 #include "gui/editors/parameters/TrackParameterBox.h"
150 
151 #include "document/io/LilyPondExporter.h"
152 
153 #include <QWidget>
154 #include <QAction>
155 #include <QActionGroup>
156 #include <QDir>
157 #include <QMenu>
158 #include <QMessageBox>
159 #include <QSettings>
160 #include <QSharedPointer>
161 #include <QTemporaryFile>
162 #include <QToolBar>
163 #include <QInputDialog>
164 #include <QStatusBar>
165 #include <QToolButton>
166 #include <QUrl>
167 #include <QDesktopServices>
168 #include <QApplication>
169 
170 #include <algorithm>
171 #include <set>
172 
173 #define CALL_MEMBER_FN(OBJECT,PTRTOMEMBER)  ((OBJECT).*(PTRTOMEMBER))
174 
175 namespace
176 {
invertPixmap(QPixmap pmap)177     QPixmap invertPixmap(QPixmap pmap)
178     {
179         QImage img = pmap.toImage().convertToFormat(QImage::Format_ARGB32);
180 
181         for (int y = 0; y < img.height(); ++y) {
182             for (int x = 0; x < img.width(); ++x) {
183 
184                 QRgb rgba = img.pixel(x, y);
185                 QColor colour = QColor
186                     (qRed(rgba), qGreen(rgba), qBlue(rgba), qAlpha(rgba));
187 
188                 const int alpha = colour.alpha();
189 
190                 // If this is a shade of gray that is relatively opaque,
191                 // invert the V component.
192                 if (colour.saturation() < 5 && colour.alpha() > 10) {
193                     colour.setHsv(colour.hue(),
194                                   colour.saturation(),
195                                   255 - colour.value());
196                     // ??? This should be unnecessary as it is merely being
197                     //     set to itself.
198                     colour.setAlpha(alpha);
199 
200                     img.setPixel(x, y, colour.rgba());
201                 }
202             }
203         }
204 
205         pmap = QPixmap::fromImage(img);
206 
207         return pmap;
208     }
209 }
210 
211 namespace Rosegarden
212 {
213 
214 using namespace Accidentals;
215 
NotationView(RosegardenDocument * doc,std::vector<Segment * > segments,QWidget * parent)216 NotationView::NotationView(RosegardenDocument *doc,
217                            std::vector<Segment *> segments,
218                            QWidget *parent) :
219     EditViewBase(doc, segments, parent),
220     m_document(doc),
221     m_durationMode(InsertingRests),
222     m_durationPressed(nullptr),
223     m_accidentalPressed(nullptr),
224     m_selectionCounter(nullptr),
225     m_insertModeLabel(nullptr),
226     m_annotationsLabel(nullptr),
227     m_lilyPondDirectivesLabel(nullptr),
228     m_currentNotePixmap(nullptr),
229     m_hoveredOverNoteName(nullptr),
230     m_hoveredOverAbsoluteTime(nullptr),
231     m_fontCombo(nullptr),
232     m_fontSizeCombo(nullptr),
233     m_spacingCombo(nullptr),
234     m_segments(segments),
235     m_oldPointerPosition(0),
236     m_cursorPosition(0)
237 {
238     m_notationWidget = new NotationWidget();
239     setCentralWidget(m_notationWidget);
240 
241     m_notationWidget->suspendLayoutUpdates();
242 
243     setWidgetSegments();
244 
245     // connect the editElement signal from NotationSelector, relayed through
246     // NotationWidget to be acted upon here in NotationView
247     connect(m_notationWidget, &NotationWidget::editElement,
248             this, &NotationView::slotEditElement);
249 
250     // Many actions are created here
251     m_commandRegistry = new NotationCommandRegistry(this);
252 
253     setupActions();
254     createMenusAndToolbars("notation.rc");
255     slotUpdateMenuStates();
256     slotTestClipboard();
257 
258     setWindowIcon(IconLoader::loadPixmap("window-notation"));
259 
260     connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
261             this, SLOT(slotUpdateMenuStates()));
262 
263     connect(m_notationWidget->getScene(), SIGNAL(selectionChanged()),
264             this, SLOT(slotUpdateMenuStates()));
265 
266     //Initialize NoteRestInserter and DurationToolbar
267     initializeNoteRestInserter();
268 
269     // Determine default action stolen from MatrixView.cpp
270     // Toggle the desired tool off and then trigger it on again, to
271     // make sure its signal is called at least once (as would not
272     // happen if the tool was on by default otherwise)
273     QAction *toolAction = nullptr;
274     if (!m_notationWidget->segmentsContainNotes()) {
275         toolAction = findAction("draw");
276     } else {
277         toolAction = findAction("select");
278     }
279     if (toolAction) {
280         NOTATION_DEBUG << "initial state for action '" << toolAction->objectName() << "' is " << toolAction->isChecked();
281         if (toolAction->isChecked()) toolAction->toggle();
282         NOTATION_DEBUG << "newer state for action '" << toolAction->objectName() << "' is " << toolAction->isChecked();
283         toolAction->trigger();
284         NOTATION_DEBUG << "newest state for action '" << toolAction->objectName() << "' is " << toolAction->isChecked();
285     }
286 
287     // Set display configuration
288     bool visible;
289     QSettings settings;
290 
291     m_Thorn = ThornStyle::isEnabled();
292 
293     settings.beginGroup(NotationViewConfigGroup);
294 
295     // Set font size for single or multiple staffs (hopefully this happens
296     // before we've drawn anything, so there's no penalty changing it)
297     m_fontSize = NoteFontFactory::getDefaultSize(m_fontName);
298     if (m_notationWidget->getScene()->getVisibleStaffCount() > 1) {
299         m_fontSize = settings.value("multistaffnotesize", 6).toInt();
300         //RG_DEBUG << "setting multi staff size to " << size;
301     } else {
302         m_fontSize = settings.value("singlestaffnotesize", 8).toInt();
303         //RG_DEBUG << "setting single staff size to " << size;
304     }
305     m_notationWidget->slotSetFontSize(m_fontSize);
306 
307     //Update Font Size pulldown menu
308     QString action = QString("note_font_size_%1").arg(m_fontSize);
309     findAction(action)->setChecked(true);
310 
311 
312     // Set initial notation layout mode
313     int layoutMode = settings.value("layoutmode", 0).toInt();
314     switch (layoutMode) {
315         case 0 :
316             findAction("linear_mode")->setChecked(true);
317             findAction("continuous_page_mode")->setChecked(false);
318             findAction("multi_page_mode")->setChecked(false);
319             slotLinearMode();
320         break;
321         case 1 :
322             findAction("linear_mode")->setChecked(false);
323             findAction("continuous_page_mode")->setChecked(true);
324             findAction("multi_page_mode")->setChecked(false);
325             slotContinuousPageMode();
326         break;
327         case 2 :
328             findAction("linear_mode")->setChecked(false);
329             findAction("continuous_page_mode")->setChecked(false);
330             findAction("multi_page_mode")->setChecked(true);
331             slotMultiPageMode();
332         break;
333     }
334 
335     // Set initial visibility of chord name ruler, ...
336     visible = settings.value("Chords ruler shown",
337                           findAction("show_chords_ruler")->isChecked()
338                          ).toBool();
339     findAction("show_chords_ruler")->setChecked(visible);
340     m_notationWidget->setChordNameRulerVisible(visible);
341 
342     // ... raw note ruler, ...
343     visible = settings.value("Raw note ruler shown",
344                           findAction("show_raw_note_ruler")->isChecked()
345                          ).toBool();
346     findAction("show_raw_note_ruler")->setChecked(visible);
347     m_notationWidget->setRawNoteRulerVisible(visible);
348 
349     // ... and tempo ruler.
350     visible = settings.value("Tempo ruler shown",
351                           findAction("show_tempo_ruler")->isChecked()
352                          ).toBool();
353     findAction("show_tempo_ruler")->setChecked(visible);
354     m_notationWidget->setTempoRulerVisible(visible);
355 
356     settings.endGroup();
357 
358     if (segments.size() > 1) {
359         enterActionState("have_multiple_staffs");
360     } else {
361         leaveActionState("have_multiple_staffs");
362     }
363 
364     // We never start with any adopted segments so we don't need to
365     // test for this.
366     leaveActionState("focus_adopted_segment");
367 
368     initLayoutToolbar();
369     initRulersToolbar();
370     initStatusBar();
371 
372     slotUpdateWindowTitle();
373     connect(m_document, &RosegardenDocument::documentModified,
374             this, &NotationView::slotUpdateWindowTitle);
375 
376     connect(m_notationWidget, &NotationWidget::showContextHelp,
377             this, &NotationView::slotShowContextHelp);
378 
379     // Restore window geometry and toolbar/dock state
380     settings.beginGroup(WindowGeometryConfigGroup);
381     this->restoreGeometry(settings.value("Notation_View_Geometry").toByteArray());
382     this->restoreState(settings.value("Notation_View_State").toByteArray());
383     settings.endGroup();
384 
385     connect(m_notationWidget, &NotationWidget::sceneNeedsRebuilding,
386             this, &NotationView::slotRegenerateScene);
387 
388     // Set the rewind and fast-forward buttons for auto-repeat.
389     enableAutoRepeat("Transport Toolbar", "playback_pointer_back_bar");
390     enableAutoRepeat("Transport Toolbar", "playback_pointer_forward_bar");
391     enableAutoRepeat("Transport Toolbar", "cursor_back");
392     enableAutoRepeat("Transport Toolbar", "cursor_forward");
393 
394     m_notationWidget->resumeLayoutUpdates();
395 
396     // Connection to update the "Show staff headers" check box in the menu
397     // (Must be done before setting the initial visibility of the headers)
398     connect(m_notationWidget, &NotationWidget::headersVisibilityChanged,
399             this,  &NotationView::slotCheckShowHeadersMenu);
400 
401     // Set initial visibility of staff headers.
402     // (Could not be done earlier because both view size and headers size are
403     //  needed to know what should be done when the "show when needed" option
404     //  is selected).
405     settings.beginGroup(NotationViewConfigGroup);
406     switch (settings.value("shownotationheader",
407                            HeadersGroup::DefaultShowMode).toInt()) {
408       case HeadersGroup::ShowNever :
409           m_notationWidget->setHeadersVisible(false);
410           break;
411       case HeadersGroup::ShowWhenNeeded :
412           m_notationWidget->setHeadersVisibleIfNeeded();
413           break;
414       case HeadersGroup::ShowAlways :
415           m_notationWidget->setHeadersVisible(true);
416           break;
417       default :
418           RG_WARNING << "NotationView: settings.value(\"shownotationheader\") "
419                     << "returned an unexpected value. This is a bug.";
420     }
421     settings.endGroup();
422 
423     // Show the pointer as soon as notation editor opens
424     m_notationWidget->updatePointerPosition(false);
425 
426     readOptions();
427 
428     m_notationWidget->scrollToTopLeft();
429 }
430 
~NotationView()431 NotationView::~NotationView()
432 {
433     NOTATION_DEBUG << "Deleting notation view";
434     m_notationWidget->clearAll();
435 
436     // I own the m_adoptedSegments segments.
437     for (SegmentVector::iterator it = m_adoptedSegments.begin();
438          it != m_adoptedSegments.end(); ++it) {
439         delete (*it);
440     }
441 
442     delete m_commandRegistry;
443 }
444 
445 bool
hasSegment(Segment * seg) const446 NotationView::hasSegment(Segment * seg) const
447 {
448     for (std::vector<Segment *>::const_iterator it = m_segments.begin(); it != m_segments.end(); ++it) {
449         if ((*it) == seg) return true;
450     }
451     return false;
452 }
453 
454 void
closeEvent(QCloseEvent * event)455 NotationView::closeEvent(QCloseEvent *event)
456 {
457     // Save window geometry and toolbar/dock state
458     QSettings settings;
459     settings.beginGroup(WindowGeometryConfigGroup);
460     RG_DEBUG << "storing window geometry for notation view";
461     settings.setValue("Notation_View_Geometry", this->saveGeometry());
462     settings.setValue("Notation_View_State", this->saveState());
463     settings.endGroup();
464 
465     QWidget::closeEvent(event);
466 }
467 
468 // Adopt a segment that doesn't live in Composition.  Take ownership
469 // of s; it is caller's responsibility to guarantee that s is not
470 // owned by something else.
471 void
472 NotationView::
adoptSegment(Segment * s)473 adoptSegment(Segment *s)
474 {
475     m_adoptedSegments.push_back(s);
476     // We have at least 2 staffs.
477     enterActionState("have_multiple_staffs");
478     slotRegenerateScene();
479     slotUpdateMenuStates();
480 }
481 
482 // Unadopt a segment that we adopted earlier.  If s was not adopted
483 // earlier, do nothing.
484 void
485 NotationView::
unadoptSegment(Segment * s)486 unadoptSegment(Segment *s)
487 {
488     SegmentVector::iterator found = findAdopted(s);
489 
490     if (found != m_adoptedSegments.end()) {
491         m_adoptedSegments.erase(found);
492         if (m_adoptedSegments.size() + m_segments.size() == 1)
493             { leaveActionState("have_multiple_staffs"); }
494         slotRegenerateScene();
495         slotUpdateMenuStates();
496     }
497 }
498 
499 // Adopt a segment that does live in Composition. Do not take ownership
500 // of s;
501 void
502 NotationView::
adoptCompositionSegment(Segment * s)503 adoptCompositionSegment(Segment *s)
504 {
505     if (std::find(m_segments.begin(),
506                   m_segments.end(), s) != m_segments.end()) {
507         // we already have it
508         return;
509     }
510     Composition& comp = m_doc->getComposition();
511     if (comp.findSegment(s) == comp.end()) {
512         // segment is not in composition
513         RG_WARNING << "segment" << s << "not found in composition";
514         return;
515     }
516     m_segments.push_back(s);
517     // re-invoke setSegments with the amended m_segments
518     setWidgetSegments();
519 }
520 
521 // Unadopt a segment that we adopted earlier.  If s was not adopted
522 // earlier, do nothing.
523 void
524 NotationView::
unadoptCompositionSegment(Segment * s)525 unadoptCompositionSegment(Segment *s)
526 {
527     SegmentVector::iterator it =
528         std::find(m_segments.begin(), m_segments.end(), s);
529     if (it == m_segments.end()) {
530         // we do not have it
531         return;
532     }
533     Composition& comp = m_doc->getComposition();
534     if (comp.findSegment(s) == comp.end()) {
535         // segment is not in composition
536         RG_WARNING << "segment" << s << "not found in composition";
537         return;
538     }
539     m_segments.erase(it);
540     slotUpdateMenuStates(); // single <-> multiple
541 }
542 
543 NotationView::SegmentVector::iterator
544 NotationView::
findAdopted(Segment * s)545 findAdopted(Segment *s)
546 {
547     return
548         std::find(m_adoptedSegments.begin(), m_adoptedSegments.end(), s);
549 }
550 
551 
552 // Set NotationWidget's segments.
553 void
setWidgetSegments()554 NotationView::setWidgetSegments()
555 {
556     SegmentVector allSegments = m_segments;
557     allSegments.insert(allSegments.end(),
558                        m_adoptedSegments.begin(),
559                        m_adoptedSegments.end());
560     m_notationWidget->setSegments(m_document, allSegments);
561     // Reconnect because there's a new scene.
562     connect(m_notationWidget->getScene(), SIGNAL(selectionChanged()),
563             this, SLOT(slotUpdateMenuStates()));
564 }
565 
566 void
setupActions()567 NotationView::setupActions()
568 {
569     //setup actions common to all views.
570     setupBaseActions(true);
571 
572     //"file" MenuBar menu
573     // "file_save"
574     // Created in EditViewBase::setupActions() via creatAction()
575 
576     createAction("file_print_lilypond", SLOT(slotPrintLilyPond()));
577     createAction("file_preview_lilypond", SLOT(slotPreviewLilyPond()));
578 
579     // "file_close"
580     // Created in EditViewBase::setupActions() via creatAction()
581 
582     // "edit" MenuBar menu
583     // "edit_undo"
584     // Created in EditViewBase::setupActions() via creatAction()
585 
586     // "edit_redo"
587     // Created in EditViewBase::setupActions() via creatAction()
588 
589     createAction("cut_and_close", SLOT(slotEditCutAndClose()));
590     createAction("general_paste", SLOT(slotEditGeneralPaste()));
591     createAction("delete", SLOT(slotEditDelete()));
592     createAction("move_events_up_staff",
593                  SLOT(slotMoveEventsUpStaff()));
594     createAction("general_move_events_up_staff",
595                  SLOT(slotMoveEventsUpStaffInteractive()));
596     createAction("move_events_down_staff",
597                  SLOT(slotMoveEventsDownStaff()));
598     createAction("general_move_events_down_staff",
599                  SLOT(slotMoveEventsDownStaffInteractive()));
600     createAction("select_from_start", SLOT(slotEditSelectFromStart()));
601     createAction("select_to_end", SLOT(slotEditSelectToEnd()));
602     createAction("select_whole_staff", SLOT(slotEditSelectWholeStaff()));
603     createAction("clear_selection", SLOT(slotClearSelection()));
604     createAction("search_select", SLOT(slotSearchSelect()));
605     createAction("filter_selection", SLOT(slotFilterSelection()));
606     createAction("select_evenly_spaced_notes", SLOT(slotSelectEvenlySpacedNotes()));
607     createAction("expression_sequence", SLOT(slotExpressionSequence()));
608     createAction("pitch_bend_sequence", SLOT(slotPitchBendSequence()));
609     createAction("controller_sequence", SLOT(slotControllerSequence()));
610 
611 
612     //"view" MenuBar menu
613     // "note_font_actionmenu" subMenu
614     // Custom Code. Coded below.
615 
616     // "note_font_size_actionmenu" subMenu
617     // Custom Code. Coded below.
618 
619     // "stretch_actionmenu" subMenu
620     // Custom Code. Coded below.
621     // Code deactivated.
622 
623     // "proportion_actionmenu" subMenu
624     // Custom Code. Coded below.
625     // Code deactivated.
626 
627     // "layout" submenu
628     createAction("add_layer", SLOT(slotAddLayer()));
629     createAction("magic_layer", SLOT(slotMagicLayer()));
630     createAction("linear_mode", SLOT(slotLinearMode()));
631     createAction("continuous_page_mode", SLOT(slotContinuousPageMode()));
632     createAction("multi_page_mode", SLOT(slotMultiPageMode()));
633 
634     createAction("lyric_editor", SLOT(slotEditLyrics()));
635     createAction("show_track_headers", SLOT(slotShowHeadersGroup()));
636 
637     //"document" Menubar menu
638     createAction("add_tempo", SLOT(slotAddTempo()));
639     createAction("add_time_signature", SLOT(slotAddTimeSignature()));
640     createAction("check_for_parallels", SLOT(slotCheckForParallels()));
641 
642     //"segment" Menubar menu
643     // "open-with" subMenu
644     // Created in EditViewBase::setupActions() via creatAction()
645 
646     createAction("add_clef", SLOT(slotEditAddClef()));
647     //uncomment this when we implement linked segment transposition
648     //createAction("add_clef_this_link_only", SLOT(slotEditAddClefLinkOnly()));
649     createAction("add_key_signature", SLOT(slotEditAddKeySignature()));
650     createAction("add_sustain_down", SLOT(slotEditAddSustainDown()));
651     createAction("add_sustain_up", SLOT(slotEditAddSustainUp()));
652 
653     // "set_segment_start"
654     // Created in EditViewBase::setupActions() via creatAction()
655 
656     // "set_segment_duration"
657     // Created in EditViewBase::setupActions() via creatAction()
658 
659     createAction("transpose_segment", SLOT(slotEditTranspose()));
660     createAction("switch_preset", SLOT(slotEditSwitchPreset()));
661     //createAction("create_anacrusis", SLOT(slotCreateAnacrusis()));
662 
663     //"Notes" Menubar menu
664 
665     // "Marks" subMenu
666     //Created in Constructor via NotationCommandRegistry()
667     //with AddMarkCommand::registerCommand()
668     //with RemoveMarksCommand::registerCommand()
669 
670     // "ornaments" subMenu
671     createAction("use_ornament", SLOT(slotUseOrnament()));
672     createAction("make_ornament", SLOT(slotMakeOrnament()));
673     createAction("remove_ornament", SLOT(slotRemoveOrnament()));
674     createAction("edit_ornament_inline", SLOT(slotEditOrnamentInline()));
675     createAction("show_ornament_expansion", SLOT(slotShowOrnamentExpansion()));
676     createAction("mask_ornament", SLOT(slotMaskOrnament()));
677     createAction("unmask_ornament", SLOT(slotUnmaskOrnament()));
678 
679     // "Fingering" subMenu
680     // Created in Constructor via NotationCommandRegistry()
681     // with AddFingeringMarkCommand::registerCommand()
682     // with RemoveFingeringMarksCommand::registerCommand()
683 
684     // "Slashes" subMenu
685     // Created in Constructor via NotationCommandRegistry()
686     // with AddSlashesCommand::registerCommand()
687 
688     // "note_style_actionmenu" subMenu
689     // Created in Constructor via NotationCommandRegistry()
690     // with ChangeStyleCommand::registerCommand()
691     // actionCreate really should be a custon code. Oh, well.
692 
693 
694     // "Respell" subMenu
695     // Created in Constructor via NotationCommandRegistry()
696     // with RespellCommand::registerCommand()
697 
698     // "stem_up"
699     // Created in Constructor via NotationCommandRegistry()
700     // with ChangeStemsCommand::registerCommand()
701 
702     // "stem_down"
703     // Created in Constructor via NotationCommandRegistry()
704     // with ChangeStemsCommand::registerCommand()
705 
706     // "restore_stems"
707     // Created in Constructor via NotationCommandRegistry()
708     // with RestoreStemsCommand::registerCommand();
709 
710     //"Phrase" Menubar menu
711     // "make_chord"
712     // Created in Constructor via NotationCommandRegistry()
713     // with MakeChordCommand::registerCommand();
714 
715     // "beam"
716     // Created in Constructor via NotationCommandRegistry()
717     // with BeamCommand::registerCommand();
718 
719     // "auto_beam"
720     // Created in Constructor via NotationCommandRegistry()
721     // with AutoBeamCommand::registerCommand();
722 
723     // "break_group"
724     // Created in Constructor via NotationCommandRegistry()
725     // with BreakCommand::registerCommand();
726 
727     // "remove_indications"
728 
729     createAction("simple_tuplet", SLOT(slotGroupSimpleTuplet()));
730     createAction("tuplet", SLOT(slotGroupGeneralTuplet()));
731 
732     //Where is "break_tuplet" created?
733     //"slur" & "phrasing_slur" are created in AddIndicationCommand
734 
735     //"Slurs" subMenu
736     //where are "restore_slurs", "slurs_above", "slurs_below" created?
737 
738     //Where are "tie_notes", "untie_notes", created?
739 
740     //"Ties" subMenu
741     //"restore_ties", "ties_above", & "ties_below" created?
742 
743     //"crescendo" & "decrescendo" are created in AddIndicationCommand
744 
745     //"octaves" subMenu
746     //All are created in AddIndicationCommand
747 
748     //Actions first appear in "Adjust" Menubar menu
749 
750     //"rests" subMenu
751     createAction("normalize_rests", SLOT(slotTransformsNormalizeRests()));
752     //Where is "collapse_rests_aggresively" created?
753 
754     //"transform_notes" subMenu
755     createAction("collapse_notes", SLOT(slotTransformsCollapseNotes()));
756     //Where are "make_notes_viable" & "de_counterpoint" created?
757 
758     //Quantitize subMenu
759     createAction("quantize", SLOT(slotTransformsQuantize()));
760     //Where are "fix_quantization" & "remove_quantization" created?
761 
762     createAction("interpret", SLOT(slotTransformsInterpret()));
763 
764     //"Rescale" subMenu
765     createAction("halve_durations", SLOT(slotHalveDurations()));
766     createAction("double_durations", SLOT(slotDoubleDurations()));
767     createAction("rescale", SLOT(slotRescale()));
768 
769     //"Transpose" subMenu
770     createAction("transpose_up", SLOT(slotTransposeUp()));
771     createAction("transpose_down", SLOT(slotTransposeDown()));
772     createAction("transpose_up_octave", SLOT(slotTransposeUpOctave()));
773     createAction("transpose_down_octave", SLOT(slotTransposeDownOctave()));
774     createAction("general_transpose", SLOT(slotTranspose()));
775     createAction("general_diatonic_transpose", SLOT(slotDiatonicTranspose()));
776 
777     //"Convert" subMenu
778     createAction("invert", SLOT(slotInvert()));
779     createAction("retrograde", SLOT(slotRetrograde()));
780     createAction("retrograde_invert", SLOT(slotRetrogradeInvert()));
781 
782     //"velocities" subMenu
783     createAction("velocity_up", SLOT(slotVelocityUp()));
784     createAction("velocity_down", SLOT(slotVelocityDown()));
785     createAction("set_velocities", SLOT(slotSetVelocities()));
786 
787     //"fine_positioning" subMenu
788     //Where are "fine_position_restore", "fine_position_left",
789     //"fine_position_right", "fine_position_up" &
790     //"fine_position_down" created?
791 
792     //"fine_timing" subMenu
793     createAction("jog_left", SLOT(slotJogLeft()));
794     createAction("jog_right", SLOT(slotJogRight()));
795 
796     //"visibility" subMenu
797     //Where are "make_invisible" & "make_visible" created?
798 
799     //"controllers" Menubar menu
800     createAction("copy_controllers",  SLOT(slotEditCopyControllers()));
801     createAction("cut_controllers",   SLOT(slotEditCutControllers()));
802     createAction("set_controllers",   SLOT(slotSetControllers()));
803     createAction("place_controllers", SLOT(slotPlaceControllers()));
804 
805     //Actions first appear in "Tools" Menubar menu
806     createAction("select", SLOT(slotSetSelectTool()));
807     createAction("selectnoties", SLOT(slotSetSelectNoTiesTool()));
808     createAction("erase", SLOT(slotSetEraseTool()));
809     createAction("draw", SLOT(slotSetNoteRestInserter()));
810 
811     // These actions do as their names imply, and in this case, the toggle will
812     // call one or the other of these
813     // These rely on .rc script keeping the right state visible
814     createAction("switch_to_rests", SLOT(slotSwitchToRests()));
815     createAction("switch_to_notes", SLOT(slotSwitchToNotes()));
816 
817     // These actions always just pass straight to the toggle.
818     // These rely on .rc script keeping the right state visible
819     createAction("switch_dots_on", SLOT(slotToggleDot()));
820     createAction("switch_dots_off", SLOT(slotToggleDot()));
821 
822     // Menu uses now "Switch to Notes", "Switch to Rests" and "Durations".
823     createAction("duration_breve", SLOT(slotNoteAction()));
824     createAction("duration_semibreve", SLOT(slotNoteAction()));
825     createAction("duration_minim", SLOT(slotNoteAction()));
826     createAction("duration_crotchet", SLOT(slotNoteAction()));
827     createAction("duration_quaver", SLOT(slotNoteAction()));
828     createAction("duration_semiquaver", SLOT(slotNoteAction()));
829     createAction("duration_demisemi", SLOT(slotNoteAction()));
830     createAction("duration_hemidemisemi", SLOT(slotNoteAction()));
831     createAction("duration_dotted_breve", SLOT(slotNoteAction()));
832     createAction("duration_dotted_semibreve", SLOT(slotNoteAction()));
833     createAction("duration_dotted_minim", SLOT(slotNoteAction()));
834     createAction("duration_dotted_crotchet", SLOT(slotNoteAction()));
835     createAction("duration_dotted_quaver", SLOT(slotNoteAction()));
836     createAction("duration_dotted_semiquaver", SLOT(slotNoteAction()));
837     createAction("duration_dotted_demisemi", SLOT(slotNoteAction()));
838     createAction("duration_dotted_hemidemisemi", SLOT(slotNoteAction()));
839 
840     // since we can't create toolbars with disabled icons, and to avoid having
841     // to draw a lot of fancy icons for disabled durations, we have this dummy
842     // filler to keep spacing the same across all toolbars, and there have to
843     // two of them
844     // Without this handler, the action takes up no space on the toolbar.
845     createAction("dummy_1", SLOT(slotDummy1()));
846 
847     //"NoteTool" subMenu
848     //NEED to create action methods
849     createAction("breve", SLOT(slotNoteAction()));
850     createAction("semibreve", SLOT(slotNoteAction()));
851     createAction("minim", SLOT(slotNoteAction()));
852     createAction("crotchet", SLOT(slotNoteAction()));
853     createAction("quaver", SLOT(slotNoteAction()));
854     createAction("semiquaver", SLOT(slotNoteAction()));
855     createAction("demisemi", SLOT(slotNoteAction()));
856     createAction("hemidemisemi", SLOT(slotNoteAction()));
857     createAction("dotted_breve", SLOT(slotNoteAction()));
858     createAction("dotted_semibreve", SLOT(slotNoteAction()));
859     createAction("dotted_minim", SLOT(slotNoteAction()));
860     createAction("dotted_crotchet", SLOT(slotNoteAction()));
861     createAction("dotted_quaver", SLOT(slotNoteAction()));
862     createAction("dotted_semiquaver", SLOT(slotNoteAction()));
863     createAction("dotted_demisemi", SLOT(slotNoteAction()));
864     createAction("dotted_hemidemisemi", SLOT(slotNoteAction()));
865 
866     //"RestTool" subMenu
867     //NEED to create action methods
868     createAction("rest_breve", SLOT(slotNoteAction()));
869     createAction("rest_semibreve", SLOT(slotNoteAction()));
870     createAction("rest_minim", SLOT(slotNoteAction()));
871     createAction("rest_crotchet", SLOT(slotNoteAction()));
872     createAction("rest_quaver", SLOT(slotNoteAction()));
873     createAction("rest_semiquaver", SLOT(slotNoteAction()));
874     createAction("rest_demisemi", SLOT(slotNoteAction()));
875     createAction("rest_hemidemisemi", SLOT(slotNoteAction()));
876     createAction("rest_dotted_breve", SLOT(slotNoteAction()));
877     createAction("rest_dotted_semibreve", SLOT(slotNoteAction()));
878     createAction("rest_dotted_minim", SLOT(slotNoteAction()));
879     createAction("rest_dotted_crotchet", SLOT(slotNoteAction()));
880     createAction("rest_dotted_quaver", SLOT(slotNoteAction()));
881     createAction("rest_dotted_semiquaver", SLOT(slotNoteAction()));
882     createAction("rest_dotted_demisemi", SLOT(slotNoteAction()));
883     createAction("rest_dotted_hemidemisemi", SLOT(slotNoteAction()));
884 
885     //"Accidentals" submenu
886     createAction("no_accidental", SLOT(slotNoAccidental()));
887     createAction("follow_accidental", SLOT(slotFollowAccidental()));
888     createAction("sharp_accidental", SLOT(slotSharp()));
889     createAction("flat_accidental", SLOT(slotFlat()));
890     createAction("natural_accidental", SLOT(slotNatural()));
891     createAction("double_sharp_accidental", SLOT(slotDoubleSharp()));
892     createAction("double_flat_accidental", SLOT(slotDoubleFlat()));
893 
894     //JAS "Clefs" subMenu
895     createAction("treble_clef", SLOT(slotClefAction()));
896     createAction("alto_clef", SLOT(slotClefAction()));
897     createAction("tenor_clef", SLOT(slotClefAction()));
898     createAction("bass_clef", SLOT(slotClefAction()));
899 
900     createAction("text", SLOT(slotText()));
901     createAction("guitarchord", SLOT(slotGuitarChord()));
902 
903     // "Symbols" (sub)Menu
904     createAction("add_segno", SLOT(slotSymbolAction()));
905     createAction("add_coda", SLOT(slotSymbolAction()));
906     createAction("add_breath", SLOT(slotSymbolAction()));
907 
908     //JAS "Move" subMenu
909     createAction("extend_selection_backward", SLOT(slotExtendSelectionBackward()));
910     createAction("extend_selection_forward", SLOT(slotExtendSelectionForward()));
911     createAction("preview_selection", SLOT(slotPreviewSelection()));
912     createAction("clear_loop", SLOT(slotClearLoop()));
913 
914     createAction("cursor_up_staff", SLOT(slotCurrentStaffUp()));
915     createAction("cursor_down_staff", SLOT(slotCurrentStaffDown()));
916     createAction("cursor_prior_segment", SLOT(slotCurrentSegmentPrior()));
917     createAction("cursor_next_segment", SLOT(slotCurrentSegmentNext()));
918     createAction("unadopt_segment", SLOT(slotUnadoptSegment()));
919 
920     //"Transport" subMenu
921     createAction("play", SIGNAL(play()));
922     createAction("stop", SIGNAL(stop()));
923     //Step Backward/Forward are protected signals
924     // so the pitch tracker (our derrived class) can see them
925     // Because they're protected, we'll connect them here.
926     createAction("cursor_back", SIGNAL(stepBackward()));
927     connect(this, &NotationView::stepBackward,
928             this, &NotationView::slotStepBackward);
929     createAction("cursor_forward", SIGNAL(stepForward()));
930     connect(this, &NotationView::stepForward,
931             this, &NotationView::slotStepForward);
932     createAction("playback_pointer_back_bar", SIGNAL(rewindPlayback()));
933     createAction("playback_pointer_forward_bar", SIGNAL(fastForwardPlayback()));
934     createAction("playback_pointer_start", SIGNAL(rewindPlaybackToBeginning()));
935     createAction("playback_pointer_end", SIGNAL(fastForwardPlaybackToEnd()));
936     createAction("toggle_solo", SLOT(slotToggleSolo()));
937     createAction("toggle_tracking", SLOT(slotToggleTracking()));
938     createAction("panic", SIGNAL(panic()));
939 
940     //"insert_note_actionmenu" coded below.
941 
942     createAction("chord_mode", SLOT(slotUpdateInsertModeStatus()));
943     createAction("triplet_mode", SLOT(slotUpdateInsertModeStatusTriplet()));
944     createAction("tuplet_mode", SLOT(slotUpdateInsertModeStatusTuplet()));
945     createAction("grace_mode", SLOT(slotUpdateInsertModeStatus()));
946     createAction("toggle_step_by_step", SLOT(slotToggleStepByStep()));
947 
948     /// YG: Only for debug
949      createAction("dump_staves", SLOT(slotDebugDump()));
950      createAction("dump_bardata", SLOT(slotBarDataDump()));
951 
952     createAction("manual", SLOT(slotHelp()));
953     createAction("tutorial", SLOT(slotTutorial()));
954     createAction("guidelines", SLOT(slotBugGuidelines()));
955     createAction("help_about_app", SLOT(slotHelpAbout()));
956     createAction("help_about_qt", SLOT(slotHelpAboutQt()));
957     createAction("donate", SLOT(slotDonate()));
958 
959     createAction("toggle_velocity_ruler", SLOT(slotToggleVelocityRuler()));
960     createAction("toggle_pitchbend_ruler", SLOT(slotTogglePitchbendRuler()));
961     createAction("add_control_ruler", "");
962 
963     createAction("cycle_slashes", SLOT(slotCycleSlashes()));
964 
965     createAction("interpret_activate", SLOT(slotInterpretActivate()));
966     // I don't think you can use <qt> in tooltips in .rc files, and this one
967     // wants formatting, so I pulled it out here.
968     findAction("interpret_activate")->setToolTip(tr("<qt><p>Apply the interpretations selected on this toolbar to the selection.</p><p>If there is no selection, interpretations apply to the entire segment automatically.</p></qt>"));
969 
970     // These have to connect to something, so connect to the dummy slot:
971     createAction("interpret_text_dynamics", SLOT(slotDummy1()));
972     createAction("interpret_hairpins", SLOT(slotDummy1()));
973     createAction("interpret_slurs", SLOT(slotDummy1()));
974     createAction("interpret_beats", SLOT(slotDummy1()));
975 
976     QMenu *addControlRulerMenu = new QMenu;
977     Controllable *c =
978         dynamic_cast<MidiDevice *>(getCurrentDevice());
979     if (!c) {
980         c = dynamic_cast<SoftSynthDevice *>(getCurrentDevice());
981     }
982 
983     if (c) {
984 
985         const ControlList &list = c->getControlParameters();
986 
987         QString itemStr;
988 
989         for (ControlList::const_iterator it = list.begin();
990              it != list.end(); ++it) {
991 
992             // Pitch Bend is treated separately now, and there's no
993             // point in adding "unsupported" controllers to the menu,
994             // so skip everything else
995             if (it->getType() != Controller::EventType) continue;
996 
997             const QString hexValue =
998                 QString::asprintf("(0x%x)", it->getControllerNumber());
999 
1000             // strings extracted from data files must be QObject::tr()
1001             itemStr = QObject::tr("%1 Controller %2 %3")
1002                 .arg(QObject::tr(it->getName().c_str()))
1003                 .arg(it->getControllerNumber())
1004                 .arg(hexValue);
1005 
1006             addControlRulerMenu->addAction(itemStr);
1007         }
1008     }
1009 
1010     connect(addControlRulerMenu, &QMenu::triggered,
1011             this, &NotationView::slotAddControlRuler);
1012 
1013     connect(m_notationWidget, &NotationWidget::hoveredOverNoteChanged,
1014             this, &NotationView::slotHoveredOverNoteChanged);
1015 
1016     findAction("add_control_ruler")->setMenu(addControlRulerMenu);
1017 
1018     //Actions first appear in "settings" Menubar menu
1019     //"toolbars" subMenu
1020     createAction("options_show_toolbar", SLOT(slotToggleGeneralToolBar()));
1021     createAction("show_tools_toolbar", SLOT(slotToggleToolsToolBar()));
1022     createAction("show_duration_toolbar", SLOT(slotToggleDurationToolBar()));
1023     createAction("show_accidentals_toolbar", SLOT(slotToggleAccidentalsToolBar()));
1024     createAction("show_clefs_toolbar", SLOT(slotToggleClefsToolBar()));
1025     createAction("show_marks_toolbar", SLOT(slotToggleMarksToolBar()));
1026     createAction("show_group_toolbar", SLOT(slotToggleGroupToolBar()));
1027     createAction("show_symbol_toolbar", SLOT(slotToggleSymbolsToolBar()));
1028     createAction("show_transport_toolbar", SLOT(slotToggleTransportToolBar()));
1029     createAction("show_layout_toolbar", SLOT(slotToggleLayoutToolBar()));
1030     createAction("show_layer_toolbar", SLOT(slotToggleLayerToolBar()));
1031     createAction("show_rulers_toolbar", SLOT(slotToggleRulersToolBar()));
1032     createAction("show_interpret_toolbar", SLOT(slotToggleInterpretToolBar()));
1033 
1034     //"rulers" subMenu
1035     createAction("show_chords_ruler", SLOT(slotToggleChordsRuler()));
1036     createAction("show_raw_note_ruler", SLOT(slotToggleRawNoteRuler()));
1037     createAction("show_tempo_ruler", SLOT(slotToggleTempoRuler()));
1038 
1039     //createAction("show_annotations", SLOT(slotToggleAnnotations()));
1040     //createAction("show_lilypond_directives", SLOT(slotToggleLilyPondDirectives()));
1041 
1042     createAction("extend_selection_backward_bar", SLOT(slotExtendSelectionBackwardBar()));
1043     createAction("extend_selection_forward_bar", SLOT(slotExtendSelectionForwardBar()));
1044     //!!! not here yet createAction("move_selection_left", SLOT(slotMoveSelectionLeft()));
1045     //&&& NB Play has two shortcuts (Enter and Ctrl+Return) -- need to
1046     // ensure both get carried across somehow
1047     createAction("add_dot", SLOT(slotAddDot()));
1048     createAction("add_notation_dot", SLOT(slotAddDotNotationOnly()));
1049 
1050     //set duration of notes by CTRL+<number>
1051     createAction("set_note_type_doublewhole",SLOT(slotSetNoteType()));
1052     createAction("set_note_type_whole",SLOT(slotSetNoteType()));
1053     createAction("set_note_type_half",SLOT(slotSetNoteType()));
1054     createAction("set_note_type_quarter",SLOT(slotSetNoteType()));
1055     createAction("set_note_type_eighth",SLOT(slotSetNoteType()));
1056     createAction("set_note_type_sixteenth",SLOT(slotSetNoteType()));
1057     createAction("set_note_type_thirtysecond",SLOT(slotSetNoteType()));
1058     createAction("set_note_type_sixtyfourth",SLOT(slotSetNoteType()));
1059 
1060     //set duration of notes by CTRL+ALT+<number>
1061     createAction("set_note_type_notation_doublewhole",SLOT(slotSetNoteTypeNotationOnly()));
1062     createAction("set_note_type_notation_whole",SLOT(slotSetNoteTypeNotationOnly()));
1063     createAction("set_note_type_notation_half",SLOT(slotSetNoteTypeNotationOnly()));
1064     createAction("set_note_type_notation_quarter",SLOT(slotSetNoteTypeNotationOnly()));
1065     createAction("set_note_type_notation_eighth",SLOT(slotSetNoteTypeNotationOnly()));
1066     createAction("set_note_type_notation_sixteenth",SLOT(slotSetNoteTypeNotationOnly()));
1067     createAction("set_note_type_notation_thirtysecond",SLOT(slotSetNoteTypeNotationOnly()));
1068     createAction("set_note_type_notation_sixtyfourth",SLOT(slotSetNoteTypeNotationOnly()));
1069 
1070     //JAS insert note section is a rewrite
1071     //JAS from EditView::createInsertPitchActionMenu()
1072     for (int octave = 0; octave <= 2; ++octave) {
1073         QString octaveSuffix;
1074         if (octave == 1) octaveSuffix = "_high";
1075         else if (octave == 2) octaveSuffix = "_low";
1076 
1077         createAction(QString("insert_0%1").arg(octaveSuffix),
1078                      SLOT(slotInsertNoteFromAction()));
1079         createAction(QString("insert_0_sharp%1").arg(octaveSuffix),
1080                      SLOT(slotInsertNoteFromAction()));
1081         createAction(QString("insert_1_flat%1").arg(octaveSuffix),
1082                      SLOT(slotInsertNoteFromAction()));
1083         createAction(QString("insert_1%1").arg(octaveSuffix),
1084                      SLOT(slotInsertNoteFromAction()));
1085         createAction(QString("insert_1_sharp%1").arg(octaveSuffix),
1086                      SLOT(slotInsertNoteFromAction()));
1087         createAction(QString("insert_2_flat%1").arg(octaveSuffix),
1088                      SLOT(slotInsertNoteFromAction()));
1089         createAction(QString("insert_2%1").arg(octaveSuffix),
1090                      SLOT(slotInsertNoteFromAction()));
1091         createAction(QString("insert_3%1").arg(octaveSuffix),
1092                      SLOT(slotInsertNoteFromAction()));
1093         createAction(QString("insert_3_sharp%1").arg(octaveSuffix),
1094                      SLOT(slotInsertNoteFromAction()));
1095         createAction(QString("insert_4_flat%1").arg(octaveSuffix),
1096                      SLOT(slotInsertNoteFromAction()));
1097         createAction(QString("insert_4%1").arg(octaveSuffix),
1098                      SLOT(slotInsertNoteFromAction()));
1099         createAction(QString("insert_4_sharp%1").arg(octaveSuffix),
1100                      SLOT(slotInsertNoteFromAction()));
1101         createAction(QString("insert_5_flat%1").arg(octaveSuffix),
1102                      SLOT(slotInsertNoteFromAction()));
1103         createAction(QString("insert_5%1").arg(octaveSuffix),
1104                      SLOT(slotInsertNoteFromAction()));
1105         createAction(QString("insert_5_sharp%1").arg(octaveSuffix),
1106                      SLOT(slotInsertNoteFromAction()));
1107         createAction(QString("insert_6_flat%1").arg(octaveSuffix),
1108                      SLOT(slotInsertNoteFromAction()));
1109         createAction(QString("insert_6%1").arg(octaveSuffix),
1110                      SLOT(slotInsertNoteFromAction()));
1111     }
1112     createAction(QString("insert_rest"),SLOT(slotInsertRestFromAction()));
1113 
1114     std::set<QString> fs(NoteFontFactory::getFontNames());
1115     std::vector<QString> f;
1116     for (std::set<QString>::const_iterator i = fs.begin(); i != fs.end(); ++i) {
1117         f.push_back(*i);
1118     }
1119     std::sort(f.begin(), f.end());
1120 
1121     // Custom Note Font Menu creator
1122     QMenu *fontActionMenu = new QMenu(tr("Note &Font"), this);
1123     fontActionMenu->setObjectName("note_font_actionmenu");
1124 
1125     QActionGroup *ag = new QActionGroup(this);
1126 
1127     QSettings settings;
1128     settings.beginGroup(NotationViewConfigGroup);
1129 
1130     m_fontName = settings.value("notefont", NoteFontFactory::getDefaultFontName()).toString();
1131 
1132     for (std::vector<QString>::iterator i = f.begin(); i != f.end(); ++i) {
1133 
1134         QString fontQName(*i);
1135 
1136         m_availableFontNames.push_back(fontQName);
1137 
1138         QAction *a = createAction("note_font_" + fontQName,
1139                                   SLOT(slotChangeFontFromAction()));
1140 
1141         ag->addAction(a);
1142 
1143         a->setText(fontQName);
1144         a->setCheckable(true);
1145         a->setChecked(*i == m_fontName);
1146 
1147         fontActionMenu->addAction(a);
1148     }
1149 
1150     QMenu *fontSizeActionMenu = new QMenu(tr("Si&ze"), this);
1151     fontSizeActionMenu->setObjectName("note_font_size_actionmenu");
1152     ag = new QActionGroup(this);
1153 
1154     m_availableFontSizes = NoteFontFactory::getScreenSizes(m_fontName);
1155 
1156     for (unsigned int i = 0; i < m_availableFontSizes.size(); ++i) {
1157 
1158         QString actionName = QString("note_font_size_%1").arg(m_availableFontSizes[i]);
1159 
1160         QAction *sizeAction = createAction(actionName,
1161                                 SLOT(slotChangeFontSizeFromAction()));
1162         sizeAction->setText(tr("%n pixel(s)", "", m_availableFontSizes[i]));
1163         sizeAction->setCheckable(true);
1164         ag->addAction(sizeAction);
1165 
1166         sizeAction->setChecked(m_availableFontSizes[i] == m_fontSize);
1167         fontSizeActionMenu->addAction(sizeAction);
1168     }
1169 
1170     QMenu *spacingActionMenu = new QMenu(tr("S&pacing"), this);
1171     spacingActionMenu->setObjectName("stretch_actionmenu");
1172 
1173     m_notationWidget->getScene()->setHSpacing(
1174             m_doc->getComposition().m_notationSpacing);
1175     m_availableSpacings = NotationHLayout::getAvailableSpacings();
1176 
1177     ag = new QActionGroup(this);
1178 
1179     for (std::vector<int>::iterator i = m_availableSpacings.begin();
1180          i != m_availableSpacings.end(); ++i) {
1181 
1182         QAction *a = createAction(QString("spacing_%1").arg(*i),
1183                                   SLOT(slotChangeSpacingFromAction()));
1184 
1185         ag->addAction(a);
1186         a->setText(QString("%1%").arg(*i));
1187         a->setCheckable(true);
1188         a->setChecked(*i == m_doc->getComposition().m_notationSpacing);
1189 
1190         spacingActionMenu->addAction(a);
1191     }
1192 
1193     // no more duration factor controls
1194 
1195     settings.endGroup();
1196 
1197     // connect up the segment changer signals
1198     connect(m_notationWidget, &NotationWidget::currentSegmentNext,
1199             this, &NotationView::slotCurrentSegmentNext);
1200     connect(m_notationWidget, &NotationWidget::currentSegmentPrior,
1201             this, &NotationView::slotCurrentSegmentPrior);
1202 }
1203 
1204 void
slotUpdateMenuStates()1205 NotationView::slotUpdateMenuStates()
1206 {
1207     //NOTATION_DEBUG << "NotationView::slotUpdateMenuStates";
1208 
1209     // 1. set selection-related states
1210 
1211     // Clear states first, then enter only those ones that apply
1212     // (so as to avoid ever clearing one after entering another, in
1213     // case the two overlap at all)
1214     leaveActionState("have_selection");
1215     leaveActionState("have_notes_in_selection");
1216     leaveActionState("have_rests_in_selection");
1217     leaveActionState("have_clefs_in_selection");
1218     leaveActionState("have_symbols_in_selection");
1219     leaveActionState("have_linked_segment");
1220 
1221     if (!m_notationWidget) return;
1222 
1223     EventSelection *selection = m_notationWidget->getSelection();
1224 
1225     if (selection) {
1226 
1227         //NOTATION_DEBUG << "NotationView::slotUpdateMenuStates: Have selection; it's " << selection << " covering range from " << selection->getStartTime() << " to " << selection->getEndTime() << " (" << selection->getSegmentEvents().size() << " events)";
1228 
1229         enterActionState("have_selection");
1230         if (selection->contains(Note::EventType)) {
1231             enterActionState("have_notes_in_selection");
1232         }
1233         if (selection->contains(Note::EventRestType)) {
1234             enterActionState("have_rests_in_selection");
1235         }
1236         if (selection->contains(Clef::EventType)) {
1237             enterActionState("have_clefs_in_selection");
1238         }
1239         if (selection->contains(Symbol::EventType)) {
1240             enterActionState("have_symbols_in_selection");
1241         }
1242 
1243         // Special case - the AddDot command does nothing for tied
1244         // notes so if the selection contains only tied notes we
1245         // should disable the command
1246         bool allTied = true;
1247         EventSelection::eventcontainer &ec =
1248             selection->getSegmentEvents();
1249         for (EventSelection::eventcontainer::iterator i =
1250                  ec.begin(); i != ec.end(); ++i) {
1251             if ((*i)->isa(Note::EventType)) {
1252                 bool tiedNote = ((*i)->has(BaseProperties::TIED_FORWARD) ||
1253                                  (*i)->has(BaseProperties::TIED_BACKWARD));
1254                 if (! tiedNote) {
1255                     // found a note which is not tied
1256                     allTied = false;
1257                     break;
1258                 }
1259             }
1260         }
1261         if (allTied) {
1262             // all selected notes are tied
1263             QAction *addDot = findAction("add_dot");
1264             QAction *addDotNotation = findAction("add_notation_dot");
1265             addDot->setEnabled(false);
1266             addDotNotation->setEnabled(false);
1267         }
1268 
1269     } else {
1270         //NOTATION_DEBUG << "Do not have a selection";
1271     }
1272 
1273     // 2. set inserter-related states
1274     NoteRestInserter *currentTool = dynamic_cast<NoteRestInserter *>(m_notationWidget->getCurrentTool());
1275     if (currentTool) {
1276         //NOTATION_DEBUG << "Have NoteRestInserter ";
1277         enterActionState("note_rest_tool_current");
1278 
1279     } else {
1280         //NOTATION_DEBUG << "Do not have NoteRestInserter ";
1281         leaveActionState("note_rest_tool_current");
1282     }
1283 
1284     if (m_selectionCounter) {
1285         if (selection && !selection->getSegmentEvents().empty()) {
1286             m_selectionCounter->setText(tr("  %n event(s) selected ", "",
1287                                            selection->getSegmentEvents().size()));
1288         } else {
1289             m_selectionCounter->setText(tr("  No selection "));
1290         }
1291     }
1292 
1293     // 3. linked segment specific states
1294     Segment *segment = getCurrentSegment();
1295     if (segment && segment->isLinked()) {
1296         enterActionState("have_linked_segment");
1297     }
1298 
1299     conformRulerSelectionState();
1300 
1301     // 4. multiple staffs - this can change through er. add layer
1302     RG_DEBUG << "segment size" << m_segments.size();
1303     if (m_segments.size() > 1) {
1304         enterActionState("have_multiple_staffs");
1305     } else {
1306         leaveActionState("have_multiple_staffs");
1307     }
1308 
1309 }
1310 
1311 void
1312 NotationView::
conformRulerSelectionState()1313 conformRulerSelectionState()
1314 {
1315     ControlRulerWidget * cr = m_notationWidget->getControlsWidget();
1316     if (cr->isAnyRulerVisible())
1317         {
1318             cr->slotSelectionChanged(getSelection());
1319 
1320             enterActionState("have_control_ruler");
1321             if (cr->hasSelection())
1322                 { enterActionState("have_controller_selection"); }
1323             else
1324                 { leaveActionState("have_controller_selection"); }
1325         }
1326     else {
1327         leaveActionState("have_control_ruler");
1328         // No ruler implies no controller selection
1329         leaveActionState("have_controller_selection");
1330     }
1331 }
1332 
1333 void
initLayoutToolbar()1334 NotationView::initLayoutToolbar()
1335 {
1336     QToolBar *layoutToolbar = findToolbar("Layout Toolbar");
1337 
1338     if (!layoutToolbar) {
1339         RG_WARNING << "NotationView::initLayoutToolbar() : layout toolbar not found";
1340         return;
1341     }
1342 
1343     QLabel *label = new QLabel(tr("  Font:  "), layoutToolbar);
1344     layoutToolbar->addWidget(label);
1345 
1346 
1347     //
1348     // font combo
1349     //
1350     m_fontCombo = new QComboBox(layoutToolbar);
1351     m_fontCombo->setEditable(false);
1352     layoutToolbar->addWidget(m_fontCombo);
1353 
1354     bool foundFont = false;
1355 
1356     for (std::vector<QString>::const_iterator i = m_availableFontNames.begin(); i != m_availableFontNames.end(); ++i) {
1357 
1358         QString fontQName(*i);
1359 
1360         m_fontCombo->addItem(fontQName);
1361         if (fontQName.toLower() == m_fontName.toLower()) {
1362             m_fontCombo->setCurrentIndex(m_fontCombo->count() - 1);
1363             foundFont = true;
1364         }
1365     }
1366 
1367     if (!foundFont) {
1368         // don't annoy user with stupid internal warning dialog (except while
1369         // debugging)
1370         QMessageBox::warning (this, tr("Rosegarden"), tr("Unknown font \"%1\", using default")
1371                              .arg(m_fontName) );
1372         m_fontName = NoteFontFactory::getDefaultFontName();
1373     }
1374 
1375     connect(m_fontCombo, SIGNAL(currentIndexChanged(int)),
1376             this, SLOT(slotFontComboChanged(int)));
1377 
1378     label = new QLabel(tr("  Size:  "), layoutToolbar);
1379     layoutToolbar->addWidget(label);
1380 
1381     QString value;
1382 
1383     //
1384     // font size combo
1385     //
1386     m_fontSizeCombo = new QComboBox(layoutToolbar);
1387     layoutToolbar->addWidget(m_fontSizeCombo);
1388 
1389     for (std::vector<int>::iterator i = m_availableFontSizes.begin(); i != m_availableFontSizes.end(); ++i) {
1390         value.setNum(*i);
1391         m_fontSizeCombo->addItem(value);
1392         if ((*i) == m_fontSize) {
1393             m_fontSizeCombo->setCurrentIndex(m_fontSizeCombo->count() - 1);
1394         }
1395     }
1396 
1397     connect(m_fontSizeCombo, SIGNAL(currentIndexChanged(int)),
1398             this, SLOT(slotSizeComboChanged(int)));
1399 
1400     label = new QLabel(tr("  Spacing:  "), layoutToolbar);
1401 
1402     layoutToolbar->addWidget(label);
1403 
1404     //
1405     // spacing combo
1406     //
1407     int spacing = m_notationWidget->getScene()->getHSpacing();
1408     m_availableSpacings = NotationHLayout::getAvailableSpacings();
1409 
1410     m_spacingCombo = new QComboBox(layoutToolbar);
1411     for (std::vector<int>::iterator i = m_availableSpacings.begin(); i != m_availableSpacings.end(); ++i) {
1412 
1413         value.setNum(*i);
1414         value += "%";
1415         m_spacingCombo->addItem(value);
1416         if ((*i) == spacing) {
1417             m_spacingCombo->setCurrentIndex(m_spacingCombo->count() - 1);
1418         }
1419     }
1420 
1421     connect(m_spacingCombo, SIGNAL(currentIndexChanged(int)),
1422             this, SLOT(slotSpacingComboChanged(int)));
1423 
1424     layoutToolbar->addWidget(m_spacingCombo);
1425 }
1426 
1427 void
initRulersToolbar()1428 NotationView::initRulersToolbar()
1429 {
1430     QToolBar *rulersToolbar = findToolbar("Rulers Toolbar");
1431     if (!rulersToolbar) {
1432         RG_WARNING << "NotationView::initRulersToolbar() - rulers toolbar not found!";
1433         return;
1434     }
1435 
1436     // set the "ruler n" tool button to pop up its menu instantly
1437     QToolButton *tb = dynamic_cast<QToolButton *>(findToolbar("Rulers Toolbar")->widgetForAction(findAction("add_control_ruler")));
1438     if (tb) {
1439         tb->setPopupMode(QToolButton::InstantPopup);
1440     }
1441 }
1442 
1443 void
initStatusBar()1444 NotationView::initStatusBar()
1445 {
1446     QStatusBar* sb = statusBar();
1447 
1448     m_hoveredOverNoteName = new QLabel(sb);
1449     m_hoveredOverNoteName->setMinimumWidth(32);
1450     sb->addPermanentWidget(m_hoveredOverNoteName);
1451 
1452     m_hoveredOverAbsoluteTime = new QLabel(sb);
1453     m_hoveredOverAbsoluteTime->setMinimumWidth(160);
1454     sb->addPermanentWidget(m_hoveredOverAbsoluteTime);
1455 
1456     m_currentNotePixmap = new QLabel(sb);
1457     m_currentNotePixmap->setMinimumWidth(20);
1458     sb->addPermanentWidget(m_currentNotePixmap);
1459 
1460     m_insertModeLabel = new QLabel(sb);
1461     sb->addPermanentWidget(m_insertModeLabel);
1462 
1463     m_annotationsLabel = new QLabel(sb);
1464     sb->addPermanentWidget(m_annotationsLabel);
1465 
1466     m_lilyPondDirectivesLabel = new QLabel(sb);
1467     sb->addPermanentWidget(m_lilyPondDirectivesLabel);
1468 
1469     m_selectionCounter = new QLabel(sb);
1470     sb->addWidget(m_selectionCounter);
1471 
1472     sb->setContentsMargins(0, 0, 0, 0);
1473 }
1474 
1475 void
slotShowContextHelp(const QString & help)1476 NotationView::slotShowContextHelp(const QString &help)
1477 {
1478     statusBar()->showMessage(help, 10000);
1479 }
1480 
1481 void
readOptions()1482 NotationView::readOptions()
1483 {
1484     setCheckBoxState("options_show_toolbar", "General Toolbar");
1485     setCheckBoxState("show_tools_toolbar", "Tools Toolbar");
1486     setCheckBoxState("show_accidentals_toolbar", "Accidentals Toolbar");
1487     setCheckBoxState("show_clefs_toolbar", "Clefs Toolbar");
1488     setCheckBoxState("show_marks_toolbar", "Marks Toolbar");
1489     setCheckBoxState("show_group_toolbar", "Group Toolbar");
1490     setCheckBoxState("show_symbol_toolbar", "Symbols Toolbar");
1491     setCheckBoxState("show_transport_toolbar", "Transport Toolbar");
1492     setCheckBoxState("show_layout_toolbar", "Layout Toolbar");
1493     setCheckBoxState("show_layer_toolbar", "Layer Toolbar");
1494     setCheckBoxState("show_rulers_toolbar", "Rulers Toolbar");
1495     setCheckBoxState("show_duration_toolbar", "Duration Toolbar");
1496     setCheckBoxState("show_interpret_toolbar", "Interpret Toolbar");
1497     // Suspected BUG.  We read the options, but when do we ever write them?  Not
1498     // in slotToggleNamedToolBar() I don't imagine.  How would it translate name
1499     // "Foo Toolbar" into action "show_foo_toolbar" reliably?  I bet it doesn't,
1500     // but haven't looked.  I have a vague feeling all of these toggle actions
1501     // have never maintained their persistence reliably, and suspect I know why.
1502     // To be investigated one day...
1503 }
1504 
1505 void
setCurrentNotePixmap(QPixmap p)1506 NotationView::setCurrentNotePixmap(QPixmap p)
1507 {
1508     if (!m_currentNotePixmap) return;
1509     QPixmap ip = invertPixmap(p);
1510     if (ip.height() > 16) {
1511         ip = ip.scaledToHeight(16, Qt::SmoothTransformation);
1512     }
1513     m_currentNotePixmap->setPixmap(ip);
1514 }
1515 
1516 void
setCurrentNotePixmapFrom(QAction * a)1517 NotationView::setCurrentNotePixmapFrom(QAction *a)
1518 {
1519     if (!a) return;
1520     //setCurrentNotePixmap(a->icon().pixmap());
1521     // QT3: You have to use one of the ctors that takes a QSize() argument now,
1522     // but I only have the vaguest idea what this code does.  I pulled 32x32 out
1523     // my ass to get the code compiling, but there's a 99.99% chance this is
1524     // wrong, and even if it's right, it's still wrong to write code this way.
1525     setCurrentNotePixmap(a->icon().pixmap(QSize(32,32)));
1526 }
1527 
1528 bool
exportLilyPondFile(QString file,bool forPreview)1529 NotationView::exportLilyPondFile(QString file, bool forPreview)
1530 {
1531     QString caption = "", heading = "";
1532     if (forPreview) {
1533         caption = tr("LilyPond Preview Options");
1534         heading = tr("LilyPond preview options");
1535     }
1536 
1537     LilyPondOptionsDialog dialog(this, m_doc, caption, heading, true);
1538     if (dialog.exec() != QDialog::Accepted) {
1539         return false;
1540     }
1541 
1542     RosegardenMainViewWidget * view = RosegardenMainWindow::self()->getView();
1543 
1544     LilyPondExporter e(m_doc, view->getSelection(), std::string(QFile::encodeName(file)), this);
1545 
1546     if (!e.write()) {
1547         QMessageBox::warning(this, tr("Rosegarden"), e.getMessage());
1548         return false;
1549     }
1550 
1551     return true;
1552 }
1553 
1554 void
slotPrintLilyPond()1555 NotationView::slotPrintLilyPond()
1556 {
1557     TmpStatusMsg msg(tr("Printing with LilyPond..."), this);
1558 
1559     QString filename = getLilyPondTmpFilename();
1560 
1561     if (filename.isEmpty()) return;
1562 
1563     if (!exportLilyPondFile(filename, true)) {
1564         return ;
1565     }
1566 
1567     LilyPondProcessor *dialog = new LilyPondProcessor(this, LilyPondProcessor::Print, filename);
1568 
1569     dialog->exec();
1570 }
1571 
1572 void
slotPreviewLilyPond()1573 NotationView::slotPreviewLilyPond()
1574 {
1575     TmpStatusMsg msg(tr("Previewing with LilyPond..."), this);
1576 
1577     QString filename = getLilyPondTmpFilename();
1578 
1579     if (filename.isEmpty()) return;
1580 
1581     if (!exportLilyPondFile(filename, true)) {
1582         return ;
1583     }
1584 
1585     LilyPondProcessor *dialog = new LilyPondProcessor(this, LilyPondProcessor::Preview, filename);
1586 
1587     dialog->exec();
1588 }
1589 
1590 QString
getLilyPondTmpFilename()1591 NotationView::getLilyPondTmpFilename()
1592 {
1593     QString mask = QString("%1/rosegarden_tmp_XXXXXX.ly").arg(QDir::tempPath());
1594     RG_DEBUG << "NotationView::getLilyPondTmpName() - using tmp file: " << qstrtostr(mask);
1595 
1596     QTemporaryFile *file = new QTemporaryFile(mask);
1597     file->setAutoRemove(true);
1598     if (!file->open()) {
1599         QMessageBox::warning(this, tr("Rosegarden"),
1600                                        tr("<qt><p>Failed to open a temporary file for LilyPond export.</p>"
1601                                           "<p>This probably means you have run out of disk space on <pre>%1</pre></p></qt>").
1602                                        arg(QDir::tempPath()));
1603         delete file;
1604         return QString();
1605     }
1606     QString filename = file->fileName(); // must call this before close()
1607     file->close(); // we just want the filename
1608 
1609     return filename;
1610 }
1611 
1612 
1613 void
slotLinearMode()1614 NotationView::slotLinearMode()
1615 {
1616     enterActionState("linear_mode");
1617     if (m_notationWidget) m_notationWidget->slotSetLinearMode();
1618 }
1619 
1620 void
slotContinuousPageMode()1621 NotationView::slotContinuousPageMode()
1622 {
1623     leaveActionState("linear_mode");
1624     if (m_notationWidget) m_notationWidget->slotSetContinuousPageMode();
1625 }
1626 
1627 void
slotMultiPageMode()1628 NotationView::slotMultiPageMode()
1629 {
1630     leaveActionState("linear_mode");
1631     if (m_notationWidget) m_notationWidget->slotSetMultiPageMode();
1632 }
1633 
1634 void
slotShowHeadersGroup()1635 NotationView::slotShowHeadersGroup()
1636 {
1637     if (m_notationWidget) m_notationWidget->toggleHeadersView();
1638 }
1639 
1640 void
slotChangeFontFromAction()1641 NotationView::slotChangeFontFromAction()
1642 {
1643     const QObject *s = sender();
1644     QString name = s->objectName();
1645     if (name.left(10) == "note_font_") {
1646         name = name.right(name.length() - 10);
1647         if (m_notationWidget) m_notationWidget->slotSetFontName(name);
1648         for (uint i = 0; i < m_availableFontNames.size(); ++i) {
1649             if (m_availableFontNames[i] == name) {
1650                 m_fontCombo->setCurrentIndex(i);
1651                 break;
1652             }
1653         }
1654     } else {
1655         QMessageBox::warning
1656             (this, tr("Rosegarden"), tr("Unknown font action %1").arg(name));
1657     }
1658 }
1659 
1660 void
slotChangeFontSizeFromAction()1661 NotationView::slotChangeFontSizeFromAction()
1662 {
1663     const QObject *s = sender();
1664     QString name = s->objectName();
1665 
1666     if (name.left(15) == "note_font_size_") {
1667         name = name.right(name.length() - 15);
1668         bool ok = false;
1669         int size = name.toInt(&ok);
1670         if (ok) {
1671             if (m_notationWidget) m_notationWidget->slotSetFontSize(size);
1672             for (uint i = 0; i < m_availableFontSizes.size(); ++i) {
1673                 if (m_availableFontSizes[i] == size) {
1674                     m_fontSizeCombo->setCurrentIndex(i);
1675                     break;
1676                 }
1677             }
1678             return;
1679         }
1680     }
1681     QMessageBox::warning
1682         (this, tr("Rosegarden"), tr("Unknown font size action %1").arg(name));
1683 }
1684 
1685 void
slotChangeSpacingFromAction()1686 NotationView::slotChangeSpacingFromAction()
1687 {
1688     const QObject *s = sender();
1689     QString name = s->objectName();
1690 
1691     if (name.left(8) == "spacing_") {
1692         name = name.right(name.length() - 8);
1693         bool ok = false;
1694         int spacing = name.toInt(&ok);
1695         if (ok) {
1696             if (m_notationWidget) m_notationWidget->getScene()->setHSpacing(spacing);
1697             for (uint i = 0; i < m_availableSpacings.size(); ++i) {
1698                 if (m_availableSpacings[i] == spacing) {
1699                     m_spacingCombo->setCurrentIndex(i);
1700                     break;
1701                 }
1702             }
1703             return;
1704         }
1705     }
1706     QMessageBox::warning
1707         (this, tr("Rosegarden"), tr("Unknown spacing action %1").arg(name));
1708 }
1709 
1710 Segment *
getCurrentSegment()1711 NotationView::getCurrentSegment()
1712 {
1713     if (m_notationWidget) return m_notationWidget->getCurrentSegment();
1714     else return nullptr;
1715 }
1716 
1717 EventSelection *
getSelection() const1718 NotationView::getSelection() const
1719 {
1720     if (m_notationWidget) return m_notationWidget->getSelection();
1721     else return nullptr;
1722 }
1723 
1724 void
setSelection(EventSelection * selection,bool preview)1725 NotationView::setSelection(EventSelection *selection, bool preview)
1726 {
1727     if (m_notationWidget) m_notationWidget->setSelection(selection, preview);
1728 }
1729 
1730 timeT
getInsertionTime() const1731 NotationView::getInsertionTime() const
1732 {
1733     if (m_notationWidget) return m_notationWidget->getInsertionTime();
1734     else return 0;
1735 }
1736 
1737 void
slotEditCut()1738 NotationView::slotEditCut()
1739 {
1740     EventSelection *selection = getSelection();
1741     if (!selection) return;
1742     CommandHistory::getInstance()->addCommand
1743         (new CutCommand(*selection, getClipboard()));
1744 }
1745 
1746 void
slotEditDelete()1747 NotationView::slotEditDelete()
1748 {
1749     EventSelection *selection = getSelection();
1750     if (!selection) return;
1751     CommandHistory::getInstance()->addCommand(new EraseCommand(*selection));
1752 }
1753 
1754 void
slotEditCopy()1755 NotationView::slotEditCopy()
1756 {
1757     EventSelection *selection = getSelection();
1758     if (!selection) return;
1759     CommandHistory::getInstance()->addCommand
1760         (new CopyCommand(*selection, getClipboard()));
1761 }
1762 
1763 void
slotEditCutAndClose()1764 NotationView::slotEditCutAndClose()
1765 {
1766     EventSelection *selection = getSelection();
1767     if (!selection) return;
1768     CommandHistory::getInstance()->addCommand
1769         (new CutAndCloseCommand(*selection, getClipboard()));
1770 }
1771 
1772 void
slotEditPaste()1773 NotationView::slotEditPaste()
1774 {
1775     Clipboard *clipboard = getClipboard();
1776 
1777     if (clipboard->isEmpty()) return;
1778     if (!clipboard->isSingleSegment()) {
1779         slotStatusHelpMsg(tr("Can't paste multiple Segments into one"));
1780         return;
1781     }
1782 
1783     Segment *segment = getCurrentSegment();
1784     if (!segment) return;
1785 
1786     // Paste at cursor position
1787     //
1788     timeT insertionTime = getInsertionTime();
1789     timeT endTime = insertionTime +
1790         (clipboard->getSingleSegment()->getEndTime() -
1791          clipboard->getSingleSegment()->getStartTime());
1792 
1793     PasteEventsCommand::PasteType defaultType =
1794         PasteNotationDialog::getSavedPasteType();
1795 
1796     PasteEventsCommand *command = new PasteEventsCommand
1797         (*segment, clipboard, insertionTime, defaultType);
1798 
1799     if (!command->isPossible()) {
1800         // NOTES: To get a reasonable presentation of the standard and detailed
1801         // text, we have to build up our own QMessageBox
1802         //
1803         // The old RESTRICTED_PASTE_DESCRIPTION was removed because it was
1804         // impossible to get the translation, which had to be done in the
1805         // QObject::tr() context, to work in this context here.  Qt is really
1806         // quirky that way.  Instead, I'm just block copying all of this now
1807         // that I've reworked it.  Is this copy you're looking at the original,
1808         // or the copy?  Only I know for sure, and I'll never tell!  Bwa haha!
1809         QMessageBox msgBox;
1810         msgBox.setWindowTitle(tr("Rosegarden"));
1811         msgBox.setIcon(QMessageBox::Warning);
1812         msgBox.setText(tr("Couldn't paste at this point."));
1813         if (defaultType == PasteEventsCommand::Restricted) {
1814             msgBox.setInformativeText(tr("<qt><p>The Restricted paste type requires enough empty space (containing only rests) at the paste position to hold all of the events to be pasted.</p><p>Not enough space was found.</p><p>If you want to paste anyway, consider using one of the other paste types from the <b>Paste...</b> option on the Edit menu.  You can also change the default paste type to something other than Restricted if you wish.</p></qt>"));
1815         }
1816         msgBox.setStandardButtons(QMessageBox::Ok);
1817         msgBox.setDefaultButton(QMessageBox::Ok);
1818         msgBox.exec();
1819         delete command;
1820     } else {
1821         CommandHistory::getInstance()->addCommand(command);
1822         setSelection(command->getSubsequentSelection(), false);
1823 //!!!        slotSetInsertCursorPosition(endTime, true, false);
1824         m_document->slotSetPointerPosition(endTime);
1825     }
1826 }
1827 
1828 void
slotEditGeneralPaste()1829 NotationView::slotEditGeneralPaste()
1830 {
1831     Clipboard *clipboard = getClipboard();
1832 
1833     if (clipboard->isEmpty()) {
1834         slotStatusHelpMsg(tr("Clipboard is empty"));
1835         return ;
1836     }
1837 
1838     slotStatusHelpMsg(tr("Inserting clipboard contents..."));
1839 
1840     Segment *segment = getCurrentSegment();
1841     if (!segment) return;
1842 
1843     PasteNotationDialog dialog(this);
1844 
1845     if (dialog.exec() == QDialog::Accepted) {
1846 
1847         PasteEventsCommand::PasteType type = dialog.getPasteType();
1848 
1849         timeT insertionTime = getInsertionTime();
1850         timeT endTime = insertionTime +
1851             (clipboard->getSingleSegment()->getEndTime() -
1852              clipboard->getSingleSegment()->getStartTime());
1853 
1854         PasteEventsCommand *command = new PasteEventsCommand
1855             (*segment, clipboard, insertionTime, type);
1856 
1857         if (!command->isPossible()) {
1858             // NOTES: To get a reasonable presentation of the standard and detailed
1859             // text, we have to build up our own QMessageBox
1860             //
1861             // The old RESTRICTED_PASTE_DESCRIPTION was removed because it was
1862             // impossible to get the translation, which had to be done in the
1863             // QObject::tr() context, to work in this context here.  Qt is really
1864             // quirky that way.  Instead, I'm just block copying all of this now
1865             // that I've reworked it.  Is this copy you're looking at the original,
1866             // or the copy?  Only I know for sure, and I'll never tell!  Bwa haha!
1867             QMessageBox msgBox;
1868             msgBox.setWindowTitle(tr("Rosegarden"));
1869             msgBox.setIcon(QMessageBox::Warning);
1870             msgBox.setText(tr("Couldn't paste at this point."));
1871             if (type == PasteEventsCommand::Restricted) {
1872                 msgBox.setInformativeText(tr("<qt><p>The Restricted paste type requires enough empty space (containing only rests) at the paste position to hold all of the events to be pasted.</p><p>Not enough space was found.</p><p>If you want to paste anyway, consider using one of the other paste types from the <b>Paste...</b> option on the Edit menu.  You can also change the default paste type to something other than Restricted if you wish.</p></qt>"));
1873             }
1874             msgBox.setStandardButtons(QMessageBox::Ok);
1875             msgBox.setDefaultButton(QMessageBox::Ok);
1876             msgBox.exec();
1877             delete command;
1878         } else {
1879             CommandHistory::getInstance()->addCommand(command);
1880             setSelection(new EventSelection(*segment, insertionTime, endTime),
1881                          false);
1882 //!!!            slotSetInsertCursorPosition(endTime, true, false);
1883             m_document->slotSetPointerPosition(endTime);
1884         }
1885     }
1886 }
1887 
1888 void
slotPreviewSelection()1889 NotationView::slotPreviewSelection()
1890 {
1891     if (!getSelection())
1892         return ;
1893 
1894     getDocument()->slotSetLoop(getSelection()->getStartTime(),
1895                                getSelection()->getEndTime());
1896 }
1897 
1898 void
slotClearSelection()1899 NotationView::slotClearSelection()
1900 {
1901     // Actually we don't clear the selection immediately: if we're
1902     // using some tool other than the select tool, then the first
1903     // press switches us back to the select tool.
1904 
1905     NotationSelector *selector = dynamic_cast<NotationSelector *>(m_notationWidget->getCurrentTool());
1906 
1907     if (!selector) {
1908         slotSetSelectTool();
1909     } else {
1910         setSelection(nullptr, false);
1911     }
1912 }
1913 
1914 void
slotEditSelectFromStart()1915 NotationView::slotEditSelectFromStart()
1916 {
1917     timeT t = getInsertionTime();
1918     Segment *segment = getCurrentSegment();
1919     setSelection(new EventSelection(*segment,
1920                                     segment->getStartTime(),
1921                                     t),
1922                  false);
1923 }
1924 
1925 void
slotEditSelectToEnd()1926 NotationView::slotEditSelectToEnd()
1927 {
1928     timeT t = getInsertionTime();
1929     Segment *segment = getCurrentSegment();
1930     setSelection(new EventSelection(*segment,
1931                                     t,
1932                                     segment->getEndMarkerTime()),
1933                  false);
1934 }
1935 
1936 void
slotEditSelectWholeStaff()1937 NotationView::slotEditSelectWholeStaff()
1938 {
1939     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1940     Segment *segment = getCurrentSegment();
1941     setSelection(new EventSelection(*segment,
1942                                     segment->getStartTime(),
1943                                     segment->getEndMarkerTime()),
1944                  false);
1945     QApplication::restoreOverrideCursor();
1946 }
1947 
1948 void
slotSearchSelect()1949 NotationView::slotSearchSelect()
1950 {
1951     NOTATION_DEBUG << "NotationView::slotSearchSelect";
1952 
1953     SelectDialog dialog(this);
1954     if (dialog.exec() == QDialog::Accepted) {
1955         NOTATION_DEBUG << "slotSearchSelect- accepted";
1956     }
1957 }
1958 
1959 void
slotFilterSelection()1960 NotationView::slotFilterSelection()
1961 {
1962     NOTATION_DEBUG << "NotationView::slotFilterSelection";
1963 
1964     Segment *segment = getCurrentSegment();
1965     EventSelection *existingSelection = getSelection();
1966     if (!segment || !existingSelection)
1967         return ;
1968 
1969     EventFilterDialog dialog(this);
1970     if (dialog.exec() == QDialog::Accepted) {
1971         NOTATION_DEBUG << "slotFilterSelection- accepted";
1972 
1973         bool haveEvent = false;
1974 
1975         EventSelection *newSelection = new EventSelection(*segment);
1976         EventSelection::eventcontainer &ec =
1977             existingSelection->getSegmentEvents();
1978         for (EventSelection::eventcontainer::iterator i =
1979                  ec.begin(); i != ec.end(); ++i) {
1980             if (dialog.keepEvent(*i)) {
1981                 haveEvent = true;
1982                 newSelection->addEvent(*i);
1983             }
1984         }
1985 
1986         if (haveEvent) {
1987             setSelection(newSelection, false);
1988         } else {
1989             setSelection(nullptr, false);
1990         }
1991     }
1992 }
1993 
1994 // Launch SelectAddEvenNotesCommand
1995 void
slotSelectEvenlySpacedNotes()1996 NotationView::slotSelectEvenlySpacedNotes()
1997 {
1998     if (!getSelection()) { return; }
1999 
2000     EventSelection *eventSelection = getSelection();
2001     if (eventSelection->getSegmentEvents().size() < 2) { return; }
2002     BasicCommand *command = new
2003         SelectAddEvenNotesCommand(SelectAddEvenNotesCommand::findBeatEvents(eventSelection),
2004                            &eventSelection->getSegment());
2005 
2006     CommandHistory::getInstance()->addCommand(command);
2007     setSelection(command->getSubsequentSelection(), false);
2008 }
2009 
2010 void
slotVelocityUp()2011 NotationView::slotVelocityUp()
2012 {
2013     if (!getSelection())
2014         return ;
2015     TmpStatusMsg msg(tr("Raising velocities..."), this);
2016 
2017     CommandHistory::getInstance()->addCommand(new ChangeVelocityCommand(
2018             10, *getSelection()));
2019 }
2020 
2021 void
slotVelocityDown()2022 NotationView::slotVelocityDown()
2023 {
2024     if (!getSelection())
2025         return ;
2026     TmpStatusMsg msg(tr("Lowering velocities..."), this);
2027 
2028     CommandHistory::getInstance()->addCommand(new ChangeVelocityCommand(
2029             -10, *getSelection()));
2030 }
2031 
2032 void
slotSetVelocities()2033 NotationView::slotSetVelocities()
2034 {
2035     ParameterPattern::
2036         setVelocities(this, getSelection());
2037 }
2038 
2039 void
slotEditCopyControllers()2040 NotationView::slotEditCopyControllers()
2041 {
2042     ControlRulerWidget *cr = m_notationWidget->getControlsWidget();
2043     EventSelection *selection = cr->getSelection();
2044     if (!selection) return;
2045     CommandHistory::getInstance()->addCommand
2046         (new CopyCommand(*selection, getClipboard()));
2047 }
2048 
2049 void
slotEditCutControllers()2050 NotationView::slotEditCutControllers()
2051 {
2052     ControlRulerWidget *cr = m_notationWidget->getControlsWidget();
2053     EventSelection *selection = cr->getSelection();
2054     if (!selection) return;
2055     CommandHistory::getInstance()->addCommand
2056         (new CutCommand(*selection, getClipboard()));
2057 }
2058 
2059 void
slotSetControllers()2060 NotationView::slotSetControllers()
2061 {
2062     ControlRulerWidget * cr = m_notationWidget->getControlsWidget();
2063     ParameterPattern::setProperties(
2064             this,
2065             tr("Set Controller Values"),
2066             cr->getSituation(),
2067             &ParameterPattern::VelocityPatterns);
2068 }
2069 
2070 void
slotPlaceControllers()2071 NotationView::slotPlaceControllers()
2072 {
2073     EventSelection *selection = getSelection();
2074     if (!selection) { return; }
2075 
2076     ControlRulerWidget *cr = m_notationWidget->getControlsWidget();
2077     if (!cr) { return; }
2078 
2079     ControlParameter *cp = cr->getControlParameter();
2080     if (!cp) { return; }
2081 
2082     const Instrument *instrument =
2083         getDocument()->getInstrument(getCurrentSegment());
2084     if (!instrument) { return; }
2085 
2086     PlaceControllersCommand *command =
2087         new PlaceControllersCommand(*selection,
2088                                     instrument,
2089                                     cp);
2090     CommandHistory::getInstance()->addCommand(command);
2091 }
2092 
2093 void
slotClearLoop()2094 NotationView::slotClearLoop()
2095 {
2096     getDocument()->slotSetLoop(0, 0);
2097 }
2098 
2099 void
slotCurrentStaffUp()2100 NotationView::slotCurrentStaffUp()
2101 {
2102     NotationScene *scene = m_notationWidget->getScene();
2103     if (!scene) return;
2104     timeT pointerPosition = m_doc->getComposition().getPosition();
2105     // if the pointer has moved take that time
2106     if (pointerPosition != m_oldPointerPosition) {
2107         m_oldPointerPosition = pointerPosition;
2108         m_cursorPosition = pointerPosition;
2109     }
2110     timeT targetTime = m_cursorPosition;
2111     NotationStaff *staff = scene->getStaffAbove(targetTime);
2112     if (!staff) return;
2113     setCurrentStaff(staff);
2114 
2115     // This can take a very long time.
2116     //slotEditSelectWholeStaff();
2117 }
2118 
2119 void
slotCurrentStaffDown()2120 NotationView::slotCurrentStaffDown()
2121 {
2122     NotationScene *scene = m_notationWidget->getScene();
2123     if (!scene) return;
2124     timeT pointerPosition = m_doc->getComposition().getPosition();
2125     // if the pointer has moved take that time
2126     if (pointerPosition != m_oldPointerPosition) {
2127         m_oldPointerPosition = pointerPosition;
2128         m_cursorPosition = pointerPosition;
2129     }
2130     timeT targetTime = m_cursorPosition;
2131     NotationStaff *staff = scene->getStaffBelow(targetTime);
2132     if (!staff) return;
2133     setCurrentStaff(staff);
2134 
2135     // This can take a very long time.
2136     //slotEditSelectWholeStaff();
2137 }
2138 
2139 void
slotCurrentSegmentPrior()2140 NotationView::slotCurrentSegmentPrior()
2141 {
2142     NotationScene *scene = m_notationWidget->getScene();
2143     if (!scene) return;
2144     NotationStaff *staff = scene->getPriorStaffOnTrack();
2145     if (!staff) {
2146         // move to next staff above this one
2147         staff = scene->getStaffAbove(0);
2148         if (!staff) return;
2149         // make sure it is the last
2150         NotationStaff *nextRightStaff = staff;
2151         while (nextRightStaff) {
2152             staff = nextRightStaff;
2153             setCurrentStaff(staff);
2154             nextRightStaff = scene->getNextStaffOnTrack();
2155         }
2156     }
2157     m_cursorPosition = staff->getStartTime();
2158     setCurrentStaff(staff);
2159 
2160     // This can take a very long time.  But there is no other indicator
2161     // of which notes are in the current Segment.  We should probably
2162     // either gray the notes that aren't in the current Segment, or
2163     // highlight the notes that are.
2164     slotEditSelectWholeStaff();
2165 }
2166 
2167 void
slotCurrentSegmentNext()2168 NotationView::slotCurrentSegmentNext()
2169 {
2170     NotationScene *scene = m_notationWidget->getScene();
2171     if (!scene) return;
2172     NotationStaff *staff = scene->getNextStaffOnTrack();
2173     if (!staff) {
2174         // move to next staff below this one
2175         staff = scene->getStaffBelow(0);
2176         if (!staff) return;
2177         // make sure it is the first
2178         NotationStaff *nextLeftStaff = staff;
2179         while (nextLeftStaff) {
2180             staff = nextLeftStaff;
2181             setCurrentStaff(staff);
2182             nextLeftStaff = scene->getPriorStaffOnTrack();
2183         }
2184     }
2185     m_cursorPosition = staff->getStartTime();
2186     setCurrentStaff(staff);
2187 
2188     // This can take a very long time.  But there is no other indicator
2189     // of which notes are in the current Segment.  We should probably
2190     // either gray the notes that aren't in the current Segment, or
2191     // highlight the notes that are.
2192     slotEditSelectWholeStaff();
2193 }
2194 
2195 void
setCurrentStaff(NotationStaff * staff)2196 NotationView::setCurrentStaff(NotationStaff *staff)
2197 {
2198     if (!staff) return;
2199     NotationScene *scene = m_notationWidget->getScene();
2200     if (!scene) return;
2201 
2202     if (findAdopted(&staff->getSegment()) != m_adoptedSegments.end())
2203         { enterActionState("focus_adopted_segment"); }
2204     else
2205         { leaveActionState("focus_adopted_segment"); }
2206 
2207     scene->setCurrentStaff(staff);
2208 }
2209 
2210 void
slotToggleGeneralToolBar()2211 NotationView::slotToggleGeneralToolBar()
2212 {
2213     toggleNamedToolBar("General Toolbar");
2214 }
2215 
2216 void
slotToggleToolsToolBar()2217 NotationView::slotToggleToolsToolBar()
2218 {
2219     toggleNamedToolBar("Tools Toolbar");
2220 }
2221 
2222 void
slotToggleDurationToolBar()2223 NotationView::slotToggleDurationToolBar()
2224 {
2225     toggleNamedToolBar("Duration Toolbar");
2226 }
2227 
2228 void
slotToggleInterpretToolBar()2229 NotationView::slotToggleInterpretToolBar()
2230 {
2231     toggleNamedToolBar("Interpret Toolbar");
2232 }
2233 
2234 void
slotToggleAccidentalsToolBar()2235 NotationView::slotToggleAccidentalsToolBar()
2236 {
2237     toggleNamedToolBar("Accidentals Toolbar");
2238 }
2239 
2240 void
slotToggleClefsToolBar()2241 NotationView::slotToggleClefsToolBar()
2242 {
2243     toggleNamedToolBar("Clefs Toolbar");
2244 }
2245 
2246 void
slotToggleMarksToolBar()2247 NotationView::slotToggleMarksToolBar()
2248 {
2249     toggleNamedToolBar("Marks Toolbar");
2250 }
2251 
2252 void
slotToggleGroupToolBar()2253 NotationView::slotToggleGroupToolBar()
2254 {
2255     toggleNamedToolBar("Group Toolbar");
2256 }
2257 
2258 void
slotToggleSymbolsToolBar()2259 NotationView::slotToggleSymbolsToolBar()
2260 {
2261     toggleNamedToolBar("Symbols Toolbar");
2262 }
2263 
2264 void
slotToggleLayoutToolBar()2265 NotationView::slotToggleLayoutToolBar()
2266 {
2267     toggleNamedToolBar("Layout Toolbar");
2268 }
2269 
2270 void
slotToggleRulersToolBar()2271 NotationView::slotToggleRulersToolBar()
2272 {
2273     toggleNamedToolBar("Rulers Toolbar");
2274 }
2275 
2276 void
slotToggleTransportToolBar()2277 NotationView::slotToggleTransportToolBar()
2278 {
2279     toggleNamedToolBar("Transport Toolbar");
2280 }
2281 
2282 void
slotToggleLayerToolBar()2283 NotationView::slotToggleLayerToolBar()
2284 {
2285     toggleNamedToolBar("Layer Toolbar");
2286 }
2287 
2288 void
toggleNamedToolBar(const QString & toolBarName,bool * force)2289 NotationView::toggleNamedToolBar(const QString& toolBarName, bool* force)
2290 {
2291 //     QToolBar *namedToolBar = toolBar(toolBarName);
2292     QToolBar *namedToolBar = findChild<QToolBar*>(toolBarName);
2293 
2294     if (!namedToolBar) {
2295         NOTATION_DEBUG << "NotationView::toggleNamedToolBar() : toolBar "
2296                        << toolBarName << " not found";
2297         return ;
2298     }
2299 
2300     if (!force) {
2301 
2302         if (namedToolBar->isVisible())
2303             namedToolBar->hide();
2304         else
2305             namedToolBar->show();
2306     } else {
2307 
2308         if (*force)
2309             namedToolBar->show();
2310         else
2311             namedToolBar->hide();
2312     }
2313 
2314 //     setSettingsDirty();    //&&& not required ?
2315 
2316 }
2317 
2318 void
slotSetSelectTool()2319 NotationView::slotSetSelectTool()
2320 {
2321     if (m_notationWidget) m_notationWidget->slotSetSelectTool();
2322     slotUpdateMenuStates();
2323 }
2324 
2325 void
slotSetSelectNoTiesTool()2326 NotationView::slotSetSelectNoTiesTool()
2327 {
2328     if (m_notationWidget) m_notationWidget->slotSetSelectNoTiesTool();
2329     slotUpdateMenuStates();
2330 }
2331 
2332 void
slotSetEraseTool()2333 NotationView::slotSetEraseTool()
2334 {
2335     if (m_notationWidget) m_notationWidget->slotSetEraseTool();
2336     slotUpdateMenuStates();
2337 }
2338 
2339 void
slotSetNoteRestInserter()2340 NotationView::slotSetNoteRestInserter()
2341 {
2342     NOTATION_DEBUG << "NotationView::slotSetNoteRestInserter : entered. ";
2343 
2344     if (m_notationWidget) m_notationWidget->slotSetNoteRestInserter();
2345 
2346     //Must ensure it is set since may be called from multiple actions.
2347     findAction("draw")->setChecked(true);
2348     slotUpdateMenuStates();
2349 }
2350 
2351 void
slotSwitchToNotes()2352 NotationView::slotSwitchToNotes()
2353 {
2354     NOTATION_DEBUG << "NotationView::slotSwitchToNotes : entered. ";
2355 
2356     QString actionName = "";
2357     NoteRestInserter *currentInserter = nullptr;
2358     if (m_notationWidget) {
2359         currentInserter = dynamic_cast<NoteRestInserter *>
2360             (m_notationWidget->getCurrentTool());
2361 
2362         if (!currentInserter) {
2363             // Switch to NoteRestInserter
2364             slotSetNoteRestInserter();
2365             NOTATION_DEBUG << "NotationView::slotSwitchToNotes() : "
2366                     << "NoteRestInserter not current. Attempted to  switch. ";
2367             //Try again to see if tool is set.
2368             currentInserter = dynamic_cast<NoteRestInserter *>
2369                     (m_notationWidget->getCurrentTool());
2370             if (!currentInserter) {
2371                 NOTATION_DEBUG << "NotationView::slotSwitchToNotes() : expected"
2372                         << " NoteRestInserter as current tool & "
2373                         << "could not switch to it.  Silent exit.";
2374                 return;
2375             }
2376         }
2377 
2378         Note::Type unitType = currentInserter->getCurrentNote()
2379             .getNoteType();
2380         int dots = (currentInserter->getCurrentNote().getDots() ? 1 : 0);
2381         actionName = NotationStrings::getReferenceName(Note(unitType,dots));
2382         actionName.replace(QRegularExpression("-"), "_");
2383 
2384         m_notationWidget->slotSetNoteInserter();
2385     }
2386 
2387     //Must set duration_ shortcuts to false to fix bug when in rest mode
2388     // and a duration shortcut key is pressed (or selected from dur. menu).
2389     findAction(QString("duration_%1").arg(actionName))->setChecked(false);
2390     QAction *currentAction = findAction(actionName);
2391     currentAction->setChecked(true);
2392 
2393     // This code and last line above used to maintain exclusive state
2394     // of the Duration Toolbar so we can reactivate the NoteRestInserter
2395     // even when from a pressed button on the bar.
2396 
2397     // Now un-select previous pressed button pressed
2398     if (currentAction != m_durationPressed) {
2399         m_durationPressed->setChecked(false);
2400         m_durationPressed = currentAction;
2401     }
2402 
2403     morphDurationMonobar();
2404 
2405     slotUpdateMenuStates();
2406 }
2407 
2408 void
slotSwitchToRests()2409 NotationView::slotSwitchToRests()
2410 {
2411     NOTATION_DEBUG << "NotationView::slotSwitchToRests : entered. ";
2412 
2413     QString actionName = "";
2414     NoteRestInserter *currentInserter = nullptr;
2415     if (m_notationWidget) {
2416         currentInserter = dynamic_cast<NoteRestInserter *>
2417             (m_notationWidget->getCurrentTool());
2418 
2419         if (!currentInserter) {
2420             // Switch to NoteRestInserter
2421             slotSetNoteRestInserter();
2422             NOTATION_DEBUG << "NotationView::slotSwitchToRests() : "
2423                     << "NoteRestInserter not current. Attempted to  switch. ";
2424 
2425             //Try again to see if tool is set.
2426             currentInserter = dynamic_cast<NoteRestInserter *>
2427                     (m_notationWidget->getCurrentTool());
2428             if (!currentInserter) {
2429                 NOTATION_DEBUG << "NotationView::slotSwitchToRests() : expected"
2430                         << " NoteRestInserter as current tool & "
2431                         << "could not switch to it.  Silent exit.";
2432                 return;
2433             }
2434         }
2435 
2436         Note::Type unitType = currentInserter->getCurrentNote()
2437             .getNoteType();
2438         int dots = (currentInserter->getCurrentNote().getDots() ? 1 : 0);
2439         actionName = NotationStrings::getReferenceName(Note(unitType,dots));
2440         actionName.replace(QRegularExpression("-"), "_");
2441 
2442         m_notationWidget->slotSetRestInserter();
2443     }
2444 
2445     //Must set duration_ shortcuts to false to fix bug when in rest mode
2446     // and a duration shortcut key is pressed (or selected from dur. menu).
2447     findAction(QString("duration_%1").arg(actionName))->setChecked(false);
2448     findAction(QString("rest_%1").arg(actionName))->setChecked(true);
2449 
2450 
2451     //Must set duration_ shortcuts to false to fix bug when in rest mode
2452     // and a duration shortcut key is pressed (or selected from dur. menu).
2453     findAction(QString("duration_%1").arg(actionName))->setChecked(false);
2454     QAction *currentAction = findAction(QString("rest_%1").arg(actionName));
2455     currentAction->setChecked(true);
2456 
2457     // This code and last line above used to maintain exclusive state
2458     // of the Duration Toolbar so we can reactivate the NoteRestInserter
2459     // even when from a pressed button on the bar.
2460 
2461     // Now un-select previous pressed button pressed
2462     if (currentAction != m_durationPressed) {
2463         m_durationPressed->setChecked(false);
2464         m_durationPressed = currentAction;
2465     }
2466 
2467     morphDurationMonobar();
2468 
2469     slotUpdateMenuStates();
2470 }
2471 
2472 void
morphDurationMonobar()2473 NotationView::morphDurationMonobar()
2474 {
2475     NOTATION_DEBUG << "NotationView::morphDurationMonobar : entered. ";
2476 
2477     NoteRestInserter *currentInserter = nullptr;
2478     if (m_notationWidget) {
2479         currentInserter = dynamic_cast<NoteRestInserter *>
2480         (m_notationWidget->getCurrentTool());
2481     }
2482 
2483     if (!currentInserter)
2484     {
2485         // Morph called when NoteRestInserter not set as current tool
2486         NOTATION_DEBUG << "NotationView::morphNotationToolbar() : expected"
2487                << " NoteRestInserter.  Silent Exit."
2488               ;
2489         return;
2490 
2491     }
2492     // Retrieve duration and dot values
2493     int dots = currentInserter->getCurrentNote().getDots();
2494     Note::Type note = currentInserter->getCurrentNote().getNoteType();
2495 
2496     // Determine duration tooolbar mode
2497     DurationMonobarModeType newMode = InsertingNotes;
2498     if (currentInserter->isaRestInserter()) {
2499         newMode = (dots ? InsertingDottedRests : InsertingRests);
2500     } else {
2501         newMode = (dots ? InsertingDottedNotes : InsertingNotes);
2502     }
2503 
2504     //Convert to English for debug purposes.
2505     std::string modeStr;
2506     switch (newMode) {
2507 
2508     case InsertingNotes: modeStr = "Notes Toolbar"; break;
2509     case InsertingDottedNotes: modeStr = "Dotted Notes Toolbar"; break;
2510     case InsertingRests: modeStr = "Rests Toolbar"; break;
2511     case InsertingDottedRests: modeStr = "Dotted Rests Toolbar"; break;
2512     default: modeStr = "WTF?  This won't be pretty.";
2513 
2514     }
2515     NOTATION_DEBUG << "NotationView::morphDurationMonobar: morphing to "
2516         << modeStr;
2517 
2518     if (newMode == m_durationMode && note != Note::Shortest && dots) {
2519         NOTATION_DEBUG << "NotationView::morphDurationMonobar: new "
2520             << "mode and last mode are the same.  exit wothout morphing."
2521            ;
2522         return;
2523     }
2524 
2525     // Turn off current state (or last state--depending on perspective.)
2526     switch (m_durationMode) {
2527 
2528     case InsertingNotes:
2529         leaveActionState("note_0_dot_mode");
2530         break;
2531 
2532     case InsertingDottedNotes:
2533         leaveActionState("note_1_dot_mode");
2534         break;
2535 
2536     case InsertingRests:
2537         leaveActionState("rest_0_dot_mode");
2538         break;
2539 
2540     case InsertingDottedRests:
2541         leaveActionState("rest_1_dot_mode");
2542         break;
2543 
2544     default:
2545         NOTATION_DEBUG << "NotationView::morphDurationMonobar:  None of "
2546             << "The standard four modes were selected for m_durationMode. "
2547             << "How did that happen?";
2548     }
2549 
2550     // transfer new mode to member for next recall.
2551     m_durationMode = newMode;
2552 
2553     // Now morph to new state.
2554     switch (newMode) {
2555 
2556     case InsertingNotes:
2557         enterActionState("note_0_dot_mode");
2558         break;
2559 
2560     case InsertingDottedNotes:
2561         enterActionState("note_1_dot_mode");
2562         break;
2563 
2564     case InsertingRests:
2565         enterActionState("rest_0_dot_mode");
2566         break;
2567 
2568     case InsertingDottedRests:
2569         enterActionState("rest_1_dot_mode");
2570         break;
2571     default:
2572         NOTATION_DEBUG << "NotationView::morphDurationMonobar:  None of "
2573             << "The standard four modes were selected for newMode. "
2574             << "How did that happen?";
2575     }
2576 
2577     // This code to manage shortest dotted note selection.
2578     // Disable the shortcut in the menu for shortest duration.
2579     if (note == Note::Shortest && !dots) {
2580         NOTATION_DEBUG << "NotationView::morphDurationMonobar:  shortest "
2581             << "note / no dots.  disable off +. action";
2582         QAction *switchDots = findAction("switch_dots_on");
2583         switchDots->setEnabled(false);
2584     }
2585 }
2586 
2587 void
initializeNoteRestInserter()2588 NotationView::initializeNoteRestInserter()
2589 {
2590     // Set Default Duration based on Time Signature denominator.
2591     // The default unitType is taken from the denominator of the time signature:
2592     //   e.g. 4/4 -> 1/4, 6/8 -> 1/8, 2/2 -> 1/2.
2593     TimeSignature sig = getDocument()->getComposition().getTimeSignatureAt(getInsertionTime());
2594     Note::Type unitType = sig.getUnit();
2595 
2596     QString actionName = NotationStrings::getReferenceName(Note(unitType,0));
2597     actionName.replace(QRegularExpression("-"), "_");
2598 
2599     //Initialize Duration Toolbar (hide all buttons)
2600     leaveActionState("note_0_dot_mode");
2601     leaveActionState("note_1_dot_mode");
2602     leaveActionState("rest_0_dot_mode");
2603     leaveActionState("rest_1_dot_mode");
2604 
2605     //Change exclusive settings so we can retrigger Duration Toolbar
2606     //actions when button needed is pressed.
2607     //exclusive state maintianed via slotSwitchToRests() / slotSwitchToNotes().
2608     findGroup("duration_toolbar")->setExclusive(false);
2609 
2610 
2611     // Initialize the m_durationPressed so we don't have to null check elswhere.
2612     m_durationPressed = findAction(QString("duration_%1").arg(actionName));
2613 
2614     // Counting on a InsertingRests to be stored in NoteRestInserter::
2615     // m_durationMode which it was passed in the constructor.  This will
2616     // ensure morphDurationMonobar always fires correctly since
2617     // a duration_ shortcut is always tied to the note palette.
2618     m_durationPressed->trigger();
2619 
2620     //Change exclusive settings so we can retrigger Accidental Toolbar
2621     //actions when button needed is pressed.
2622     //exclusive state maintianed via manageAccidentalAction().
2623     findGroup("accidentals")->setExclusive(false);
2624 
2625     // Initialize the m_durationPressed so we don't have to null check elswhere.
2626     m_accidentalPressed = findAction("no_accidental");
2627 }
2628 
2629 int
getPitchFromNoteInsertAction(QString name,Accidental & accidental,const Clef & clef,const Rosegarden::Key & key)2630 NotationView::getPitchFromNoteInsertAction(QString name,
2631                                               Accidental &accidental,
2632                                               const Clef &clef,
2633                                               const Rosegarden::Key &key)
2634 {
2635     using namespace Accidentals;
2636 
2637     accidental = NoAccidental;
2638 
2639     if (name.left(7) == "insert_") {
2640 
2641         name = name.right(name.length() - 7);
2642 
2643         // int modify = 0;
2644         int octave = 0;
2645 
2646         if (name.right(5) == "_high") {
2647 
2648             octave = 1;
2649             name = name.left(name.length() - 5);
2650 
2651         } else if (name.right(4) == "_low") {
2652 
2653             octave = -1;
2654             name = name.left(name.length() - 4);
2655         }
2656 
2657         if (name.right(6) == "_sharp") {
2658 
2659             // modify = 1;
2660             accidental = Sharp;
2661             name = name.left(name.length() - 6);
2662 
2663         } else if (name.right(5) == "_flat") {
2664 
2665             // modify = -1;
2666             accidental = Flat;
2667             name = name.left(name.length() - 5);
2668         }
2669 
2670         int scalePitch = name.toInt();
2671 
2672         if (scalePitch < 0 || scalePitch > 7) {
2673             RG_WARNING << "NotationView::getPitchFromNoteInsertAction: pitch "
2674                       << scalePitch << " out of range, using 0";
2675             scalePitch = 0;
2676         }
2677 
2678         Pitch clefPitch(clef.getAxisHeight(), clef, key, NoAccidental);
2679 
2680         int clefOctave = clefPitch.getOctave();
2681         int pitchOctave = clefOctave + octave;
2682 
2683         NOTATION_DEBUG << "NotationView::getPitchFromNoteInsertAction:"
2684                        << " key = " << key.getName()
2685                        << ", clef = " << clef.getClefType()
2686                        << ", octaveoffset = " << clef.getOctaveOffset()
2687                       ;
2688         NOTATION_DEBUG << "NotationView::getPitchFromNoteInsertAction: octave = "
2689                        << pitchOctave;
2690 
2691         // Rewrite to fix bug #2997303 :
2692         //
2693         // We want to make sure that
2694         //    (i) The lowest note in scale (with octave = -1) is drawn below
2695         //        the staff
2696         //    (ii) The highest note in scale (with octave = +1) is drawn above
2697         //         the staff
2698         //
2699         // Let lnh be the height on staff of this lowest note and let hnh be
2700         // the height on staff of this highest note.
2701         //    (iii) hnh = lnh + 7 + 7 + 6 = lnh + 20
2702         //
2703         // (iv) One way to have (i) and (ii) verified is to make the middle
2704         // of lnh and hnh, i.e. (lnh + hnh) / 2, as near as possible of
2705         // the middle of the staff, i.e. 4.
2706         //
2707         // (iii) and (iv) result in lnh being as near as possible of -6,
2708         //    i.e.  -10 < lnh < -2.
2709 
2710         int lowestNoteInScale = 0;
2711         Pitch lowestPitch(lowestNoteInScale, clefOctave - 1, key, NoAccidental);
2712 
2713         int lnh = lowestPitch.getHeightOnStaff(clef, key);
2714         for (; lnh < -9; lnh += 7) pitchOctave++;
2715         for (; lnh > -3; lnh -= 7) pitchOctave--;
2716 
2717         NOTATION_DEBUG << "NotationView::getPitchFromNoteInsertAction: octave = "
2718                        << pitchOctave << " (adjusted)";
2719 
2720         Pitch pitch(scalePitch, pitchOctave, key, accidental);
2721         return pitch.getPerformancePitch();
2722 
2723     } else {
2724 
2725         throw Exception("Not an insert action",
2726                         __FILE__, __LINE__);
2727     }
2728 }
2729 
2730 void
slotExpressionSequence()2731 NotationView::slotExpressionSequence()
2732 {
2733     insertControllerSequence(ControlParameter::getExpression());
2734 }
2735 
2736 void
slotPitchBendSequence()2737 NotationView::slotPitchBendSequence()
2738 {
2739     insertControllerSequence(ControlParameter::getPitchBend());
2740 }
2741 
2742 void
slotControllerSequence()2743 NotationView::slotControllerSequence()
2744 {
2745     ControlRulerWidget *cr = m_notationWidget->getControlsWidget();
2746     if (!cr)
2747         return;
2748 
2749     const ControlParameter *cp = cr->getControlParameter();
2750     if (!cp)
2751     {
2752         QMessageBox::information(
2753                 this,
2754                 tr("Rosegarden"),
2755                 tr("Please select a control ruler first."));
2756 
2757         return;
2758     }
2759 
2760     insertControllerSequence(*cp);
2761 }
2762 
2763 void
2764 NotationView::
insertControllerSequence(const ControlParameter & controlParameter)2765 insertControllerSequence(const ControlParameter &controlParameter)
2766 {
2767     EventSelection *selection = getSelection();
2768 
2769     // No selection?  Bail.
2770     if (!selection)
2771         return;
2772 
2773     const timeT startTime = selection->getStartTime();
2774     const timeT endTime = selection->getEndTime();
2775 
2776     // Times make no sense?  Bail.
2777     if (startTime >= endTime)
2778         return;
2779 
2780     PitchBendSequenceDialog dialog(
2781             this,  // parent
2782             getCurrentSegment(),
2783             controlParameter,
2784             startTime,
2785             endTime);
2786 
2787     dialog.exec();
2788 }
2789 
2790 void
slotInsertNoteFromAction()2791 NotationView::slotInsertNoteFromAction()
2792 {
2793     const QObject *s = sender();
2794     QString name = s->objectName();
2795 
2796     Segment *segment = getCurrentSegment();
2797     if (!segment) return;
2798 
2799     NoteRestInserter *currentInserter = nullptr;
2800     if(m_notationWidget) {
2801         currentInserter = dynamic_cast<NoteRestInserter *>
2802             (m_notationWidget->getCurrentTool());
2803 
2804         if(!currentInserter) {
2805             //set the NoteRestInserter as current
2806             slotSetNoteRestInserter();
2807             //re-fetch the current tool for analysis
2808             currentInserter = dynamic_cast<NoteRestInserter *>
2809                 (m_notationWidget->getCurrentTool());
2810         }
2811 
2812         if (currentInserter) {
2813             if (currentInserter->isaRestInserter()) {
2814                 slotSwitchToNotes();
2815             }
2816             int pitch = 0;
2817             Accidental accidental = Accidentals::NoAccidental;
2818 
2819             timeT insertionTime = getInsertionTime();
2820             Rosegarden::Key key = segment->getKeyAtTime(insertionTime);
2821             Clef clef = segment->getClefAtTime(insertionTime);
2822 
2823             try {
2824 
2825                 RG_DEBUG << "NotationView::slotInsertNoteFromAction: time = "
2826                     << insertionTime << ", key = " << key.getName()
2827                     << ", clef = " << clef.getClefType() << ", octaveoffset = "
2828                     << clef.getOctaveOffset();
2829 
2830                 pitch = getPitchFromNoteInsertAction(name, accidental, clef, key);
2831 
2832             } catch (...) {
2833 
2834                 QMessageBox::warning
2835                     (this, tr("Rosegarden"),  tr("Unknown note insert action %1").arg(name));
2836                 return ;
2837             }
2838 
2839             TmpStatusMsg msg(tr("Inserting note"), this);
2840 
2841             NOTATION_DEBUG << "Inserting note at pitch " << pitch;
2842             currentInserter->insertNote(*segment, insertionTime,
2843                 pitch, accidental, 100); // Velocity hard coded for now.
2844         }
2845     }
2846 }
2847 
2848 void
slotInsertRestFromAction()2849 NotationView::slotInsertRestFromAction()
2850 {
2851     Segment *segment = getCurrentSegment();
2852     if (!segment) return;
2853 
2854     NoteRestInserter *currentInserter = nullptr;
2855     if(m_notationWidget) {
2856         currentInserter = dynamic_cast<NoteRestInserter *>
2857             (m_notationWidget->getCurrentTool());
2858 
2859         if(!currentInserter) {
2860             //set the NoteRestInserter as current
2861             slotSetNoteRestInserter();
2862             //re-fetch the current tool for analysis
2863             currentInserter = dynamic_cast<NoteRestInserter *>
2864                 (m_notationWidget->getCurrentTool());
2865         }
2866 
2867         if (currentInserter) {
2868             if (!currentInserter->isaRestInserter()) {
2869                 slotSwitchToRests();
2870             }
2871            timeT insertionTime = getInsertionTime();
2872 
2873            currentInserter->insertNote(*segment, insertionTime,
2874                0, Accidentals::NoAccidental, true);
2875         }
2876     }
2877 }
2878 
2879 void
slotToggleDot()2880 NotationView::slotToggleDot()
2881 {
2882     NOTATION_DEBUG << "NotationView::slotToggleDot : entered. ";
2883     NoteRestInserter *currentInserter = nullptr;
2884     if (m_notationWidget) {
2885         currentInserter = dynamic_cast<NoteRestInserter *>
2886             (m_notationWidget->getCurrentTool());
2887         if (!currentInserter) {
2888             // Switch to NoteRestInserter
2889             slotSetNoteRestInserter();
2890             NOTATION_DEBUG << "NotationView::slotToggleDot() : "
2891                     << "NoteRestInserter not current. Attempted to  switch. ";
2892 
2893             //Try again to see if tool is set.
2894             currentInserter = dynamic_cast<NoteRestInserter *>
2895                     (m_notationWidget->getCurrentTool());
2896             if (!currentInserter) {
2897 
2898                 NOTATION_DEBUG << "NotationView::slotToggleDot() : expected"
2899                         << " NoteRestInserter as current tool & "
2900                         << "could not switch to it.  Silent exit.";
2901                 return;
2902             }
2903         }
2904         Note note = currentInserter->getCurrentNote();
2905 
2906         Note::Type noteType = note.getNoteType();
2907         int noteDots = (note.getDots() ? 0 : 1); // Toggle the dot state
2908 
2909         if (noteDots && noteType == Note::Shortest)
2910         {
2911             // This might have been invoked via a keboard shortcut or other
2912             // toggling the +. button when the shortest note was pressed.
2913             // RG does not render dotted versions of its shortest duration
2914             // and rounds it up to the next duration.
2915             // Following RG's lead on this makes the inteface feel off since
2916             // This moves the toggle to the next longest duration without
2917             // switching the pallete to dots.
2918             // So just leave the duration alone and don't toggle the dot
2919             // in this case.
2920             noteDots = 0;
2921         }
2922 
2923         QString actionName(NotationStrings::getReferenceName(Note(noteType,noteDots)));
2924         actionName.replace(QRegularExpression("-"), "_");
2925 
2926         m_notationWidget->slotSetInsertedNote(noteType, noteDots);
2927         if (currentInserter->isaRestInserter()) {
2928             slotSwitchToRests();
2929         } else {
2930             slotSwitchToNotes();
2931         }
2932     }
2933 }
2934 
2935 void
slotNoteAction()2936 NotationView::slotNoteAction()
2937 {
2938     NOTATION_DEBUG << "NotationView::slotNoteAction : entered. ";
2939 
2940     QObject *s = sender();
2941     QAction *a = dynamic_cast<QAction *>(s);
2942     QString name = s->objectName();
2943     QString noteToolbarName;
2944 
2945     //Set defaults for duration_ shortcut calls
2946     bool rest = false;
2947     int dots = 0;
2948 
2949     if (m_notationWidget) {
2950         NoteRestInserter *currentTool = dynamic_cast<NoteRestInserter *>
2951             (m_notationWidget->getCurrentTool());
2952         if (!currentTool) {
2953             //Must select NoteRestInserter tool as current Tool
2954             slotSetNoteRestInserter();
2955             //Now re-fetch the current tool for analysis.
2956             currentTool = dynamic_cast<NoteRestInserter *>(m_notationWidget
2957                 ->getCurrentTool());
2958         }
2959         if (name.startsWith("duration_")) {
2960             name = name.replace("duration_", "");
2961             NOTATION_DEBUG << "NotationView::slotNoteAction : "
2962                 << "Duration shortcut called.";
2963             //duration shortcut called from keyboard or menu.
2964             //Must switch to insert Notes mode.
2965 
2966         } else if (currentTool->isaRestInserter()) {
2967             NOTATION_DEBUG << "NotationView::slotNoteAction : "
2968                 << "Have rest inserter.";
2969             if (name.startsWith("rest_")) {
2970                 name = name.replace("rest_", "");
2971             }
2972             rest = true;
2973         } else {
2974             NOTATION_DEBUG << "NotationView::slotNoteAction : "
2975                 << "Have note inserter.";
2976         }
2977     }
2978 
2979     if (name.startsWith("dotted_")) {
2980         dots = 1;
2981         name = name.replace("dotted_", "");
2982     }
2983 
2984     Note::Type type = NotationStrings::getNoteForName(name).getNoteType();
2985     if (m_notationWidget) {
2986         m_notationWidget->slotSetInsertedNote(type, dots);
2987         if (rest) {
2988             slotSwitchToRests();
2989         } else {
2990             slotSwitchToNotes();
2991         }
2992     }
2993 
2994     setCurrentNotePixmapFrom(a);
2995 }
2996 
2997 void
slotDummy1()2998 NotationView::slotDummy1()
2999 {
3000     // Empty function required to appease Qt.
3001 }
3002 
3003 void
manageAccidentalAction(QString actionName)3004 NotationView::manageAccidentalAction(QString actionName)
3005 {
3006      NOTATION_DEBUG << "NotationView::manageAccidentalAction: enter. "
3007          << "actionName = " << actionName << ".";
3008 
3009     // Manage exclusive group setting since group->isExclusive() == false.
3010     QAction *currentAction = findAction(actionName);
3011     // Force the current button to be pressed
3012     currentAction->setChecked(true);
3013     if (m_accidentalPressed != currentAction) {
3014         m_accidentalPressed->setChecked(false);
3015         m_accidentalPressed = currentAction;
3016     }
3017 
3018     // Set The Note / Rest Inserter Tool as curretn tool if needed.
3019     if (m_notationWidget) {
3020         NoteRestInserter *currentInserter = dynamic_cast<NoteRestInserter *>
3021             (m_notationWidget->getCurrentTool());
3022         if (!currentInserter) {
3023             slotSetNoteRestInserter();
3024 
3025             // re-fetch tool for analysis.
3026             currentInserter = dynamic_cast<NoteRestInserter *>
3027             (m_notationWidget->getCurrentTool());
3028         }
3029         if (currentInserter->isaRestInserter()) {
3030             slotSwitchToNotes();
3031         }
3032     }
3033 
3034 }
3035 
3036 void
slotNoAccidental()3037 NotationView::slotNoAccidental()
3038 {
3039     QObject *s = sender();
3040     QString name = s->objectName();
3041 
3042     manageAccidentalAction(name);
3043 
3044     if (m_notationWidget) m_notationWidget->slotSetAccidental(NoAccidental, false);
3045 }
3046 
3047 void
slotFollowAccidental()3048 NotationView::slotFollowAccidental()
3049 {
3050     QObject *s = sender();
3051     QString name = s->objectName();
3052 
3053     manageAccidentalAction(name);
3054 
3055     if (m_notationWidget) m_notationWidget->slotSetAccidental(NoAccidental, true);
3056 }
3057 
3058 void
slotSharp()3059 NotationView::slotSharp()
3060 {
3061     QObject *s = sender();
3062     QString name = s->objectName();
3063 
3064     manageAccidentalAction(name);
3065 
3066     if (m_notationWidget) m_notationWidget->slotSetAccidental(Sharp, false);
3067 }
3068 
3069 void
slotFlat()3070 NotationView::slotFlat()
3071 {
3072     QObject *s = sender();
3073     QString name = s->objectName();
3074 
3075     manageAccidentalAction(name);
3076 
3077     if (m_notationWidget) m_notationWidget->slotSetAccidental(Flat, false);
3078 }
3079 
3080 void
slotNatural()3081 NotationView::slotNatural()
3082 {
3083     QObject *s = sender();
3084     QString name = s->objectName();
3085 
3086     manageAccidentalAction(name);
3087 
3088     if (m_notationWidget) m_notationWidget->slotSetAccidental(Natural, false);
3089 }
3090 
3091 void
slotDoubleSharp()3092 NotationView::slotDoubleSharp()
3093 {
3094     QObject *s = sender();
3095     QString name = s->objectName();
3096 
3097     manageAccidentalAction(name);
3098 
3099     if (m_notationWidget) m_notationWidget->slotSetAccidental(DoubleSharp, false);
3100 }
3101 
3102 void
slotDoubleFlat()3103 NotationView::slotDoubleFlat()
3104 {
3105     QObject *s = sender();
3106     QString name = s->objectName();
3107 
3108     manageAccidentalAction(name);
3109 
3110     if (m_notationWidget) m_notationWidget->slotSetAccidental(DoubleFlat, false);
3111 }
3112 
3113 void
slotClefAction()3114 NotationView::slotClefAction()
3115 {
3116     QObject *s = sender();
3117     QAction *a = dynamic_cast<QAction *>(s);
3118     QString n = s->objectName();
3119 
3120     Clef type = Clef::Treble;
3121 
3122     if (n == "treble_clef") type = Clef::Treble;
3123     else if (n == "alto_clef") type = Clef::Alto;
3124     else if (n == "tenor_clef") type = Clef::Tenor;
3125     else if (n == "bass_clef") type = Clef::Bass;
3126 
3127     setCurrentNotePixmapFrom(a);
3128 
3129     if (!m_notationWidget) return;
3130     m_notationWidget->slotSetClefInserter();
3131     m_notationWidget->slotSetInsertedClef(type);
3132     slotUpdateMenuStates();
3133 }
3134 
3135 void
slotText()3136 NotationView::slotText()
3137 {
3138     QObject *s = sender();
3139     setCurrentNotePixmapFrom(dynamic_cast<QAction *>(s));
3140 
3141     if (!m_notationWidget) return;
3142     m_notationWidget->slotSetTextInserter();
3143     slotUpdateMenuStates();
3144 }
3145 
3146 void
slotGuitarChord()3147 NotationView::slotGuitarChord()
3148 {
3149     QObject *s = sender();
3150     setCurrentNotePixmapFrom(dynamic_cast<QAction *>(s));
3151 
3152     if (!m_notationWidget) return;
3153     m_notationWidget->slotSetGuitarChordInserter();
3154     slotUpdateMenuStates();
3155 }
3156 
3157 void
slotTransformsQuantize()3158 NotationView::slotTransformsQuantize()
3159 {
3160     EventSelection *selection = getSelection();
3161     if (!selection) return;
3162 
3163     QuantizeDialog dialog(this, true);
3164 
3165     if (dialog.exec() == QDialog::Accepted) {
3166         CommandHistory::getInstance()->addCommand
3167              (new EventQuantizeCommand
3168               (*selection,
3169                dialog.getQuantizer()));
3170     }
3171 }
3172 
3173 void
slotTransformsInterpret()3174 NotationView::slotTransformsInterpret()
3175 {
3176     EventSelection *selection = getSelection();
3177     if (!selection) return;
3178 
3179     InterpretDialog dialog(this);
3180     if (dialog.exec() == QDialog::Accepted) {
3181         CommandHistory::getInstance()->addCommand
3182             (new InterpretCommand
3183              (*selection,
3184               getDocument()->getComposition().getNotationQuantizer(),
3185               dialog.getInterpretations()));
3186     }
3187 }
3188 
3189 void
slotMakeOrnament()3190 NotationView::slotMakeOrnament()
3191 {
3192     if (!getSelection())
3193         return ;
3194 
3195     EventSelection::eventcontainer &ec =
3196         getSelection()->getSegmentEvents();
3197 
3198     int basePitch = -1;
3199     int baseVelocity = -1;
3200 
3201     QSharedPointer<NoteStyle> style = NoteStyleFactory::getStyle(NoteStyleFactory::DefaultStyle);
3202 
3203     for (EventSelection::eventcontainer::iterator i =
3204              ec.begin(); i != ec.end(); ++i) {
3205         if ((*i)->isa(Note::EventType)) {
3206             if ((*i)->has(BaseProperties::PITCH)) {
3207                 basePitch = (*i)->get<Int>(BaseProperties::PITCH);
3208                 style = NoteStyleFactory::getStyleForEvent(*i);
3209                 if (baseVelocity != -1) break;
3210             }
3211             if ((*i)->has(BaseProperties::VELOCITY)) {
3212                 baseVelocity = (*i)->get<Int>(BaseProperties::VELOCITY);
3213                 if (basePitch != -1) break;
3214             }
3215         }
3216     }
3217 
3218     Segment *segment = getCurrentSegment();
3219     if (!segment) return;
3220 
3221     timeT absTime = getSelection()->getStartTime();
3222 
3223     Track *track =
3224         segment->getComposition()->getTrackById(segment->getTrack());
3225     QString name;
3226     int barNo = segment->getComposition()->getBarNumber(absTime);
3227     if (track) {
3228         name = QString(tr("Ornament track %1 bar %2").arg(track->getPosition() + 1).arg(barNo + 1));
3229     } else {
3230         name = QString(tr("Ornament bar %1").arg(barNo + 1));
3231     }
3232 
3233     MakeOrnamentDialog dialog(this, name, basePitch);
3234     if (dialog.exec() != QDialog::Accepted)
3235         return ;
3236 
3237     name = dialog.getName();
3238     basePitch = dialog.getBasePitch();
3239 
3240     CommandHistory::getInstance()->
3241         addCommand(new CutToTriggerSegmentCommand
3242                    (getSelection(), getDocument()->getComposition(),
3243                     name, basePitch, baseVelocity,
3244                     style->getName(), true,
3245                     BaseProperties::TRIGGER_SEGMENT_ADJUST_NONE,
3246                     Marks::NoMark));
3247 }
3248 
3249 void
slotUseOrnament()3250 NotationView::slotUseOrnament()
3251 {
3252     // Take an existing note and match an ornament to it.
3253 
3254     if (!getSelection())
3255         return ;
3256 
3257     UseOrnamentDialog dialog(this, &getDocument()->getComposition());
3258     if (dialog.exec() != QDialog::Accepted)
3259         return ;
3260 
3261     CommandHistory::getInstance()->addCommand(
3262             new SetTriggerCommand(*getSelection(),
3263                                   dialog.getId(),
3264                                   true,
3265                                   dialog.getRetune(),
3266                                   dialog.getTimeAdjust(),
3267                                   dialog.getMark(),
3268                                   tr("Use Ornament")));
3269 }
3270 
3271 void
slotRemoveOrnament()3272 NotationView::slotRemoveOrnament()
3273 {
3274     if (!getSelection())
3275         return ;
3276 
3277     CommandHistory::getInstance()->addCommand(
3278             new ClearTriggersCommand(*getSelection(),
3279                                      tr("Remove Ornaments")));
3280 }
3281 
3282 void
slotEditOrnamentInline()3283 NotationView::slotEditOrnamentInline()
3284 {
3285     ForAllSelection(&NotationView::EditOrnamentInline);
3286 }
3287 
3288 void
slotShowOrnamentExpansion()3289 NotationView::slotShowOrnamentExpansion()
3290 {
3291     ForAllSelection(&NotationView::ShowOrnamentExpansion);
3292 }
3293 
3294 void
EditOrnamentInline(Event * trigger,Segment * containing)3295 NotationView::EditOrnamentInline(Event *trigger, Segment *containing)
3296 {
3297     TriggerSegmentRec *rec =
3298         getDocument()->getComposition().getTriggerSegmentRec(trigger);
3299 
3300     if (!rec) { return; }
3301     Segment *link = rec->makeLinkedSegment(trigger, containing);
3302 
3303     // makeLinkedSegment can return nullptr, eg if ornament was squashed.
3304     if (!link) { return; }
3305 
3306     link->setParticipation(Segment::editableClone);
3307     // The same track the host segment had
3308     link->setTrack(containing->getTrack());
3309     // Give it a composition so it doesn't get into trouble.
3310     link->setComposition(&getDocument()->getComposition());
3311 
3312     // Adopt it into the view.
3313     CommandHistory::getInstance()->addCommand
3314         (new AdoptSegmentCommand
3315          (tr("Edit ornament inline"), *this, link, true));
3316 }
3317 
3318 
3319 void
ShowOrnamentExpansion(Event * trigger,Segment * containing)3320 NotationView::ShowOrnamentExpansion(Event *trigger, Segment *containing)
3321 {
3322     TriggerSegmentRec *rec =
3323         getDocument()->getComposition().getTriggerSegmentRec(trigger);
3324     if (!rec) { return; }
3325     Instrument *instrument = getDocument()->getInstrument(containing);
3326 
3327     Segment *s =
3328         rec->makeExpansion(trigger, containing, instrument);
3329 
3330     if (!s) { return; }
3331 
3332     s->setParticipation(Segment::readOnly);
3333     s->setGreyOut();
3334     // The same track the host segment had
3335     s->setTrack(containing->getTrack());
3336     s->setComposition(&getDocument()->getComposition());
3337     s->normalizeRests(s->getStartTime(), s->getEndTime());
3338 
3339     // Adopt it into the view.
3340     CommandHistory::getInstance()->addCommand
3341         (new AdoptSegmentCommand
3342          (tr("Show ornament expansion"), *this, s, true));
3343 }
3344 
3345 void
3346 NotationView::
ForAllSelection(opOnEvent op)3347 ForAllSelection(opOnEvent op)
3348 {
3349     EventSelection *selection = getSelection();
3350     if (!selection) { return; }
3351 
3352     EventSelection::eventcontainer ec =
3353         selection->getSegmentEvents();
3354 
3355     for (EventSelection::eventcontainer::iterator i = ec.begin();
3356          i != ec.end();
3357          ++i) {
3358         CALL_MEMBER_FN(*this,op)(*i, getCurrentSegment());
3359     }
3360 }
3361 
3362 void
3363 NotationView::
slotUnadoptSegment()3364 slotUnadoptSegment()
3365 {
3366     // unadoptSegment checks this too, but we check now so that (a) we
3367     // don't have a did-nothing command on the history, and (b)
3368     // because undoing that command would be very wrong.
3369     SegmentVector::iterator found = findAdopted(getCurrentSegment());
3370 
3371     if (found == m_adoptedSegments.end()) { return; }
3372 
3373     CommandHistory::getInstance()->addCommand
3374         (new AdoptSegmentCommand
3375          (tr("Unadopt Segment"), *this, *found, false));
3376 }
3377 
3378 void
slotMaskOrnament()3379 NotationView::slotMaskOrnament()
3380 {
3381     if (!getSelection())
3382         { return; }
3383 
3384     CommandHistory::getInstance()->addCommand
3385         (new MaskTriggerCommand(*getSelection(), false));
3386 }
3387 
3388 void
slotUnmaskOrnament()3389 NotationView::slotUnmaskOrnament()
3390 {
3391     if (!getSelection())
3392         { return; }
3393 
3394     CommandHistory::getInstance()->addCommand
3395         (new MaskTriggerCommand(*getSelection(), true));
3396 }
3397 
3398 void
slotEditAddClef()3399 NotationView::slotEditAddClef()
3400 {
3401     Segment *segment = getCurrentSegment();
3402     timeT insertionTime = getInsertionTime();
3403     static Clef lastClef = segment->getClefAtTime(insertionTime);
3404 
3405     NotationScene *scene = m_notationWidget->getScene();
3406     if (!scene) return;
3407 
3408     // Fix bug #2997311 : don't use a NotePixmapFactory in selection mode
3409     // to draw inside the dialog
3410     NotePixmapFactory npf = *scene->getNotePixmapFactory();
3411     npf.setSelected(false);
3412 
3413     ClefDialog dialog(this, &npf, lastClef);
3414 
3415     if (dialog.exec() == QDialog::Accepted) {
3416 
3417         ClefDialog::ConversionType conversion = dialog.getConversionType();
3418 
3419         bool shouldChangeOctave = (conversion != ClefDialog::NoConversion);
3420         bool shouldTranspose = (conversion == ClefDialog::Transpose);
3421 
3422         CommandHistory::getInstance()->addCommand(
3423                 new ClefInsertionCommand(*segment,
3424                                          insertionTime,
3425                                          dialog.getClef(),
3426                                          shouldChangeOctave,
3427                                          shouldTranspose));
3428 
3429         lastClef = dialog.getClef();
3430     }
3431 }
3432 
3433 void
slotEditAddClefLinkOnly()3434 NotationView::slotEditAddClefLinkOnly()
3435 {
3436     Segment *segment = getCurrentSegment();
3437     if (!segment->isLinked()) {
3438         return;
3439     }
3440     timeT insertionTime = getInsertionTime();
3441     static Clef lastClef = segment->getClefAtTime(insertionTime);
3442 
3443     NotationScene *scene = m_notationWidget->getScene();
3444     if (!scene) return;
3445 
3446     NotePixmapFactory npf = *scene->getNotePixmapFactory();
3447     npf.setSelected(false);
3448 
3449     ClefDialog dialog(this, &npf, lastClef);
3450 
3451     if (dialog.exec() == QDialog::Accepted) {
3452 
3453         ClefDialog::ConversionType conversion = dialog.getConversionType();
3454 
3455         bool shouldChangeOctave = (conversion != ClefDialog::NoConversion);
3456         bool shouldTranspose = (conversion == ClefDialog::Transpose);
3457 
3458         CommandHistory::getInstance()->addCommand(
3459                 new ClefLinkInsertionCommand(*segment,
3460                                             insertionTime,
3461                                             dialog.getClef(),
3462                                             shouldChangeOctave,
3463                                             shouldTranspose));
3464 
3465         lastClef = dialog.getClef();
3466     }
3467 }
3468 
3469 void
slotEditAddKeySignature()3470 NotationView::slotEditAddKeySignature()
3471 {
3472     Segment *segment = getCurrentSegment();
3473     timeT insertionTime = getInsertionTime();
3474     Clef clef = segment->getClefAtTime(insertionTime);
3475     Key key = AnalysisHelper::guessKeyForSegment(insertionTime, segment);
3476 
3477     NotationScene *scene = m_notationWidget->getScene();
3478     if (!scene) return;
3479 
3480     // Fix bug #2997311 : don't use a NotePixmapFactory in selection mode
3481     // to draw inside the dialog
3482     NotePixmapFactory npf = *scene->getNotePixmapFactory();
3483     npf.setSelected(false);
3484 
3485     KeySignatureDialog dialog(this,
3486                               &npf,
3487                               clef,
3488                               key,
3489                               true,
3490                               true,
3491                               tr("Estimated key signature shown"));
3492 
3493     if (dialog.exec() == QDialog::Accepted &&
3494         dialog.isValid()) {
3495 
3496         KeySignatureDialog::ConversionType conversion =
3497             dialog.getConversionType();
3498 
3499         bool transposeKey = dialog.shouldBeTransposed();
3500         bool applyToAll = dialog.shouldApplyToAll();
3501         bool ignorePercussion = dialog.shouldIgnorePercussion();
3502 
3503         if (applyToAll) {
3504             CommandHistory::getInstance()->addCommand(
3505                     new MultiKeyInsertionCommand(
3506                             getDocument(),
3507                             insertionTime, dialog.getKey(),
3508                             conversion == KeySignatureDialog::Convert,
3509                             conversion == KeySignatureDialog::Transpose,
3510                             transposeKey,
3511                             ignorePercussion));
3512         } else {
3513             CommandHistory::getInstance()->addCommand(
3514                     new KeyInsertionCommand(*segment,
3515                                             insertionTime,
3516                                             dialog.getKey(),
3517                                             conversion == KeySignatureDialog::Convert,
3518                                             conversion == KeySignatureDialog::Transpose,
3519                                             transposeKey,
3520                                             false));
3521         }
3522     }
3523 }
3524 
3525 void
slotEditAddSustain(bool down)3526 NotationView::slotEditAddSustain(bool down)
3527 {
3528     Segment *segment = getCurrentSegment();
3529     timeT insertionTime = getInsertionTime();
3530 
3531     Studio *studio = &getDocument()->getStudio();
3532     Track *track = segment->getComposition()->getTrackById(segment->getTrack());
3533 
3534     if (track) {
3535 
3536         Instrument *instrument = studio->getInstrumentById
3537             (track->getInstrument());
3538         if (instrument) {
3539             MidiDevice *device = dynamic_cast<MidiDevice *>
3540                 (instrument->getDevice());
3541             if (device) {
3542                 for (ControlList::const_iterator i =
3543                          device->getControlParameters().begin();
3544                      i != device->getControlParameters().end(); ++i) {
3545 
3546                     if (i->getType() == Controller::EventType &&
3547                         (i->getName() == "Sustain" ||
3548                          strtoqstr(i->getName()) == tr("Sustain"))) {
3549 
3550                         CommandHistory::getInstance()->addCommand(
3551                                 new SustainInsertionCommand(*segment, insertionTime, down,
3552                                                             i->getControllerNumber()));
3553                         return ;
3554                     }
3555                 }
3556             } else if (instrument->getDevice() &&
3557                        instrument->getDevice()->getType() == Device::SoftSynth) {
3558                 CommandHistory::getInstance()->addCommand(
3559                         new SustainInsertionCommand(*segment, insertionTime, down, 64));
3560                 return;
3561             }
3562         }
3563     }
3564 
3565     QMessageBox::warning(this, tr("Rosegarden"), tr("There is no sustain controller defined for this device.\nPlease ensure the device is configured correctly in the Manage MIDI Devices dialog in the main window."));
3566 }
3567 
3568 void
slotEditAddSustainDown()3569 NotationView::slotEditAddSustainDown()
3570 {
3571     slotEditAddSustain(true);
3572 }
3573 
3574 void
slotEditAddSustainUp()3575 NotationView::slotEditAddSustainUp()
3576 {
3577     slotEditAddSustain(false);
3578 }
3579 
3580 void
slotEditTranspose()3581 NotationView::slotEditTranspose()
3582 {
3583     IntervalDialog intervalDialog(this, true, true);
3584     int ok = intervalDialog.exec();
3585 
3586     int semitones = intervalDialog.getChromaticDistance();
3587     int steps = intervalDialog.getDiatonicDistance();
3588 
3589     if (!ok || (semitones == 0 && steps == 0)) return;
3590 
3591     // TODO combine commands into one
3592     for (size_t i = 0; i < m_segments.size(); i++)
3593     {
3594         CommandHistory::getInstance()->addCommand(new SegmentTransposeCommand(
3595                 *(m_segments[i]),
3596                 intervalDialog.getChangeKey(), steps, semitones,
3597                 intervalDialog.getTransposeSegmentBack()));
3598     }
3599 }
3600 
3601 void
slotEditSwitchPreset()3602 NotationView::slotEditSwitchPreset()
3603 {
3604     PresetHandlerDialog dialog(this, true);
3605 
3606     if (dialog.exec() != QDialog::Accepted) return;
3607 
3608     if (dialog.getConvertAllSegments()) {
3609         // get all segments for this track and convert them.
3610         Composition& comp = getDocument()->getComposition();
3611         TrackId selectedTrack = getCurrentSegment()->getTrack();
3612 
3613         // satisfy #1885251 the way that seems most reasonble to me at the
3614         // moment, only changing track parameters when acting on all segments on
3615         // this track from the notation view
3616         //
3617         //!!! This won't be undoable, and I'm not sure if that's seriously
3618         // wrong, or just mildly wrong, but I'm betting somebody will tell me
3619         // about it if this was inappropriate
3620         Track *track = comp.getTrackById(selectedTrack);
3621         track->setPresetLabel( qstrtostr(dialog.getName()) );
3622         track->setClef(dialog.getClef());
3623         track->setTranspose(dialog.getTranspose());
3624         track->setLowestPlayable(dialog.getLowRange());
3625         track->setHighestPlayable(dialog.getHighRange());
3626 
3627         CommandHistory::getInstance()->addCommand(new SegmentSyncCommand(
3628                             comp.getSegments(), selectedTrack,
3629                             dialog.getTranspose(),
3630                             dialog.getLowRange(),
3631                             dialog.getHighRange(),
3632                             clefIndexToClef(dialog.getClef())));
3633 
3634         comp.notifyTrackChanged(track);
3635 
3636     } else {
3637         CommandHistory::getInstance()->addCommand(new SegmentSyncCommand(
3638                             m_segments,
3639                             dialog.getTranspose(),
3640                             dialog.getLowRange(),
3641                             dialog.getHighRange(),
3642                             clefIndexToClef(dialog.getClef())));
3643     }
3644 
3645     m_doc->slotDocumentModified();
3646 }
3647 
3648 void
slotToggleChordsRuler()3649 NotationView::slotToggleChordsRuler()
3650 {
3651     bool visible = findAction("show_chords_ruler")->isChecked();
3652 
3653     m_notationWidget->setChordNameRulerVisible(visible);
3654 
3655     QSettings settings;
3656     settings.beginGroup(NotationViewConfigGroup);
3657     settings.setValue("Chords ruler shown", visible);
3658     settings.endGroup();
3659 }
3660 
3661 void
slotToggleTempoRuler()3662 NotationView::slotToggleTempoRuler()
3663 {
3664     bool visible = findAction("show_tempo_ruler")->isChecked();
3665 
3666     m_notationWidget->setTempoRulerVisible(visible);
3667 
3668     QSettings settings;
3669     settings.beginGroup(NotationViewConfigGroup);
3670     settings.setValue("Tempo ruler shown", visible);
3671     settings.endGroup();
3672 }
3673 
3674 void
slotAddTempo()3675 NotationView::slotAddTempo()
3676 {
3677     const timeT insertionTime = getInsertionTime();
3678     EditTempoController::self()->editTempo(this, insertionTime);
3679 }
3680 
3681 void
slotAddTimeSignature()3682 NotationView::slotAddTimeSignature()
3683 {
3684     Segment *segment = getCurrentSegment();
3685     if (!segment)
3686         return ;
3687     Composition *composition = segment->getComposition();
3688     timeT insertionTime = getInsertionTime();
3689 
3690     TimeSignatureDialog *dialog = nullptr;
3691     int timeSigNo = composition->getTimeSignatureNumberAt(insertionTime);
3692 
3693     if (timeSigNo >= 0) {
3694 
3695         dialog = new TimeSignatureDialog
3696                 (this, composition, insertionTime,
3697                  composition->getTimeSignatureAt(insertionTime));
3698 
3699     } else {
3700 
3701         timeT endTime = composition->getDuration();
3702         if (composition->getTimeSignatureCount() > 0) {
3703             endTime = composition->getTimeSignatureChange(0).first;
3704         }
3705 
3706         CompositionTimeSliceAdapter adapter
3707                 (composition, insertionTime, endTime);
3708         AnalysisHelper helper;
3709         TimeSignature timeSig = helper.guessTimeSignature(adapter);
3710 
3711         dialog = new TimeSignatureDialog
3712                 (this, composition, insertionTime, timeSig, false,
3713                  tr("Estimated time signature shown"));
3714     }
3715 
3716     if (dialog->exec() == QDialog::Accepted) {
3717 
3718         insertionTime = dialog->getTime();
3719 
3720         if (dialog->shouldNormalizeRests()) {
3721 
3722             CommandHistory::getInstance()->addCommand(new AddTimeSignatureAndNormalizeCommand
3723                     (composition, insertionTime,
3724                      dialog->getTimeSignature()));
3725 
3726         } else {
3727 
3728             CommandHistory::getInstance()->addCommand(new AddTimeSignatureCommand
3729                     (composition, insertionTime,
3730                      dialog->getTimeSignature()));
3731         }
3732     }
3733 
3734     delete dialog;
3735 }
3736 
3737 // check composition for parallels
3738 
3739 void
slotCheckForParallels()3740 NotationView::slotCheckForParallels()
3741 {
3742     qDebug() << "check for parallels...";
3743 
3744     Segment *segment = getCurrentSegment();
3745 
3746     if (!segment) return ;
3747 
3748     Composition *composition = segment->getComposition();
3749 
3750     CheckForParallelsDialog *dialog = nullptr;
3751 
3752     dialog = new CheckForParallelsDialog(this, m_document, m_notationWidget->getScene(), composition);
3753 
3754     dialog->show();
3755 }
3756 
3757 void
slotToggleRawNoteRuler()3758 NotationView::slotToggleRawNoteRuler()
3759 {
3760     bool visible = findAction("show_raw_note_ruler")->isChecked();
3761 
3762     m_notationWidget->setRawNoteRulerVisible(visible);
3763 
3764     QSettings settings;
3765     settings.beginGroup(NotationViewConfigGroup);
3766     settings.setValue("Raw note ruler shown", visible);
3767     settings.endGroup();
3768 }
3769 
3770 void
slotToggleTracking()3771 NotationView::slotToggleTracking()
3772 {
3773     if (m_notationWidget) m_notationWidget->slotTogglePlayTracking();
3774 }
3775 
3776 void
slotRegenerateScene()3777 NotationView::slotRegenerateScene()
3778 {
3779     NOTATION_DEBUG << "NotationView::slotRegenerateScene: "
3780                    << m_notationWidget->getScene()->getSegmentsDeleted()->size()
3781                    << " segments deleted";
3782 
3783     // The scene is going to be deleted then restored.  To continue
3784     // processing at best is useless and at the worst may cause a
3785     // crash.  This call could replace the multiple calls in
3786     // NotationScene.
3787     disconnect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
3788                m_notationWidget->getScene(), SLOT(slotCommandExecuted()));
3789 
3790     // Look for segments to be removed from vector
3791     std::vector<Segment *> * segmentDeleted =
3792         m_notationWidget->getScene()->getSegmentsDeleted();
3793 
3794     // If there is no such segment regenerate the notation widget directly
3795     if (segmentDeleted->size() != 0) {
3796 
3797         // else look if there is something to display still
3798         if (m_notationWidget->getScene()->isSceneEmpty()) {
3799             // All segments have been removed : don't regenerate anything
3800             // but close the editor.
3801             NOTATION_DEBUG << "NotationView::slotSceneDeleted";
3802 
3803             close();
3804             return;
3805         }
3806 
3807         // then remove the deleted segments
3808         for (std::vector<Segment *>::iterator isd = segmentDeleted->begin();
3809             isd != segmentDeleted->end(); ++isd) {
3810             for (std::vector<Segment *>::iterator i = m_segments.begin();
3811                 i != m_segments.end(); ++i) {
3812                 if (*isd == *i) {
3813                     m_segments.erase(i);
3814                     NOTATION_DEBUG << "NotationView::slotRegenerateScene:"
3815                                     " Erased segment from vector, have "
3816                                 << m_segments.size() << " segment(s) remaining"
3817                                ;
3818                     break;
3819                 }
3820             }
3821         }
3822         slotUpdateMenuStates(); // single <-> multiple
3823     }
3824 
3825     // Fix bug #2960243:
3826     // When a segment is deleted : remove the selection rect
3827     NotationTool * tool =  m_notationWidget->getCurrentTool();
3828     QString toolName;
3829     if (tool) {
3830         toolName = tool->getToolName();
3831         tool->stow();
3832     }
3833 
3834     // remember zoom factors
3835     double hZoomFactor = m_notationWidget->getHorizontalZoomFactor();
3836     double vZoomFactor = m_notationWidget->getVerticalZoomFactor();
3837 
3838     // TODO: remember scene position
3839 
3840     // regenerate the whole notation widget .
3841     setWidgetSegments();
3842 
3843     // restore size and spacing of notation police
3844     m_notationWidget->slotSetFontName(m_fontName);
3845     m_notationWidget->slotSetFontSize(m_fontSize);
3846     m_notationWidget->getScene()->setHSpacing(
3847             m_doc->getComposition().m_notationSpacing);
3848 
3849     // restore zoom factors
3850     m_notationWidget->setVerticalZoomFactor(vZoomFactor);
3851     m_notationWidget->setHorizontalZoomFactor(hZoomFactor);
3852 
3853     // TODO: restore scene position
3854 
3855     // and restore the current tool if any
3856     if (tool) m_notationWidget->slotSetTool(toolName);
3857 }
3858 
3859 void
slotUpdateWindowTitle(bool)3860 NotationView::slotUpdateWindowTitle(bool)
3861 {
3862     if (m_segments.empty()) return;
3863 
3864     // Scene may be empty and the editor is about to be closed,
3865     // but this info doesn't propagate to view still.
3866     // (Because signals used to trig slotUpdateWindowTitle() _are not queued_
3867     //  but signal used to trig slotRegenerateScene() _is queued_).
3868     // In such a case, don't do anything (to avoid a crash).
3869     if (m_notationWidget->getScene()->isSceneEmpty()) return;
3870 
3871     QString view = tr("Notation");
3872     setWindowTitle(getTitle(view));
3873 }
3874 
3875 void
slotGroupSimpleTuplet()3876 NotationView::slotGroupSimpleTuplet()
3877 {
3878     slotGroupTuplet(true);
3879 }
3880 
3881 void
slotGroupGeneralTuplet()3882 NotationView::slotGroupGeneralTuplet()
3883 {
3884     slotGroupTuplet(false);
3885 }
3886 
3887 void
slotGroupTuplet(bool simple)3888 NotationView::slotGroupTuplet(bool simple)
3889 {
3890     timeT t = 0;
3891     timeT unit = 0;
3892     int tupled = 2;
3893     int untupled = 3;
3894     Segment *segment = nullptr;
3895     bool hasTimingAlready = false;
3896     EventSelection *selection = getSelection();
3897 
3898     if (selection) {
3899         t = selection->getStartTime();
3900 
3901         timeT duration = selection->getTotalDuration();
3902         Note::Type unitType = Note::getNearestNote(duration / 3, 0)
3903                                                    .getNoteType();
3904         unit = Note(unitType).getDuration();
3905 
3906         if (!simple) {
3907             TupletDialog dialog(this, unitType, duration);
3908             if (dialog.exec() != QDialog::Accepted)
3909                 return ;
3910             unit = Note(dialog.getUnitType()).getDuration();
3911             tupled = dialog.getTupledCount();
3912             untupled = dialog.getUntupledCount();
3913             hasTimingAlready = dialog.hasTimingAlready();
3914         }
3915 
3916         segment = &selection->getSegment();
3917 
3918     } else {
3919 
3920         t = getInsertionTime();
3921 
3922         NoteRestInserter *currentInserter = dynamic_cast<NoteRestInserter *> (m_notationWidget->getCurrentTool());
3923 
3924         Note::Type unitType;
3925 
3926 // Should fix this too (maybe go fetch the NoteRestTool and check its duration).
3927         if (currentInserter) {
3928             unitType = currentInserter->getCurrentNote().getNoteType();
3929         } else {
3930             unitType = Note::Quaver;
3931         }
3932 
3933         unit = Note(unitType).getDuration();
3934 
3935         if (!simple) {
3936             TupletDialog dialog(this, unitType);
3937             if (dialog.exec() != QDialog::Accepted)
3938                 return ;
3939             unit = Note(dialog.getUnitType()).getDuration();
3940             tupled = dialog.getTupledCount();
3941             untupled = dialog.getUntupledCount();
3942             hasTimingAlready = dialog.hasTimingAlready();
3943         }
3944 
3945         segment = getCurrentSegment();
3946     }
3947 
3948     CommandHistory::getInstance()->addCommand(new TupletCommand
3949                                               (*segment, t, unit, untupled,
3950                                               tupled, hasTimingAlready));
3951 
3952     if (!hasTimingAlready) {
3953 //        slotSetInsertCursorPosition(t + (unit * tupled), true, false);
3954         m_document->slotSetPointerPosition(t + (unit * tupled));
3955     }
3956 }
3957 
3958 void
slotUpdateInsertModeStatusTriplet()3959 NotationView::slotUpdateInsertModeStatusTriplet()
3960 {
3961     if (isInTripletMode()) {
3962       m_notationWidget->setTupletMode(true);
3963       m_notationWidget->setTupledCount();
3964       m_notationWidget->setUntupledCount();
3965       (findAction("tuplet_mode"))->setChecked(false);
3966     } else m_notationWidget->setTupletMode(false);
3967     slotUpdateInsertModeStatus();
3968 }
3969 
3970 void
slotUpdateInsertModeStatusTuplet()3971 NotationView::slotUpdateInsertModeStatusTuplet()
3972 {
3973     if (isInTupletMode()) {
3974       m_notationWidget->setTupletMode(true);
3975       InsertTupletDialog dialog(this, m_notationWidget->getUntupledCount(),  m_notationWidget->getTupledCount());
3976       if (dialog.exec() == QDialog::Accepted) {
3977         m_notationWidget->setTupledCount(dialog.getTupledCount());
3978         m_notationWidget->setUntupledCount(dialog.getUntupledCount());
3979       }
3980       (findAction("triplet_mode"))->setChecked(false);
3981     } else m_notationWidget->setTupletMode(false);
3982     slotUpdateInsertModeStatus();
3983 }
3984 
3985 void
slotUpdateInsertModeStatus()3986 NotationView::slotUpdateInsertModeStatus()
3987 {
3988     QString tripletMessage = tr("Tuplet");
3989     QString chordMessage = tr("Chord");
3990     QString graceMessage = tr("Grace");
3991     QString message;
3992 
3993     m_notationWidget->setChordMode(isInChordMode());
3994     m_notationWidget->setGraceMode(isInGraceMode());
3995 
3996     if (isInTripletMode()||isInTupletMode()) {
3997         message = tr("%1 %2").arg(message).arg(tripletMessage);
3998     }
3999 
4000     if (isInChordMode()) {
4001         message = tr("%1 %2").arg(message).arg(chordMessage);
4002     }
4003 
4004     if (isInGraceMode()) {
4005         message = tr("%1 %2").arg(message).arg(graceMessage);
4006     }
4007 
4008     m_insertModeLabel->setText(message);
4009 }
4010 
4011 bool
isInChordMode()4012 NotationView::isInChordMode()
4013 {
4014     QAction* tac = findAction("chord_mode");
4015     return tac->isChecked();
4016 }
4017 
4018 bool
isInTripletMode()4019 NotationView::isInTripletMode()
4020 {
4021     QAction* tac = findAction("triplet_mode");
4022     return tac->isChecked();
4023 }
4024 
4025 bool
isInTupletMode()4026 NotationView::isInTupletMode()
4027 {
4028     QAction* tac = findAction("tuplet_mode");
4029     return tac->isChecked();
4030 }
4031 
4032 bool
isInGraceMode()4033 NotationView::isInGraceMode()
4034 {
4035     QAction* tac = findAction("grace_mode");
4036     return tac->isChecked();
4037 }
4038 
4039 void
slotSymbolAction()4040 NotationView::slotSymbolAction()
4041 {
4042     QObject *s = sender();
4043     setCurrentNotePixmapFrom(dynamic_cast<QAction *>(s));
4044     QString n = s->objectName();
4045 
4046     Symbol type = Symbol::Segno;
4047 
4048     if (n == "add_segno") type = Symbol::Segno;
4049     else if (n == "add_coda") type = Symbol::Coda;
4050     else if (n == "add_breath") type = Symbol::Breath;
4051 
4052     if (!m_notationWidget) return;
4053     m_notationWidget->slotSetSymbolInserter();
4054     m_notationWidget->slotSetInsertedSymbol(type);
4055     slotUpdateMenuStates();
4056 }
4057 
4058 void
slotHalveDurations()4059 NotationView::slotHalveDurations()
4060 {
4061     if (!getSelection()) return ;
4062 
4063     CommandHistory::getInstance()->addCommand(new RescaleCommand(*getSelection(),
4064                                               getSelection()->getTotalDuration() / 2,
4065                                               false));
4066 }
4067 
4068 void
slotDoubleDurations()4069 NotationView::slotDoubleDurations()
4070 {
4071     if (!getSelection()) return ;
4072 
4073     CommandHistory::getInstance()->addCommand(new RescaleCommand(*getSelection(),
4074                                               getSelection()->getTotalDuration() * 2,
4075                                               false));
4076 }
4077 
4078 void
slotRescale()4079 NotationView::slotRescale()
4080 {
4081     if (!getSelection()) return ;
4082 
4083     RescaleDialog dialog
4084     (this,
4085      &getDocument()->getComposition(),
4086      getSelection()->getStartTime(),
4087      getSelection()->getEndTime() -
4088          getSelection()->getStartTime(),
4089      1,
4090      true,
4091      true);
4092 
4093     if (dialog.exec() == QDialog::Accepted) {
4094         CommandHistory::getInstance()->addCommand(new RescaleCommand
4095                                                   (*getSelection(),
4096                                                   dialog.getNewDuration(),
4097                                                   dialog.shouldCloseGap()));
4098     }
4099 }
4100 
4101 void
slotTransposeUp()4102 NotationView::slotTransposeUp()
4103 {
4104     if (!getSelection()) return ;
4105 
4106     CommandHistory::getInstance()->addCommand(new TransposeCommand
4107                                               (1, *getSelection()));
4108 }
4109 
4110 void
slotTransposeDown()4111 NotationView::slotTransposeDown()
4112 {
4113     if (!getSelection()) return ;
4114 
4115     CommandHistory::getInstance()->addCommand(new TransposeCommand
4116                                               ( -1, *getSelection()));
4117 }
4118 
4119 void
slotTransposeUpOctave()4120 NotationView::slotTransposeUpOctave()
4121 {
4122     if (!getSelection()) return ;
4123 
4124     CommandHistory::getInstance()->addCommand(new TransposeCommand
4125                                               (12, *getSelection()));
4126 }
4127 
4128 void
slotTransposeDownOctave()4129 NotationView::slotTransposeDownOctave()
4130 {
4131     if (!getSelection()) return ;
4132 
4133     CommandHistory::getInstance()->addCommand(new TransposeCommand
4134                                               ( -12, *getSelection()));
4135 }
4136 
4137 void
slotTranspose()4138 NotationView::slotTranspose()
4139 {
4140     EventSelection *selection = getSelection();
4141     if (!selection) {
4142         RG_WARNING << "Hint: selection is nullptr in slotTranpose()";
4143         return;
4144     }
4145 
4146     QSettings settings;
4147     settings.beginGroup(NotationViewConfigGroup);
4148 
4149     int dialogDefault = settings.value("lasttransposition", 0).toInt() ;
4150 
4151     bool ok = false;
4152     int semitones = QInputDialog::getInt
4153             (this, tr("Transpose"),
4154              tr("By number of semitones: "),
4155                 dialogDefault, -127, 127, 1, &ok);
4156     if (!ok || semitones == 0) return;
4157 
4158     settings.setValue("lasttransposition", semitones);
4159 
4160     CommandHistory::getInstance()->addCommand(new TransposeCommand
4161             (semitones, *selection));
4162 
4163     settings.endGroup();
4164 }
4165 
4166 void
slotDiatonicTranspose()4167 NotationView::slotDiatonicTranspose()
4168 {
4169     if (!getSelection()) return ;
4170 
4171     QSettings settings;
4172     settings.beginGroup(NotationViewConfigGroup);
4173 
4174     IntervalDialog intervalDialog(this);
4175     int ok = intervalDialog.exec();
4176         //int dialogDefault = settings.value("lasttransposition", 0).toInt() ;
4177     int semitones = intervalDialog.getChromaticDistance();
4178     int steps = intervalDialog.getDiatonicDistance();
4179     settings.endGroup();
4180 
4181     if (!ok || (semitones == 0 && steps == 0)) return;
4182 
4183     if (intervalDialog.getChangeKey())
4184     {
4185         RG_WARNING << "Transposing changing keys is not currently supported on selections";
4186     }
4187     else
4188     {
4189         // Transpose within key
4190                 //RG_DEBUG << "Transposing semitones, steps: " << semitones << ", " << steps;
4191         CommandHistory::getInstance()->addCommand(new TransposeCommand
4192                                                   (semitones, steps,
4193                                                   *getSelection()));
4194     }
4195 }
4196 
4197 void
slotInvert()4198 NotationView::slotInvert()
4199 {
4200     if (!getSelection()) return;
4201 
4202     int semitones = 0;
4203 
4204     CommandHistory::getInstance()->addCommand(new InvertCommand
4205                                               (semitones, *getSelection()));
4206 }
4207 
4208 void
slotRetrograde()4209 NotationView::slotRetrograde()
4210 {
4211     if (!getSelection()) return;
4212 
4213     int semitones = 0;
4214 
4215     CommandHistory::getInstance()->addCommand(new RetrogradeCommand
4216                                               (semitones, *getSelection()));
4217 }
4218 
4219 void
slotRetrogradeInvert()4220 NotationView::slotRetrogradeInvert()
4221 {
4222     if (!getSelection()) return;
4223 
4224     int semitones = 0;
4225 
4226     CommandHistory::getInstance()->addCommand(new RetrogradeInvertCommand
4227                                               (semitones, *getSelection()));
4228 }
4229 
4230 void
slotJogLeft()4231 NotationView::slotJogLeft()
4232 {
4233     EventSelection *selection = getSelection();
4234     if (!selection) return ;
4235 
4236     RG_DEBUG << "NotationView::slotJogLeft";
4237 
4238     bool useNotationTimings = true;
4239 
4240     CommandHistory::getInstance()->addCommand(new MoveCommand
4241                                               (*getCurrentSegment(),
4242                                               -Note(Note::Demisemiquaver).getDuration(),
4243                                               useNotationTimings,
4244                                               *selection));
4245 }
4246 
4247 void
slotJogRight()4248 NotationView::slotJogRight()
4249 {
4250     EventSelection *selection = getSelection();
4251     if (!selection) return ;
4252 
4253     RG_DEBUG << "NotationView::slotJogRight";
4254 
4255     bool useNotationTimings = true;
4256 
4257     CommandHistory::getInstance()->addCommand(new MoveCommand
4258                                               (*getCurrentSegment(),
4259                                               Note(Note::Demisemiquaver).getDuration(),
4260                                               useNotationTimings,
4261                                               *selection));
4262 }
4263 
4264 bool
4265 NotationView::
isShowable(Event * e)4266 isShowable(Event *e)
4267 {
4268     if (e->isa(PitchBend::EventType)) { return false; }
4269     if (e->isa(Controller::EventType)) { return false; }
4270     return true;
4271 }
4272 
4273 void
slotStepBackward()4274 NotationView::slotStepBackward()
4275 {
4276     Segment *segment = getCurrentSegment();
4277     if (!segment) return;
4278 
4279     timeT time = getInsertionTime();
4280     Segment::iterator i = segment->findTime(time);
4281 
4282     while (i != segment->begin() &&
4283            (i == segment->end() ||
4284             (*i)->getNotationAbsoluteTime() >= time ||
4285             !isShowable(*i)))
4286         { --i; }
4287 
4288     if (i != segment->end()){
4289         m_document->slotSetPointerPosition((*i)->getNotationAbsoluteTime());
4290     }
4291 }
4292 
4293 void
slotStepForward()4294 NotationView::slotStepForward()
4295 {
4296     Segment *segment = getCurrentSegment();
4297     if (!segment) return;
4298 
4299     timeT time = getInsertionTime();
4300     Segment::iterator i = segment->findTime(time);
4301 
4302     while (i != segment->end() &&
4303            ((*i)->getNotationAbsoluteTime() <= time ||
4304             !isShowable(*i)))
4305         { ++i; }
4306 
4307     if (i == segment->end()){
4308         m_document->slotSetPointerPosition(segment->getEndMarkerTime());
4309     } else {
4310         m_document->slotSetPointerPosition((*i)->getNotationAbsoluteTime());
4311     }
4312 }
4313 
4314 void
slotInsertableNoteOnReceived(int pitch,int velocity)4315 NotationView::slotInsertableNoteOnReceived(int pitch, int velocity)
4316 {
4317     NOTATION_DEBUG << "NotationView::slotInsertableNoteOnReceived: " << pitch;
4318     slotInsertableNoteEventReceived(pitch, velocity, true);
4319 }
4320 
4321 void
slotInsertableNoteOffReceived(int pitch,int velocity)4322 NotationView::slotInsertableNoteOffReceived(int pitch, int velocity)
4323 {
4324     NOTATION_DEBUG << "NotationView::slotInsertableNoteOffReceived: " << pitch;
4325     slotInsertableNoteEventReceived(pitch, velocity, false);
4326 }
4327 
4328 void
4329 //!!! shut up compiler warning about unused 'velocity' but left original intact
4330 // because it would be a good thing to make use of velocity one day
4331 //NotationView::slotInsertableNoteEventReceived(int pitch, int velocity, bool noteOn)
slotInsertableNoteEventReceived(int pitch,int velocity,bool noteOn)4332 NotationView::slotInsertableNoteEventReceived(int pitch, int velocity, bool noteOn)
4333 {
4334 
4335     // find step recording action in menu (it ought to exist!)
4336     QAction *action = findAction("toggle_step_by_step");
4337     if (!action) {
4338         NOTATION_DEBUG << "WARNING: No toggle_step_by_step action";
4339         return ;
4340     }
4341 
4342     // return if we're not in step recording (step by step) mode
4343     if (!action->isChecked()) return ;
4344 
4345     // return if this window is not active, to avoid a window that is out of
4346     // sight and out of mind filling rapidly with unexpected gibberish which has
4347     // to be undone one event at a time
4348     //
4349     // NOTE: This prevents using step recording mode with an external app like
4350     // VMPK.  I can't think of any other way to avoid the far greater problem of
4351     // having to undo 15,000 unexpected events you didn't intend to record in an
4352     // out of focus view that got left in step record mode.
4353     if (!isActiveWindow()) return;
4354 
4355     Segment *segment = getCurrentSegment();
4356 
4357     NoteRestInserter *noteInserter = dynamic_cast<NoteRestInserter *>
4358                                      (m_notationWidget->getCurrentTool());
4359     if (!noteInserter) {
4360         // The old behavior was totally evil, so let's try failing silently and
4361         // see how obnoxious it is for the user to discover the hard way that
4362         // nothing is happening, because the wrong tool is selected.
4363         return;
4364     }
4365 
4366 //    if (m_inPaintEvent) {
4367 //        NOTATION_DEBUG << "NotationView::slotInsertableNoteEventReceived: in paint event already";
4368 //        if (noteOn) {
4369 //            m_pendingInsertableNotes.push_back(std::pair<int, int>(pitch, velocity));
4370 //        }
4371 //        return ;
4372 //    }
4373 
4374     // If the segment is transposed, we want to take that into
4375     // account.  But the note has already been played back to the user
4376     // at its untransposed pitch, because that's done by the MIDI THRU
4377     // code in the sequencer which has no way to know whether a note
4378     // was intended for step recording.  So rather than adjust the
4379     // pitch for playback according to the transpose setting, we have
4380     // to adjust the stored pitch in the opposite direction.
4381 
4382     pitch -= segment->getTranspose();
4383 
4384     //    TmpStatusMsg msg(tr("Inserting note"), this);
4385 
4386     // We need to ensure that multiple notes hit at once come out as
4387     // chords, without imposing the interpretation that overlapping
4388     // notes are always chords and without getting too involved with
4389     // the actual absolute times of the notes (this is still step
4390     // editing, not proper recording).
4391 
4392     // First, if we're in chord mode, there's no problem.
4393 
4394     static int numberOfNotesOn = 0;
4395     static timeT insertionTime = getInsertionTime();
4396     static time_t lastInsertionTime = 0;
4397 
4398     if (isInChordMode()) {
4399         if (!noteOn)
4400             return ;
4401         NOTATION_DEBUG << "Inserting note in chord at pitch " << pitch;
4402         noteInserter->insertNote(*segment, getInsertionTime(), pitch,
4403                                  Accidentals::NoAccidental, velocity,
4404                                  true);
4405 
4406     } else {
4407 
4408         if (!noteOn) {
4409             numberOfNotesOn--;
4410         } else if (noteOn) {
4411             // Rules:
4412             //
4413             // * If no other note event has turned up within half a
4414             //   second, insert this note and advance.
4415             //
4416             // * Relatedly, if this note is within half a second of
4417             //   the previous one, they're chords.  Insert the previous
4418             //   one, don't advance, and use the same rules for this.
4419             //
4420             // * If a note event turns up before that time has elapsed,
4421             //   we need to wait for the note-off events: if the second
4422             //   note happened less than half way through the first,
4423             //   it's a chord.
4424             //
4425             // We haven't implemented these yet... For now:
4426             //
4427             // Rules (hjj):
4428             //
4429             // * The overlapping notes are always included in to a chord.
4430             //   This is the most convenient for step inserting of chords.
4431             //
4432             // * The timer resets the numberOfNotesOn, if noteOff signals were
4433             //   drop out for some reason (which has not been encountered yet).
4434 
4435             time_t now;
4436             time (&now);
4437             double elapsed = difftime(now, lastInsertionTime);
4438             time (&lastInsertionTime);
4439 
4440             if (numberOfNotesOn <= 0 || elapsed > 10.0 ) {
4441                 numberOfNotesOn = 0;
4442                 insertionTime = getInsertionTime();
4443             }
4444             numberOfNotesOn++;
4445 
4446             noteInserter->insertNote(*segment, insertionTime, pitch,
4447                                      Accidentals::NoAccidental, velocity,
4448                                      true);
4449         }
4450     }
4451 }
4452 
4453 void
slotEditLyrics()4454 NotationView::slotEditLyrics()
4455 {
4456     Segment *segment = getCurrentSegment();
4457     int oldVerseCount = segment->getVerseCount();
4458 
4459     LyricEditDialog dialog(this, m_segments, segment);
4460 
4461     if (dialog.exec() == QDialog::Accepted) {
4462 
4463         // User may have change segment from inside the dialog
4464         // (see ticket #1547)
4465         segment = dialog.getSegment();
4466 
4467         MacroCommand *macro = new MacroCommand
4468             (SetLyricsCommand::getGlobalName());
4469 
4470         for (int i = 0; i < dialog.getVerseCount(); ++i) {
4471             SetLyricsCommand *command = new SetLyricsCommand
4472                 (segment, i, dialog.getLyricData(i));
4473             macro->addCommand(command);
4474         }
4475         for (int i = dialog.getVerseCount(); i < oldVerseCount; ++i) {
4476             // (hjj) verse count decreased, delete extra verses.
4477             SetLyricsCommand *command = new SetLyricsCommand
4478                 (segment, i, QString(""));
4479             macro->addCommand(command);
4480         }
4481 
4482         CommandHistory::getInstance()->addCommand(macro);
4483     }
4484 }
4485 
4486 
4487 void
slotHoveredOverNoteChanged(const QString & noteName)4488 NotationView::slotHoveredOverNoteChanged(const QString &noteName)
4489 {
4490     m_hoveredOverNoteName->setText(QString(" ") + noteName);
4491 }
4492 
4493 void
slotHoveredOverAbsoluteTimeChanged(unsigned int time)4494 NotationView::slotHoveredOverAbsoluteTimeChanged(unsigned int time)
4495 {
4496     timeT t = time;
4497     RealTime rt =
4498         getDocument()->getComposition().getElapsedRealTime(t);
4499     long ms = rt.msec();
4500 
4501     int bar, beat, fraction, remainder;
4502     getDocument()->getComposition().getMusicalTimeForAbsoluteTime
4503         (t, bar, beat, fraction, remainder);
4504 
4505     //    QString message;
4506     //    QString format("%ld (%ld.%03lds)");
4507     //    format = tr("Time: %1").arg(format);
4508     //    message.sprintf(format, t, rt.sec, ms);
4509 
4510     QString message = tr("Time: %1 (%2.%3s)")
4511          .arg(QString("%1-%2-%3-%4")
4512              .arg(QString("%1").arg(bar + 1).rightJustified(3, '0'))
4513              .arg(QString("%1").arg(beat).rightJustified(2, '0'))
4514              .arg(QString("%1").arg(fraction).rightJustified(2, '0'))
4515              .arg(QString("%1").arg(remainder).rightJustified(2, '0')))
4516          .arg(rt.sec)
4517          .arg(QString("%1").arg(ms).rightJustified(3, '0'));
4518 
4519     m_hoveredOverAbsoluteTime->setText(message);
4520 }
4521 
4522 void
slotFontComboChanged(int index)4523 NotationView::slotFontComboChanged(int index)
4524 {
4525     QString name = m_availableFontNames[index];
4526     if (m_notationWidget) m_notationWidget->slotSetFontName(name);
4527     m_fontName = name;
4528     QString action = QString("note_font_%1").arg(name);
4529     findAction(action)->setChecked(true);
4530 }
4531 
4532 void
slotSizeComboChanged(int index)4533 NotationView::slotSizeComboChanged(int index)
4534 {
4535     int size = m_availableFontSizes[index];
4536     if (m_notationWidget) m_notationWidget->slotSetFontSize(size);
4537     m_fontSize = size;
4538     QString action = QString("note_font_size_%1").arg(size);
4539     findAction(action)->setChecked(true);
4540 }
4541 
4542 void
slotSpacingComboChanged(int index)4543 NotationView::slotSpacingComboChanged(int index)
4544 {
4545     int spacing = m_availableSpacings[index];
4546     if (m_notationWidget) m_notationWidget->getScene()->setHSpacing(spacing);
4547 
4548     m_doc->getComposition().m_notationSpacing = spacing;
4549     m_doc->slotDocumentModified();
4550 
4551     QString action = QString("spacing_%1").arg(spacing);
4552     findAction(action)->setChecked(true);
4553 }
4554 
4555 void
slotToggleVelocityRuler()4556 NotationView::slotToggleVelocityRuler()
4557 {
4558     m_notationWidget->slotToggleVelocityRuler();
4559     conformRulerSelectionState();
4560 }
4561 
4562 void
slotTogglePitchbendRuler()4563 NotationView::slotTogglePitchbendRuler()
4564 {
4565     m_notationWidget->slotTogglePitchbendRuler();
4566     conformRulerSelectionState();
4567 }
4568 
4569 void
slotAddControlRuler(QAction * action)4570 NotationView::slotAddControlRuler(QAction *action)
4571 {
4572     NOTATION_DEBUG << "NotationView::slotAddControlRuler(" << action << ")";
4573     m_notationWidget->slotAddControlRuler(action);
4574     conformRulerSelectionState();
4575 }
4576 
4577 Device *
getCurrentDevice()4578 NotationView::getCurrentDevice()
4579 {
4580     if (m_notationWidget) return m_notationWidget->getCurrentDevice();
4581     else return nullptr;
4582 }
4583 
4584 void
slotHelp()4585 NotationView::slotHelp()
4586 {
4587     // TRANSLATORS: if the manual is translated into your language, you can
4588     // change the two-letter language code in this URL to point to your language
4589     // version, eg. "http://rosegardenmusic.com/wiki/doc:manual-es" for the
4590     // Spanish version. If your language doesn't yet have a translation, feel
4591     // free to create one.
4592     QString helpURL = tr("http://rosegardenmusic.com/wiki/doc:notation-en");
4593     QDesktopServices::openUrl(QUrl(helpURL));
4594 }
4595 
4596 void
slotTutorial()4597 NotationView::slotTutorial()
4598 {
4599     QString tutorialURL = tr("http://www.rosegardenmusic.com/tutorials/en/chapter-0.html");
4600     QDesktopServices::openUrl(QUrl(tutorialURL));
4601 }
4602 
4603 void
slotBugGuidelines()4604 NotationView::slotBugGuidelines()
4605 {
4606     QString glURL = tr("http://rosegarden.sourceforge.net/tutorial/bug-guidelines.html");
4607      QDesktopServices::openUrl(QUrl(glURL));
4608 }
4609 
4610 void
slotHelpAbout()4611 NotationView::slotHelpAbout()
4612 {
4613     new AboutDialog(this);
4614 }
4615 
4616 void
slotHelpAboutQt()4617 NotationView::slotHelpAboutQt()
4618 {
4619     QMessageBox::aboutQt(this, tr("Rosegarden"));
4620 }
4621 
4622 void
slotDonate()4623 NotationView::slotDonate()
4624 {
4625     QDesktopServices::openUrl(QUrl(
4626             "https://www.rosegardenmusic.com/wiki/donations"));
4627 }
4628 
4629 void
slotToggleStepByStep()4630 NotationView::slotToggleStepByStep()
4631 {
4632     QAction *action = findAction("toggle_step_by_step");
4633 
4634     if (!action) {
4635         NOTATION_DEBUG << "WARNING: No toggle_step_by_step action";
4636         return ;
4637     }
4638     if (action->isChecked()) {
4639         emit stepByStepTargetRequested(this);
4640     } else {
4641         emit stepByStepTargetRequested(nullptr);
4642     }
4643 }
4644 
4645 void
slotStepByStepTargetRequested(QObject * obj)4646 NotationView::slotStepByStepTargetRequested(QObject *obj)
4647 {
4648     QAction *action = findAction("toggle_step_by_step");
4649 
4650     if (!action) {
4651         MATRIX_DEBUG << "WARNING: No toggle_step_by_step action";
4652         return ;
4653     }
4654     action->setChecked(obj == this);
4655 }
4656 
4657 void
slotMoveEventsUpStaffInteractive()4658 NotationView::slotMoveEventsUpStaffInteractive()
4659 { generalMoveEventsToStaff(true, true); }
4660 
4661 void
slotMoveEventsDownStaffInteractive()4662 NotationView::slotMoveEventsDownStaffInteractive()
4663 { generalMoveEventsToStaff(false, true); }
4664 
4665 void
slotMoveEventsUpStaff()4666 NotationView::slotMoveEventsUpStaff()
4667 { generalMoveEventsToStaff(true, false); }
4668 
4669 void
slotMoveEventsDownStaff()4670 NotationView::slotMoveEventsDownStaff()
4671 { generalMoveEventsToStaff(false, false); }
4672 
4673 // Move the selected events to another staff
4674 // @param upStaff
4675 // if true, move them to the staff above this one, otherwise to the
4676 // staff below.
4677 // @param useDialog
4678 // Whether to use a dialog, otherwise use default values and no
4679 // interaction.
4680 void
generalMoveEventsToStaff(bool upStaff,bool useDialog)4681 NotationView::generalMoveEventsToStaff(bool upStaff, bool useDialog)
4682 {
4683     EventSelection *selection = getSelection();
4684     if (!selection) return;
4685 
4686     NotationScene *scene = m_notationWidget->getScene();
4687     if (!scene) return;
4688     timeT targetTime = selection->getStartTime();
4689 
4690     PasteEventsCommand::PasteType type;
4691 
4692     if (useDialog) {
4693         PasteNotationDialog dialog(this);
4694         if (dialog.exec() != QDialog::Accepted) { return; }
4695         type = dialog.getPasteType();
4696     } else {
4697         type = PasteEventsCommand::NoteOverlay;
4698     }
4699 
4700     NotationStaff *target_staff =
4701         upStaff ?
4702         scene->getStaffAbove(targetTime) :
4703         scene->getStaffBelow(targetTime);
4704     QString commandName =
4705         upStaff ?
4706         tr("Move Events to Staff Above") :
4707         tr("Move Events to Staff Below");
4708 
4709     if (!target_staff) return;
4710 
4711     Segment *segment = &target_staff->getSegment();
4712 
4713     MacroCommand *command = new MacroCommand(commandName);
4714 
4715     timeT insertionTime = selection->getStartTime();
4716 
4717     Clipboard *c = new Clipboard;
4718     CopyCommand *cc = new CopyCommand(*selection, c);
4719     cc->execute();
4720 
4721     command->addCommand(new EraseCommand(*selection));
4722 
4723     command->addCommand(new PasteEventsCommand
4724                         (*segment, c, insertionTime,
4725                          type));
4726 
4727     CommandHistory::getInstance()->addCommand(command);
4728 
4729     delete c;
4730 }
4731 
4732 void
slotEditElement(NotationStaff * staff,NotationElement * element,bool advanced)4733 NotationView::slotEditElement(NotationStaff *staff,
4734                               NotationElement *element,
4735                               bool advanced)
4736 {
4737     NOTATION_DEBUG << "NotationView::slotEditElement()";
4738 
4739     NotationScene *scene = m_notationWidget->getScene();
4740     if (!scene) return;
4741 
4742     NotePixmapFactory *npf = scene->getNotePixmapFactory();
4743 
4744     if (advanced) {
4745 
4746         EventEditDialog dialog(this, *element->event(), true);
4747 
4748         if (dialog.exec() == QDialog::Accepted &&
4749             dialog.isModified()) {
4750 
4751             EventEditCommand *command = new EventEditCommand
4752                 (staff->getSegment(),
4753                  element->event(),
4754                  dialog.getEvent());
4755 
4756             CommandHistory::getInstance()->addCommand(command);
4757         }
4758 
4759     } else if (element->event()->isa(Clef::EventType)) {
4760 
4761         try {
4762             ClefDialog dialog(this, npf,
4763                               Clef(*element->event()));
4764 
4765             if (dialog.exec() == QDialog::Accepted) {
4766 
4767                 ClefDialog::ConversionType conversion = dialog.getConversionType();
4768                 bool shouldChangeOctave = (conversion != ClefDialog::NoConversion);
4769                 bool shouldTranspose = (conversion == ClefDialog::Transpose);
4770                 CommandHistory::getInstance()->addCommand
4771                     (new ClefInsertionCommand
4772                      (staff->getSegment(), element->event()->getAbsoluteTime(),
4773                       dialog.getClef(), shouldChangeOctave, shouldTranspose));
4774             }
4775         } catch (const Exception &e) {
4776             RG_WARNING << e.getMessage();
4777         }
4778 
4779         return ;
4780 
4781     } else if (element->event()->isa(GeneratedRegion::EventType)) {
4782 
4783         try {
4784             GeneratedRegionDialog dialog(this, npf,
4785                                          GeneratedRegion(*element->event()),
4786                                          tr("Edit Generated region mark"));
4787 
4788             if (dialog.exec() == QDialog::Accepted) {
4789                 GeneratedRegionInsertionCommand * command =
4790                     new GeneratedRegionInsertionCommand
4791                     (staff->getSegment(),
4792                      element->event()->getAbsoluteTime(),
4793                      dialog.getGeneratedRegion());
4794 
4795                 MacroCommand *macroCommand = dialog.extractCommand();
4796 
4797                 macroCommand->addCommand(new
4798                                          EraseEventCommand(staff->getSegment(),
4799                                                            element->event(),
4800                                                            false));
4801                 macroCommand->addCommand(command);
4802                 CommandHistory::getInstance()->addCommand(macroCommand);
4803 
4804             } else {
4805                 /* Still execute the command but without erase+insert,
4806                    because it may still contain legitimate commands
4807                    (eg to update tags). */
4808                 MacroCommand *macroCommand = dialog.extractCommand();
4809                 if (macroCommand->haveCommands()) {
4810                     macroCommand->setName(tr("Updated tags for aborted edit"));
4811                     CommandHistory::getInstance()->addCommand(macroCommand);
4812                 }
4813             }
4814 
4815         } catch (const Exception &e) {
4816             RG_WARNING << e.getMessage();
4817         }
4818 
4819         return ;
4820 
4821     } else if (element->event()->isa(Rosegarden::Key::EventType)) {
4822 
4823         try {
4824             Clef clef(staff->getSegment().getClefAtTime
4825                       (element->event()->getAbsoluteTime()));
4826             KeySignatureDialog dialog
4827                 (this, npf, clef, Rosegarden::Key(*element->event()),
4828                  false, true);
4829 
4830             if (dialog.exec() == QDialog::Accepted &&
4831                 dialog.isValid()) {
4832 
4833                 KeySignatureDialog::ConversionType conversion =
4834                     dialog.getConversionType();
4835 
4836                 CommandHistory::getInstance()->addCommand
4837                     (new KeyInsertionCommand
4838                      (staff->getSegment(),
4839                       element->event()->getAbsoluteTime(), dialog.getKey(),
4840                       conversion == KeySignatureDialog::Convert,
4841                       conversion == KeySignatureDialog::Transpose,
4842                       dialog.shouldBeTransposed(),
4843               dialog.shouldIgnorePercussion()));
4844             }
4845 
4846         } catch (const Exception &e) {
4847             RG_WARNING << e.getMessage();
4848         }
4849 
4850         return ;
4851 
4852     } else if (element->event()->isa(Text::EventType)) {
4853 
4854         try {
4855             TextEventDialog dialog
4856                 (this, npf, Text(*element->event()));
4857 
4858             if (dialog.exec() == QDialog::Accepted) {
4859                 TextInsertionCommand *command = new TextInsertionCommand
4860                     (staff->getSegment(),
4861                      element->event()->getAbsoluteTime(),
4862                      dialog.getText());
4863 
4864                 MacroCommand *macroCommand = new MacroCommand(tr("Edit Text Event"));
4865 
4866                 macroCommand->addCommand(new EraseEventCommand(staff->getSegment(),
4867                                                                element->event(), false));
4868                 macroCommand->addCommand(command);
4869                 CommandHistory::getInstance()->addCommand(macroCommand);
4870             }
4871         } catch (const Exception &e) {
4872             RG_WARNING << e.getMessage();
4873         }
4874 
4875         return ;
4876 
4877     } else if (element->isNote() &&
4878                element->event()->has(BaseProperties::TRIGGER_SEGMENT_ID)) {
4879 
4880         int id = element->event()->get
4881             <Int>
4882             (BaseProperties::TRIGGER_SEGMENT_ID);
4883 
4884         emit editTriggerSegment(id);
4885         return ;
4886 
4887     } else {
4888 
4889         SimpleEventEditDialog dialog(this, getDocument(), *element->event(), false);
4890 
4891         if (dialog.exec() == QDialog::Accepted &&
4892             dialog.isModified()) {
4893 
4894             EventEditCommand *command = new EventEditCommand
4895                 (staff->getSegment(),
4896                  element->event(),
4897                  dialog.getEvent());
4898 
4899             CommandHistory::getInstance()->addCommand(command);
4900         }
4901     }
4902 }
4903 
4904 void
slotTransformsNormalizeRests()4905 NotationView::slotTransformsNormalizeRests()
4906 {
4907     EventSelection *selection = m_notationWidget->getSelection();
4908 
4909     if (!selection)
4910         return ;
4911 
4912     TmpStatusMsg msg(tr("Normalizing rests..."), this);
4913 
4914     CommandHistory::getInstance()->
4915             addCommand(new NormalizeRestsCommand(*selection));
4916 }
4917 
4918 void
slotTransformsCollapseNotes()4919 NotationView::slotTransformsCollapseNotes()
4920 {
4921     EventSelection *selection = m_notationWidget->getSelection();
4922 
4923     if (!selection)
4924         return ;
4925     TmpStatusMsg msg(tr("Collapsing notes..."), this);
4926 
4927     CommandHistory::getInstance()->
4928             addCommand(new CollapseNotesCommand(*selection));
4929 }
4930 
extendSelectionHelper(bool forward,EventSelection * es,const std::vector<Event * > & eventVec,bool select)4931 void NotationView::extendSelectionHelper(bool forward, EventSelection *es, const std::vector<Event *> &eventVec, bool select)
4932 {
4933     int moveCount = 0;
4934     int prevEventTime = 0;
4935     int prevSubOrdering = 0;
4936     for (unsigned int j = 0; j < eventVec.size(); ++j) {
4937         Event *event = eventVec[j];
4938         int count;
4939         if (select) { // select
4940             count = es->addEvent(event, true, forward);
4941         } else { // unselect
4942             count = es->removeEvent(event, true, forward);
4943         }
4944         if (prevEventTime != event->getAbsoluteTime() || prevSubOrdering != event->getSubOrdering()) {
4945             moveCount = qMax(moveCount, count);
4946         }
4947         prevEventTime = event->getAbsoluteTime();
4948         prevSubOrdering = event->getSubOrdering();
4949     }
4950 
4951     // #1519: increment cursor for every event selected/unselected
4952     for (int c = 1; c < moveCount; ++c) {
4953         if (forward)
4954             slotStepForward();
4955         else
4956             slotStepBackward();
4957     }
4958 }
4959 
4960 void
slotExtendSelectionBackward()4961 NotationView::slotExtendSelectionBackward()
4962 {
4963     slotExtendSelectionBackward(false);
4964 }
4965 
4966 void
slotExtendSelectionBackwardBar()4967 NotationView::slotExtendSelectionBackwardBar()
4968 {
4969     slotExtendSelectionBackward(true);
4970 }
4971 
4972 void
slotExtendSelectionBackward(bool bar)4973 NotationView::slotExtendSelectionBackward(bool bar)
4974 {
4975     // Move the cursor left and toggle selection between oldTime and newTime
4976 
4977     timeT oldTime = getInsertionTime();
4978 
4979     if (bar) emit rewindPlayback();
4980     else slotStepBackward();
4981 
4982     timeT newTime = getInsertionTime();
4983 
4984     Segment *segment = getCurrentSegment();
4985     if (!segment) return;
4986 
4987     NotationStaff *currentStaff = m_notationWidget->getScene()->getCurrentStaff();
4988     if (!currentStaff) return;
4989 
4990     ViewElementList *vel = currentStaff->getViewElementList();
4991     EventSelection *s = getSelection();
4992     EventSelection *es;
4993     if (s && &s->getSegment() == segment)
4994         es = new EventSelection(*s);
4995     else
4996         es = new EventSelection(*segment);
4997 
4998     ViewElementList::iterator extendFrom = vel->findTime(oldTime);
4999     if (extendFrom == vel->begin()) // shouldn't happen
5000         return;
5001     ViewElementList::iterator firstNote = extendFrom;
5002     --firstNote;
5003     const bool wasSelected = es->contains((*firstNote)->event());
5004 
5005     // store events in a separate data structure, to avoid
5006     // breaking iteration while removing events from the segment.
5007     std::vector<Event *> eventVec;
5008 
5009     while (extendFrom != vel->begin() &&
5010            (*--extendFrom)->getViewAbsoluteTime() >= newTime) {
5011 
5012         //!!! This should actually grab every sort of event, and not just
5013         // notes, but making that change makes the selection die every time
5014         // the endpoint of an indication is encountered, and I'm just not
5015         // seeing why, so I'm giving up on that and leaving it in the same
5016         // stupid state I found it in (and it's probably in this state
5017         // because the last guy had the same problem with indications.)
5018         //
5019         // I don't like this, because it makes it very easy to go along and
5020         // orphan indications, text events, controllers, and all sorts of
5021         // whatnot.  However, I have to call it quits for today, and have no
5022         // idea if I'll ever remember to come back to this, so I'm leaving a
5023         // reminder to someone that all of this is stupid.
5024         Event *event = (*extendFrom)->event();
5025         if (event->isa(Note::EventType)) {
5026             eventVec.push_back(event);
5027         }
5028     }
5029 
5030     const bool forward = false;
5031     extendSelectionHelper(forward, es, eventVec, !wasSelected);
5032 
5033     setSelection(es, true);
5034 }
5035 
5036 void
slotExtendSelectionForward()5037 NotationView::slotExtendSelectionForward()
5038 {
5039     slotExtendSelectionForward(false);
5040 }
5041 
5042 void
slotExtendSelectionForwardBar()5043 NotationView::slotExtendSelectionForwardBar()
5044 {
5045     slotExtendSelectionForward(true);
5046 }
5047 
5048 void
slotExtendSelectionForward(bool bar)5049 NotationView::slotExtendSelectionForward(bool bar)
5050 {
5051     // Move the cursor right and toggle selection between oldTime and newTime
5052 
5053     timeT oldTime = getInsertionTime();
5054 
5055     if (bar) emit fastForwardPlayback();
5056     else slotStepForward();
5057 
5058     timeT newTime = getInsertionTime();
5059 
5060     Segment *segment = getCurrentSegment();
5061     if (!segment) return;
5062 
5063     NotationStaff *currentStaff = m_notationWidget->getScene()->getCurrentStaff();
5064     if (!currentStaff) return;
5065 
5066     ViewElementList *vel = currentStaff->getViewElementList();
5067     EventSelection *s = getSelection();
5068     EventSelection *es;
5069     if (s && &s->getSegment() == segment)
5070         es = new EventSelection(*s);
5071     else
5072         es = new EventSelection(*segment);
5073 
5074     ViewElementList::iterator extendFrom = vel->findTime(oldTime);
5075     if (extendFrom == vel->end()) // shouldn't happen
5076         return;
5077     const bool wasSelected = es->contains((*extendFrom)->event());
5078 
5079     std::vector<Event *> eventVec;
5080 
5081     while (extendFrom != vel->end() &&
5082            (*extendFrom)->getViewAbsoluteTime() < newTime) {
5083         Event *event = (*extendFrom)->event();
5084         // Only grab notes for now, see slotExtendSelectionBackward()
5085         if (event->isa(Note::EventType)) {
5086             eventVec.push_back(event);
5087         }
5088         ++extendFrom;
5089     }
5090 
5091     const bool forward = true;
5092     extendSelectionHelper(forward, es, eventVec, !wasSelected);
5093 
5094     setSelection(es, true);
5095 }
5096 
5097 
5098 void
slotAddDot()5099 NotationView::slotAddDot()
5100 {
5101     EventSelection *selection = getSelection();
5102     if (!selection) return;
5103     TmpStatusMsg msg(tr("Adding dot..."), this);
5104     CommandHistory::getInstance()->addCommand
5105             (new AddDotCommand(*selection, false));
5106 }
5107 
5108 void
slotAddDotNotationOnly()5109 NotationView::slotAddDotNotationOnly()
5110 {
5111     EventSelection *selection = getSelection();
5112     if (!selection) return;
5113     TmpStatusMsg msg(tr("Adding dot..."), this);
5114     CommandHistory::getInstance()->addCommand
5115             (new AddDotCommand(*selection, true));
5116 }
5117 
5118 
5119 void
slotSetNoteType()5120 NotationView::slotSetNoteType()
5121 {
5122     QObject *s = sender();
5123     QString name = s->objectName();
5124     int note=Note::WholeNote;
5125 
5126     EventSelection *selection = getSelection();
5127     if (!selection) return;
5128     TmpStatusMsg msg(tr("Set Note Type..."), this);
5129 
5130     if (name == "set_note_type_doublewhole") note=Note::DoubleWholeNote;
5131     else if (name == "set_note_type_whole") note=Note::WholeNote;
5132     else if (name == "set_note_type_half") note=Note::HalfNote;
5133     else if (name == "set_note_type_quarter") note=Note::QuarterNote;
5134     else if (name == "set_note_type_eighth") note=Note::EighthNote;
5135     else if (name == "set_note_type_sixteenth") note=Note::SixteenthNote;
5136     else if (name == "set_note_type_thirtysecond") note=Note::ThirtySecondNote;
5137     else if (name == "set_note_type_sixtyfourth") note=Note::SixtyFourthNote;
5138 
5139     CommandHistory::getInstance()->addCommand
5140             (new SetNoteTypeCommand(*selection, note, false));
5141 }
5142 
5143 void
slotSetNoteTypeNotationOnly()5144 NotationView::slotSetNoteTypeNotationOnly()
5145 {
5146     QObject *s = sender();
5147     QString name = s->objectName();
5148     int note=Note::WholeNote;
5149 
5150     EventSelection *selection = getSelection();
5151     if (!selection) return;
5152     TmpStatusMsg msg(tr("Set Note Type notation only..."), this);
5153 
5154     if (name == "set_note_type_notation_doublewhole") note=Note::DoubleWholeNote;
5155     else if (name == "set_note_type_notation_whole") note=Note::WholeNote;
5156     else if (name == "set_note_type_notation_half") note=Note::HalfNote;
5157     else if (name == "set_note_type_notation_quarter") note=Note::QuarterNote;
5158     else if (name == "set_note_type_notation_eighth") note=Note::EighthNote;
5159     else if (name == "set_note_type_notation_sixteenth") note=Note::SixteenthNote;
5160     else if (name == "set_note_type_notation_thirtysecond") note=Note::ThirtySecondNote;
5161     else if (name == "set_note_type_notation_sixtyfourth") note=Note::SixtyFourthNote;
5162 
5163     CommandHistory::getInstance()->addCommand
5164             (new SetNoteTypeCommand(*selection, note, true));
5165 }
5166 
5167 void
slotCycleSlashes()5168 NotationView::slotCycleSlashes()
5169 {
5170     EventSelection *selection = getSelection();
5171     if (!selection) return;
5172     TmpStatusMsg msg(tr("Cycling slashes..."), this);
5173     CommandHistory::getInstance()->addCommand(new CycleSlashesCommand(*selection));
5174 }
5175 
5176 void
slotAddLayer()5177 NotationView::slotAddLayer()
5178 {
5179     // switch to the pencil, as we are about to create an empty segment for
5180     // editing
5181     //
5182     //!!! This also detours around at least three related but distinct crashes
5183     // in NotationSelector, although I do not fully fathom why this is so, and am
5184     // worried about memory leaks or other obnoxious gotchas waiting in the
5185     // wings.
5186     slotSetNoteRestInserter();
5187 
5188     Composition& comp = getDocument()->getComposition();
5189 
5190     MacroCommand *macro = new MacroCommand(tr("New Layer"));
5191 
5192     AddLayerCommand *command = new AddLayerCommand(getCurrentSegment(),
5193                                                    comp);
5194     macro->addCommand(command);
5195 
5196     // ??? Couldn't AdoptSegmentCommand take an AddLayerCommand pointer
5197     //     and use that to get the new Segment to adopt?  Then it wouldn't
5198     //     need to use marking.
5199     AdoptSegmentCommand *adoptCommand =
5200         new AdoptSegmentCommand(
5201                 "Adopt Layer",  // name
5202                 *this,  // view
5203                 "Added Layer",  // segmentMarking
5204                 &comp,  // composition
5205                 true,  // into
5206                 true);  // inComposition
5207     macro->addCommand(adoptCommand);
5208 
5209     CommandHistory::getInstance()->addCommand(macro);
5210 
5211     // DEBUG.
5212     // If Segment marking is removed, this can be done differently.
5213     // E.g. if (!command->getSegment())
5214     Segment *newLayer = comp.getSegmentByMarking("Added Layer");
5215     if (!newLayer) {
5216         RG_WARNING << "NotationView: new layer not found";
5217         return;
5218     }
5219 
5220     // Make the new segment active immediately.
5221 
5222     NotationScene *scene = m_notationWidget->getScene();
5223     // ??? Ok, this appears to be where we really need the Segment marking.
5224     //     Without the marking, we have no way of finding the new staff.
5225     //     It would be nice if AdoptSegmentCommand would determine the
5226     //     specific NotationStaff for the adopted Segment and be able to
5227     //     return it.  I.e. the NotationView::adopt*() functions should
5228     //     return NotationStaff pointers that AdoptSegmentCommand can
5229     //     store for this.  That might make it possible to completely remove
5230     //     the Segment marking.
5231     NotationStaff *newLayerStaff =
5232         scene->getStaffBySegmentMarking("Added Layer");
5233     if (!newLayerStaff) {
5234         RG_WARNING << "NotationView: new layer staff not found";
5235         return;
5236     }
5237 
5238     setCurrentStaff(newLayerStaff);
5239     slotEditSelectWholeStaff();
5240 
5241     enterActionState("have_multiple_staffs");
5242 
5243 }
5244 
5245 void
slotMagicLayer()5246 NotationView::slotMagicLayer()
5247 {
5248     EventSelection *selection = getSelection();
5249     if (!selection) return;
5250 
5251     // switch to the pencil, as in slotAddLayer
5252     slotSetNoteRestInserter();
5253 
5254     Segment* currentSegment = getCurrentSegment();
5255 
5256     MacroCommand *macro = new MacroCommand(tr("New Layer from Selection"));
5257 
5258     Composition& comp = getDocument()->getComposition();
5259     // make a new "layer" segment
5260     AddLayerCommand *command = new AddLayerCommand(currentSegment, comp);
5261     macro->addCommand(command);
5262 
5263     // cut the selected events from the parent segment
5264     timeT insertionTime = selection->getStartTime();
5265 
5266     Clipboard *c = new Clipboard;
5267     CopyCommand *cc = new CopyCommand(*selection, c);
5268     cc->execute();
5269 
5270     RG_DEBUG << "CopyCommand done";
5271     RG_DEBUG << "Clipboard contents";
5272     Segment* clipseg = c->getSingleSegment();
5273     if (clipseg) RG_DEBUG << *clipseg;
5274     RG_DEBUG << "Clipboard contents done";
5275 
5276     macro->addCommand(new EraseCommand(*selection));
5277 
5278     // use overlay paste to avoid checking for space; paste to new
5279     // "layer" identify the layer with the segment marking.
5280     PasteEventsCommand::PasteType type = PasteEventsCommand::NoteOverlay;
5281     macro->addCommand(new PasteEventsCommand("Added Layer", c,
5282                                              insertionTime, type));
5283 
5284     // and adopt the segment
5285     AdoptSegmentCommand *adoptCommand =
5286         new AdoptSegmentCommand("Adopt Layer", *this, "Added Layer",
5287                                 &comp, true, true);
5288     macro->addCommand(adoptCommand);
5289 
5290     CommandHistory::getInstance()->addCommand(macro);
5291 
5292     delete c;
5293 
5294     // make the new segment active immediately
5295     NotationScene *scene = m_notationWidget->getScene();
5296     NotationStaff* newLayerStaff =
5297         scene->getStaffBySegmentMarking("Added Layer");
5298     if (! newLayerStaff) {
5299         RG_WARNING << "NotationView: new layer staff not found";
5300         return;
5301     }
5302 
5303     setCurrentStaff(newLayerStaff);
5304     slotEditSelectWholeStaff();
5305 
5306     enterActionState("have_multiple_staffs");
5307 }
5308 
5309 void
slotConfigure()5310 NotationView::slotConfigure()
5311 {
5312     ConfigureDialog *configDlg =  new ConfigureDialog(getDocument(), this);
5313 
5314     configDlg->setNotationPage();
5315     configDlg->show();
5316 }
5317 
5318 void
slotCheckShowHeadersMenu(bool checked)5319 NotationView::slotCheckShowHeadersMenu(bool checked)
5320 {
5321     findAction("show_track_headers")->setChecked(checked);
5322 }
5323 
5324 /// YG: Only for debug
5325 void
slotDebugDump()5326 NotationView::slotDebugDump()
5327 {
5328     m_notationWidget->getScene()->dumpVectors();
5329 }
5330 
5331 /// YG: Only for debug
5332 void
slotBarDataDump()5333 NotationView::slotBarDataDump()
5334 {
5335     m_notationWidget->getScene()->dumpBarDataMap();
5336 }
5337 
5338 void
slotInterpretActivate()5339 NotationView::slotInterpretActivate()
5340 {
5341     // If there is a selection, run the interpretations against it in the usual
5342     // fashion.  If there is no selection, select the entire segment first.
5343     // (Will this work well in practice?  The last thing the user did will
5344     // probably leave a selection of one event.  Let's start here and refine as
5345     // the need becomes evident through testing.)
5346     EventSelection *selection = getSelection();
5347 
5348     // After placing a hairpin and likely other incidental actions, you can end
5349     // up with a valid selection that has a total duration of 0.  In this case,
5350     // treat it like there was no selection at all by just zeroing it out and
5351     // feeding it along to be replaced with a select all.
5352     if (selection) {
5353         if (selection->getTotalDuration() == 0) selection = nullptr;
5354     }
5355 
5356     // Selections aren't undoable anywhere else, so there's no point writing a
5357     // new command or making a MacroCommand.  Just call the slot as if the user
5358     // hit Ctrl+A and go.
5359     if (!selection) {
5360         slotEditSelectWholeStaff();
5361         selection = getSelection();
5362     }
5363 
5364     // Make sure it worked, just in case.
5365     if (!selection) return;
5366 
5367     // xor all the options together in the same fashion as the dialog
5368     int flags = 0;
5369     if (findAction("interpret_text_dynamics")->isChecked()) flags |= InterpretCommand::ApplyTextDynamics;
5370     if (findAction("interpret_hairpins")->isChecked()) flags |= InterpretCommand::ApplyHairpins;
5371     if (findAction("interpret_slurs")->isChecked()) flags |= InterpretCommand::Articulate;
5372     if (findAction("interpret_beats")->isChecked()) flags |= InterpretCommand::StressBeats;
5373 
5374     // Debug output just to make it possible to observe that all the checkable
5375     // buttons are working correctly:
5376     RG_DEBUG << "NotationView::slotInterpretActivate() using flags: "
5377              << (flags & InterpretCommand::ApplyTextDynamics ? "[TEXT]" : "[    ]")
5378              << (flags & InterpretCommand::ApplyHairpins ? "[HAIR]" : "[    ]")
5379              << (flags & InterpretCommand::Articulate ? "[SLUR]" : "[    ]")
5380              << (flags & InterpretCommand::StressBeats ? "[BEAT]" : "[    ]");
5381 
5382     // go straight to the command with the flags pulled from the toolbar as
5383     // though it were the dialog
5384     CommandHistory::getInstance()->addCommand(new InterpretCommand
5385          (*selection,
5386           getDocument()->getComposition().getNotationQuantizer(),
5387           flags));
5388 }
5389 
5390 
5391 } // end namespace Rosegarden
5392 
5393