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 ¬eName)
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