summaryrefslogtreecommitdiffstats
path: root/src/gui/editors
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/editors')
-rw-r--r--src/gui/editors/eventlist/EventView.cpp1606
-rw-r--r--src/gui/editors/eventlist/EventView.h205
-rw-r--r--src/gui/editors/eventlist/EventViewItem.cpp68
-rw-r--r--src/gui/editors/eventlist/EventViewItem.h101
-rw-r--r--src/gui/editors/eventlist/TrivialVelocityDialog.cpp48
-rw-r--r--src/gui/editors/eventlist/TrivialVelocityDialog.h48
-rw-r--r--src/gui/editors/guitar/Chord.cpp113
-rw-r--r--src/gui/editors/guitar/Chord.h106
-rw-r--r--src/gui/editors/guitar/ChordMap.cpp223
-rw-r--r--src/gui/editors/guitar/ChordMap.h87
-rw-r--r--src/gui/editors/guitar/ChordXmlHandler.cpp154
-rw-r--r--src/gui/editors/guitar/ChordXmlHandler.h78
-rw-r--r--src/gui/editors/guitar/Fingering.cpp152
-rw-r--r--src/gui/editors/guitar/Fingering.h95
-rw-r--r--src/gui/editors/guitar/FingeringBox.cpp293
-rw-r--r--src/gui/editors/guitar/FingeringBox.h106
-rw-r--r--src/gui/editors/guitar/FingeringListBoxItem.cpp36
-rw-r--r--src/gui/editors/guitar/FingeringListBoxItem.h46
-rw-r--r--src/gui/editors/guitar/GuitarChordEditorDialog.cpp109
-rw-r--r--src/gui/editors/guitar/GuitarChordEditorDialog.h67
-rw-r--r--src/gui/editors/guitar/GuitarChordSelectorDialog.cpp475
-rw-r--r--src/gui/editors/guitar/GuitarChordSelectorDialog.h120
-rw-r--r--src/gui/editors/guitar/NoteSymbols.cpp486
-rw-r--r--src/gui/editors/guitar/NoteSymbols.h192
-rw-r--r--src/gui/editors/matrix/MatrixCanvasView.cpp302
-rw-r--r--src/gui/editors/matrix/MatrixCanvasView.h162
-rw-r--r--src/gui/editors/matrix/MatrixElement.cpp160
-rw-r--r--src/gui/editors/matrix/MatrixElement.h138
-rw-r--r--src/gui/editors/matrix/MatrixEraser.cpp110
-rw-r--r--src/gui/editors/matrix/MatrixEraser.h69
-rw-r--r--src/gui/editors/matrix/MatrixHLayout.cpp220
-rw-r--r--src/gui/editors/matrix/MatrixHLayout.h150
-rw-r--r--src/gui/editors/matrix/MatrixMover.cpp481
-rw-r--r--src/gui/editors/matrix/MatrixMover.h112
-rw-r--r--src/gui/editors/matrix/MatrixPainter.cpp370
-rw-r--r--src/gui/editors/matrix/MatrixPainter.h105
-rw-r--r--src/gui/editors/matrix/MatrixParameterBox.cpp99
-rw-r--r--src/gui/editors/matrix/MatrixParameterBox.h76
-rw-r--r--src/gui/editors/matrix/MatrixResizer.cpp333
-rw-r--r--src/gui/editors/matrix/MatrixResizer.h102
-rw-r--r--src/gui/editors/matrix/MatrixSelector.cpp629
-rw-r--r--src/gui/editors/matrix/MatrixSelector.h177
-rw-r--r--src/gui/editors/matrix/MatrixStaff.cpp232
-rw-r--r--src/gui/editors/matrix/MatrixStaff.h111
-rw-r--r--src/gui/editors/matrix/MatrixTool.cpp79
-rw-r--r--src/gui/editors/matrix/MatrixTool.h74
-rw-r--r--src/gui/editors/matrix/MatrixToolBox.cpp87
-rw-r--r--src/gui/editors/matrix/MatrixToolBox.h60
-rw-r--r--src/gui/editors/matrix/MatrixVLayout.cpp100
-rw-r--r--src/gui/editors/matrix/MatrixVLayout.h91
-rw-r--r--src/gui/editors/matrix/MatrixView.cpp3076
-rw-r--r--src/gui/editors/matrix/MatrixView.h692
-rw-r--r--src/gui/editors/matrix/PianoKeyboard.cpp299
-rw-r--r--src/gui/editors/matrix/PianoKeyboard.h133
-rw-r--r--src/gui/editors/matrix/QCanvasMatrixDiamond.cpp82
-rw-r--r--src/gui/editors/matrix/QCanvasMatrixDiamond.h61
-rw-r--r--src/gui/editors/matrix/QCanvasMatrixRectangle.cpp44
-rw-r--r--src/gui/editors/matrix/QCanvasMatrixRectangle.h60
-rw-r--r--src/gui/editors/notation/ClefInserter.cpp132
-rw-r--r--src/gui/editors/notation/ClefInserter.h83
-rw-r--r--src/gui/editors/notation/FontViewFrame.cpp252
-rw-r--r--src/gui/editors/notation/FontViewFrame.h77
-rw-r--r--src/gui/editors/notation/GuitarChordInserter.cpp185
-rw-r--r--src/gui/editors/notation/GuitarChordInserter.h96
-rw-r--r--src/gui/editors/notation/HeadersGroup.cpp160
-rw-r--r--src/gui/editors/notation/HeadersGroup.h144
-rw-r--r--src/gui/editors/notation/NotationCanvasView.cpp485
-rw-r--r--src/gui/editors/notation/NotationCanvasView.h218
-rw-r--r--src/gui/editors/notation/NotationChord.cpp335
-rw-r--r--src/gui/editors/notation/NotationChord.h90
-rw-r--r--src/gui/editors/notation/NotationElement.cpp198
-rw-r--r--src/gui/editors/notation/NotationElement.h176
-rw-r--r--src/gui/editors/notation/NotationEraser.cpp115
-rw-r--r--src/gui/editors/notation/NotationEraser.h81
-rw-r--r--src/gui/editors/notation/NotationGroup.cpp979
-rw-r--r--src/gui/editors/notation/NotationGroup.h133
-rw-r--r--src/gui/editors/notation/NotationHLayout.cpp2110
-rw-r--r--src/gui/editors/notation/NotationHLayout.h446
-rw-r--r--src/gui/editors/notation/NotationProperties.cpp85
-rw-r--r--src/gui/editors/notation/NotationProperties.h108
-rw-r--r--src/gui/editors/notation/NotationSelectionPaster.cpp89
-rw-r--r--src/gui/editors/notation/NotationSelectionPaster.h72
-rw-r--r--src/gui/editors/notation/NotationSelector.cpp957
-rw-r--r--src/gui/editors/notation/NotationSelector.h197
-rw-r--r--src/gui/editors/notation/NotationStaff.cpp2300
-rw-r--r--src/gui/editors/notation/NotationStaff.h488
-rw-r--r--src/gui/editors/notation/NotationStrings.cpp301
-rw-r--r--src/gui/editors/notation/NotationStrings.h121
-rw-r--r--src/gui/editors/notation/NotationTool.cpp57
-rw-r--r--src/gui/editors/notation/NotationTool.h93
-rw-r--r--src/gui/editors/notation/NotationToolBox.cpp102
-rw-r--r--src/gui/editors/notation/NotationToolBox.h65
-rw-r--r--src/gui/editors/notation/NotationVLayout.cpp731
-rw-r--r--src/gui/editors/notation/NotationVLayout.h122
-rw-r--r--src/gui/editors/notation/NotationView.cpp7552
-rw-r--r--src/gui/editors/notation/NotationView.h1131
-rw-r--r--src/gui/editors/notation/NoteCharacter.cpp133
-rw-r--r--src/gui/editors/notation/NoteCharacter.h93
-rw-r--r--src/gui/editors/notation/NoteCharacterNames.cpp123
-rw-r--r--src/gui/editors/notation/NoteCharacterNames.h120
-rw-r--r--src/gui/editors/notation/NoteFont.cpp650
-rw-r--r--src/gui/editors/notation/NoteFont.h184
-rw-r--r--src/gui/editors/notation/NoteFontFactory.cpp236
-rw-r--r--src/gui/editors/notation/NoteFontFactory.h71
-rw-r--r--src/gui/editors/notation/NoteFontMap.cpp1088
-rw-r--r--src/gui/editors/notation/NoteFontMap.h333
-rw-r--r--src/gui/editors/notation/NoteFontViewer.cpp125
-rw-r--r--src/gui/editors/notation/NoteFontViewer.h68
-rw-r--r--src/gui/editors/notation/NoteInserter.cpp722
-rw-r--r--src/gui/editors/notation/NoteInserter.h166
-rw-r--r--src/gui/editors/notation/NotePixmapFactory.cpp3689
-rw-r--r--src/gui/editors/notation/NotePixmapFactory.h358
-rw-r--r--src/gui/editors/notation/NotePixmapPainter.h148
-rw-r--r--src/gui/editors/notation/NotePixmapParameters.cpp151
-rw-r--r--src/gui/editors/notation/NotePixmapParameters.h161
-rw-r--r--src/gui/editors/notation/NoteStyle.cpp485
-rw-r--r--src/gui/editors/notation/NoteStyle.h142
-rw-r--r--src/gui/editors/notation/NoteStyleFactory.cpp124
-rw-r--r--src/gui/editors/notation/NoteStyleFactory.h61
-rw-r--r--src/gui/editors/notation/NoteStyleFileReader.cpp193
-rw-r--r--src/gui/editors/notation/NoteStyleFileReader.h59
-rw-r--r--src/gui/editors/notation/RestInserter.cpp150
-rw-r--r--src/gui/editors/notation/RestInserter.h76
-rw-r--r--src/gui/editors/notation/SystemFont.cpp165
-rw-r--r--src/gui/editors/notation/SystemFont.h63
-rw-r--r--src/gui/editors/notation/SystemFontQt.cpp78
-rw-r--r--src/gui/editors/notation/SystemFontQt.h49
-rw-r--r--src/gui/editors/notation/SystemFontXft.cpp193
-rw-r--r--src/gui/editors/notation/SystemFontXft.h58
-rw-r--r--src/gui/editors/notation/TextInserter.cpp169
-rw-r--r--src/gui/editors/notation/TextInserter.h78
-rw-r--r--src/gui/editors/notation/TrackHeader.cpp450
-rw-r--r--src/gui/editors/notation/TrackHeader.h219
-rw-r--r--src/gui/editors/parameters/AudioInstrumentParameterPanel.cpp437
-rw-r--r--src/gui/editors/parameters/AudioInstrumentParameterPanel.h107
-rw-r--r--src/gui/editors/parameters/InstrumentParameterBox.cpp265
-rw-r--r--src/gui/editors/parameters/InstrumentParameterBox.h126
-rw-r--r--src/gui/editors/parameters/InstrumentParameterPanel.cpp61
-rw-r--r--src/gui/editors/parameters/InstrumentParameterPanel.h78
-rw-r--r--src/gui/editors/parameters/MIDIInstrumentParameterPanel.cpp1175
-rw-r--r--src/gui/editors/parameters/MIDIInstrumentParameterPanel.h137
-rw-r--r--src/gui/editors/parameters/RosegardenParameterArea.cpp227
-rw-r--r--src/gui/editors/parameters/RosegardenParameterArea.h108
-rw-r--r--src/gui/editors/parameters/RosegardenParameterBox.cpp89
-rw-r--r--src/gui/editors/parameters/RosegardenParameterBox.h92
-rw-r--r--src/gui/editors/parameters/SegmentParameterBox.cpp1214
-rw-r--r--src/gui/editors/parameters/SegmentParameterBox.h174
-rw-r--r--src/gui/editors/parameters/TrackParameterBox.cpp1022
-rw-r--r--src/gui/editors/parameters/TrackParameterBox.h161
-rw-r--r--src/gui/editors/segment/ControlEditorDialog.cpp446
-rw-r--r--src/gui/editors/segment/ControlEditorDialog.h122
-rw-r--r--src/gui/editors/segment/ControlParameterEditDialog.cpp325
-rw-r--r--src/gui/editors/segment/ControlParameterEditDialog.h92
-rw-r--r--src/gui/editors/segment/ControlParameterItem.cpp34
-rw-r--r--src/gui/editors/segment/ControlParameterItem.h65
-rw-r--r--src/gui/editors/segment/MarkerEditor.cpp594
-rw-r--r--src/gui/editors/segment/MarkerEditor.h124
-rw-r--r--src/gui/editors/segment/MarkerEditorViewItem.cpp51
-rw-r--r--src/gui/editors/segment/MarkerEditorViewItem.h70
-rw-r--r--src/gui/editors/segment/PlayList.cpp254
-rw-r--r--src/gui/editors/segment/PlayList.h93
-rw-r--r--src/gui/editors/segment/PlayListDialog.cpp76
-rw-r--r--src/gui/editors/segment/PlayListDialog.h71
-rw-r--r--src/gui/editors/segment/PlayListView.cpp66
-rw-r--r--src/gui/editors/segment/PlayListView.h52
-rw-r--r--src/gui/editors/segment/PlayListViewItem.cpp42
-rw-r--r--src/gui/editors/segment/PlayListViewItem.h47
-rw-r--r--src/gui/editors/segment/TrackButtons.cpp1149
-rw-r--r--src/gui/editors/segment/TrackButtons.h228
-rw-r--r--src/gui/editors/segment/TrackEditor.cpp827
-rw-r--r--src/gui/editors/segment/TrackEditor.h248
-rw-r--r--src/gui/editors/segment/TrackEditorIface.cpp33
-rw-r--r--src/gui/editors/segment/TrackEditorIface.h55
-rw-r--r--src/gui/editors/segment/TrackHeader.cpp64
-rw-r--r--src/gui/editors/segment/TrackHeader.h65
-rw-r--r--src/gui/editors/segment/TrackLabel.cpp203
-rw-r--r--src/gui/editors/segment/TrackLabel.h122
-rw-r--r--src/gui/editors/segment/TrackVUMeter.cpp77
-rw-r--r--src/gui/editors/segment/TrackVUMeter.h65
-rw-r--r--src/gui/editors/segment/TriggerManagerItem.cpp60
-rw-r--r--src/gui/editors/segment/TriggerManagerItem.h72
-rw-r--r--src/gui/editors/segment/TriggerSegmentManager.cpp576
-rw-r--r--src/gui/editors/segment/TriggerSegmentManager.h116
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.cpp316
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.h79
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewThread.cpp267
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewThread.h99
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.cpp149
-rw-r--r--src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.h90
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionColourCache.cpp62
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionColourCache.h69
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItem.cpp34
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItem.h67
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItemHelper.cpp150
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItemHelper.h61
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItemImpl.cpp67
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionItemImpl.h74
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionModel.cpp43
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionModel.h179
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionModelImpl.cpp1328
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionModelImpl.h239
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionRect.cpp42
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionRect.h108
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionView.cpp1591
-rw-r--r--src/gui/editors/segment/segmentcanvas/CompositionView.h366
-rw-r--r--src/gui/editors/segment/segmentcanvas/PreviewRect.cpp34
-rw-r--r--src/gui/editors/segment/segmentcanvas/PreviewRect.h62
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentEraser.cpp88
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentEraser.h67
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentItemPreview.cpp37
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentItemPreview.h91
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentJoiner.cpp73
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentJoiner.h70
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentMover.cpp348
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentMover.h78
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentOrderer.cpp48
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentOrderer.h59
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentPencil.cpp295
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentPencil.h83
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentResizer.cpp393
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentResizer.h87
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentSelector.cpp532
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentSelector.h109
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentSplitter.cpp175
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentSplitter.h83
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentTool.cpp115
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentTool.h105
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentToolBox.cpp102
-rw-r--r--src/gui/editors/segment/segmentcanvas/SegmentToolBox.h63
-rw-r--r--src/gui/editors/tempo/TempoListItem.cpp52
-rw-r--r--src/gui/editors/tempo/TempoListItem.h72
-rw-r--r--src/gui/editors/tempo/TempoView.cpp839
-rw-r--r--src/gui/editors/tempo/TempoView.h172
233 files changed, 68456 insertions, 0 deletions
diff --git a/src/gui/editors/eventlist/EventView.cpp b/src/gui/editors/eventlist/EventView.cpp
new file mode 100644
index 0000000..13bd294
--- /dev/null
+++ b/src/gui/editors/eventlist/EventView.cpp
@@ -0,0 +1,1606 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "EventView.h"
+#include "EventViewItem.h"
+#include "TrivialVelocityDialog.h"
+#include "base/BaseProperties.h"
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Clipboard.h"
+#include "base/Event.h"
+#include "base/MidiTypes.h"
+#include "base/NotationTypes.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "base/SegmentPerformanceHelper.h"
+#include "base/Selection.h"
+#include "base/Track.h"
+#include "base/TriggerSegment.h"
+#include "commands/edit/CopyCommand.h"
+#include "commands/edit/CutCommand.h"
+#include "commands/edit/EraseCommand.h"
+#include "commands/edit/EventEditCommand.h"
+#include "commands/edit/PasteEventsCommand.h"
+#include "commands/edit/EventInsertionCommand.h"
+#include "commands/segment/SegmentLabelCommand.h"
+#include "commands/segment/SetTriggerSegmentBasePitchCommand.h"
+#include "commands/segment/SetTriggerSegmentBaseVelocityCommand.h"
+#include "commands/segment/SetTriggerSegmentDefaultRetuneCommand.h"
+#include "commands/segment/SetTriggerSegmentDefaultTimeAdjustCommand.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/dialogs/EventEditDialog.h"
+#include "gui/dialogs/PitchDialog.h"
+#include "gui/dialogs/SimpleEventEditDialog.h"
+#include "gui/general/EditViewBase.h"
+#include "gui/general/MidiPitchLabel.h"
+#include "gui/kdeext/KTmpStatusMsg.h"
+#include "gui/dialogs/EventFilterDialog.h"
+#include <kaction.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kstatusbar.h>
+#include <kstddirs.h>
+#include <kglobal.h>
+#include <klineeditdlg.h>
+#include <klistview.h>
+#include <kxmlguiclient.h>
+#include <qbuttongroup.h>
+#include <qcanvas.h>
+#include <qcheckbox.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qiconset.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qlistview.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qpopupmenu.h>
+#include <qpushbutton.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qwidget.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+int
+EventView::m_lastSetEventFilter = -1;
+
+
+EventView::EventView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ QWidget *parent):
+ EditViewBase(doc, segments, 2, parent, "eventview"),
+ m_eventFilter(Note | Text | SystemExclusive | Controller |
+ ProgramChange | PitchBend | Indication | Other),
+ m_menu(0)
+{
+ m_isTriggerSegment = false;
+ m_triggerName = m_triggerPitch = m_triggerVelocity = 0;
+
+ if (!segments.empty()) {
+ Segment *s = *segments.begin();
+ if (s->getComposition()) {
+ int id = s->getComposition()->getTriggerSegmentId(s);
+ if (id >= 0)
+ m_isTriggerSegment = true;
+ }
+ }
+
+ if (m_lastSetEventFilter < 0)
+ m_lastSetEventFilter = m_eventFilter;
+ else
+ m_eventFilter = m_lastSetEventFilter;
+
+ initStatusBar();
+ setupActions();
+
+ // define some note filtering buttons in a group
+ //
+ m_filterGroup =
+ new QButtonGroup(1, Horizontal, i18n("Event filters"), getCentralWidget());
+
+ m_noteCheckBox = new QCheckBox(i18n("Note"), m_filterGroup);
+ m_programCheckBox = new QCheckBox(i18n("Program Change"), m_filterGroup);
+ m_controllerCheckBox = new QCheckBox(i18n("Controller"), m_filterGroup);
+ m_pitchBendCheckBox = new QCheckBox(i18n("Pitch Bend"), m_filterGroup);
+ m_sysExCheckBox = new QCheckBox(i18n("System Exclusive"), m_filterGroup);
+ m_keyPressureCheckBox = new QCheckBox(i18n("Key Pressure"), m_filterGroup);
+ m_channelPressureCheckBox = new QCheckBox(i18n("Channel Pressure"), m_filterGroup);
+ m_restCheckBox = new QCheckBox(i18n("Rest"), m_filterGroup);
+ m_indicationCheckBox = new QCheckBox(i18n("Indication"), m_filterGroup);
+ m_textCheckBox = new QCheckBox(i18n("Text"), m_filterGroup);
+ m_otherCheckBox = new QCheckBox(i18n("Other"), m_filterGroup);
+ m_grid->addWidget(m_filterGroup, 2, 0);
+
+ // Connect up
+ //
+ connect(m_filterGroup, SIGNAL(released(int)),
+ SLOT(slotModifyFilter(int)));
+
+ m_eventList = new KListView(getCentralWidget());
+ m_eventList->setItemsRenameable(true);
+
+ m_grid->addWidget(m_eventList, 2, 1);
+
+ if (m_isTriggerSegment) {
+
+ int id = segments[0]->getComposition()->getTriggerSegmentId(segments[0]);
+ TriggerSegmentRec *rec =
+ segments[0]->getComposition()->getTriggerSegmentRec(id);
+
+ QGroupBox *groupBox = new QGroupBox
+ (1, Horizontal, i18n("Triggered Segment Properties"), getCentralWidget());
+
+ QFrame *frame = new QFrame(groupBox);
+ QGridLayout *layout = new QGridLayout(frame, 5, 3, 5, 5);
+
+ layout->addWidget(new QLabel(i18n("Label: "), frame), 0, 0);
+ QString label = strtoqstr(segments[0]->getLabel());
+ if (label == "")
+ label = i18n("<no label>");
+ m_triggerName = new QLabel(label, frame);
+ layout->addWidget(m_triggerName, 0, 1);
+ QPushButton *editButton = new QPushButton(i18n("edit"), frame);
+ layout->addWidget(editButton, 0, 2);
+ connect(editButton, SIGNAL(clicked()), this, SLOT(slotEditTriggerName()));
+
+ layout->addWidget(new QLabel(i18n("Base pitch: "), frame), 1, 0);
+ m_triggerPitch = new QLabel(QString("%1").arg(rec->getBasePitch()), frame);
+ layout->addWidget(m_triggerPitch, 1, 1);
+ editButton = new QPushButton(i18n("edit"), frame);
+ layout->addWidget(editButton, 1, 2);
+ connect(editButton, SIGNAL(clicked()), this, SLOT(slotEditTriggerPitch()));
+
+ layout->addWidget(new QLabel(i18n("Base velocity: "), frame), 2, 0);
+ m_triggerVelocity = new QLabel(QString("%1").arg(rec->getBaseVelocity()), frame);
+ layout->addWidget(m_triggerVelocity, 2, 1);
+ editButton = new QPushButton(i18n("edit"), frame);
+ layout->addWidget(editButton, 2, 2);
+ connect(editButton, SIGNAL(clicked()), this, SLOT(slotEditTriggerVelocity()));
+
+ /*!!! Comment out these two options, which are not yet used
+ anywhere else -- intended for use with library ornaments, not
+ yet implemented
+
+ layout->addWidget(new QLabel(i18n("Default timing: "), frame), 3, 0);
+
+ KComboBox *adjust = new KComboBox(frame);
+ layout->addMultiCellWidget(adjust, 3, 3, 1, 2);
+ adjust->insertItem(i18n("As stored"));
+ adjust->insertItem(i18n("Truncate if longer than note"));
+ adjust->insertItem(i18n("End at same time as note"));
+ adjust->insertItem(i18n("Stretch or squash segment to note duration"));
+
+ std::string timing = rec->getDefaultTimeAdjust();
+ if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_NONE) {
+ adjust->setCurrentItem(0);
+ } else if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_SQUISH) {
+ adjust->setCurrentItem(3);
+ } else if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_START) {
+ adjust->setCurrentItem(1);
+ } else if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_END) {
+ adjust->setCurrentItem(2);
+ }
+
+ connect(adjust, SIGNAL(activated(int)), this, SLOT(slotTriggerTimeAdjustChanged(int)));
+
+ QCheckBox *retune = new QCheckBox(i18n("Adjust pitch to trigger note by default"), frame);
+ retune->setChecked(rec->getDefaultRetune());
+ connect(retune, SIGNAL(clicked()), this, SLOT(slotTriggerRetuneChanged()));
+ layout->addMultiCellWidget(retune, 4, 4, 1, 2);
+
+ */
+
+ m_grid->addWidget(groupBox, 2, 2);
+
+ }
+
+ updateViewCaption();
+
+ for (unsigned int i = 0; i < m_segments.size(); ++i) {
+ m_segments[i]->addObserver(this);
+ }
+
+ // Connect double clicker
+ //
+ connect(m_eventList, SIGNAL(doubleClicked(QListViewItem*)),
+ SLOT(slotPopupEventEditor(QListViewItem*)));
+
+ connect(m_eventList,
+ SIGNAL(rightButtonPressed(QListViewItem*, const QPoint&, int)),
+ SLOT(slotPopupMenu(QListViewItem*, const QPoint&, int)));
+
+ m_eventList->setAllColumnsShowFocus(true);
+ m_eventList->setSelectionMode(QListView::Extended);
+
+ m_eventList->addColumn(i18n("Time "));
+ m_eventList->addColumn(i18n("Duration "));
+ m_eventList->addColumn(i18n("Event Type "));
+ m_eventList->addColumn(i18n("Pitch "));
+ m_eventList->addColumn(i18n("Velocity "));
+ m_eventList->addColumn(i18n("Type (Data1) "));
+ m_eventList->addColumn(i18n("Value (Data2) "));
+
+ for (int col = 0; col < m_eventList->columns(); ++col)
+ m_eventList->setRenameable(col, true);
+
+ readOptions();
+ setButtonsToFilter();
+ applyLayout();
+
+ makeInitialSelection(doc->getComposition().getPosition());
+
+ slotCompositionStateUpdate();
+
+ setOutOfCtor();
+}
+
+EventView::~EventView()
+{
+ for (unsigned int i = 0; i < m_segments.size(); ++i) {
+ RG_DEBUG << "~EventView - removing this observer from " << m_segments[i] << endl;
+ m_segments[i]->removeObserver(this);
+ }
+}
+
+void
+EventView::eventRemoved(const Segment *, Event *e)
+{
+ m_deletedEvents.insert(e);
+}
+
+void
+EventView::segmentDeleted(const Segment *s)
+{
+ std::vector<Segment *>::iterator i = std::find(m_segments.begin(), m_segments.end(), s);
+
+ if (i != m_segments.end()) {
+ m_segments.erase(i);
+ } else {
+ RG_DEBUG << "%%% WARNING - EventView::segmentDeleted() called on non-registered segment - should not happen\n";
+ }
+
+}
+
+bool
+EventView::applyLayout(int /*staffNo*/)
+{
+ // If no selection has already been set then we copy what's
+ // already set and try to replicate this after the rebuild
+ // of the view.
+ //
+ if (m_listSelection.size() == 0) {
+ QPtrList<QListViewItem> selection = m_eventList->selectedItems();
+
+ if (selection.count()) {
+ QPtrListIterator<QListViewItem> it(selection);
+ QListViewItem *listItem;
+
+ while ((listItem = it.current()) != 0) {
+ m_listSelection.push_back(m_eventList->itemIndex(*it));
+ ++it;
+ }
+ }
+ }
+
+ // Ok, recreate list
+ //
+ m_eventList->clear();
+
+ m_config->setGroup(EventViewConfigGroup);
+ int timeMode = m_config->readNumEntry("timemode", 0);
+
+ for (unsigned int i = 0; i < m_segments.size(); i++) {
+ SegmentPerformanceHelper helper(*m_segments[i]);
+
+ for (Segment::iterator it = m_segments[i]->begin();
+ m_segments[i]->isBeforeEndMarker(it); it++) {
+ timeT eventTime =
+ helper.getSoundingAbsoluteTime(it);
+
+ QString velyStr;
+ QString pitchStr;
+ QString data1Str = "";
+ QString data2Str = "";
+ QString durationStr;
+
+ // Event filters
+ //
+
+ if ((*it)->isa(Note::EventRestType)) {
+ if (!(m_eventFilter & Rest))
+ continue;
+
+ } else if ((*it)->isa(Note::EventType)) {
+ if (!(m_eventFilter & Note))
+ continue;
+
+ } else if ((*it)->isa(Indication::EventType)) {
+ if (!(m_eventFilter & Indication))
+ continue;
+
+ } else if ((*it)->isa(PitchBend::EventType)) {
+ if (!(m_eventFilter & PitchBend))
+ continue;
+
+ } else if ((*it)->isa(SystemExclusive::EventType)) {
+ if (!(m_eventFilter & SystemExclusive))
+ continue;
+
+ } else if ((*it)->isa(ProgramChange::EventType)) {
+ if (!(m_eventFilter & ProgramChange))
+ continue;
+
+ } else if ((*it)->isa(ChannelPressure::EventType)) {
+ if (!(m_eventFilter & ChannelPressure))
+ continue;
+
+ } else if ((*it)->isa(KeyPressure::EventType)) {
+ if (!(m_eventFilter & KeyPressure))
+ continue;
+
+ } else if ((*it)->isa(Controller::EventType)) {
+ if (!(m_eventFilter & Controller))
+ continue;
+
+ } else if ((*it)->isa(Text::EventType)) {
+ if (!(m_eventFilter & Text))
+ continue;
+
+ } else {
+ if (!(m_eventFilter & Other))
+ continue;
+ }
+
+ // avoid debug stuff going to stderr if no properties found
+
+ if ((*it)->has(BaseProperties::PITCH)) {
+ int p = (*it)->get
+ <Int>(BaseProperties::PITCH);
+ pitchStr = QString("%1 %2 ")
+ .arg(p).arg(MidiPitchLabel(p).getQString());
+ } else if ((*it)->isa(Note::EventType)) {
+ pitchStr = "<not set>";
+ }
+
+ if ((*it)->has(BaseProperties::VELOCITY)) {
+ velyStr = QString("%1 ").
+ arg((*it)->get
+ <Int>(BaseProperties::VELOCITY));
+ } else if ((*it)->isa(Note::EventType)) {
+ velyStr = "<not set>";
+ }
+
+ if ((*it)->has(Controller::NUMBER)) {
+ data1Str = QString("%1 ").
+ arg((*it)->get
+ <Int>(Controller::NUMBER));
+ } else if ((*it)->has(Text::TextTypePropertyName)) {
+ data1Str = QString("%1 ").
+ arg(strtoqstr((*it)->get
+ <String>
+ (Text::TextTypePropertyName)));
+ } else if ((*it)->has(Indication::
+ IndicationTypePropertyName)) {
+ data1Str = QString("%1 ").
+ arg(strtoqstr((*it)->get
+ <String>
+ (Indication::
+ IndicationTypePropertyName)));
+ } else if ((*it)->has(::Rosegarden::Key::KeyPropertyName)) {
+ data1Str = QString("%1 ").
+ arg(strtoqstr((*it)->get
+ <String>
+ (::Rosegarden::Key::KeyPropertyName)));
+ } else if ((*it)->has(Clef::ClefPropertyName)) {
+ data1Str = QString("%1 ").
+ arg(strtoqstr((*it)->get
+ <String>
+ (Clef::ClefPropertyName)));
+ } else if ((*it)->has(PitchBend::MSB)) {
+ data1Str = QString("%1 ").
+ arg((*it)->get
+ <Int>(PitchBend::MSB));
+ } else if ((*it)->has(BaseProperties::BEAMED_GROUP_TYPE)) {
+ data1Str = QString("%1 ").
+ arg(strtoqstr((*it)->get
+ <String>
+ (BaseProperties::BEAMED_GROUP_TYPE)));
+ }
+
+ if ((*it)->has(Controller::VALUE)) {
+ data2Str = QString("%1 ").
+ arg((*it)->get
+ <Int>(Controller::VALUE));
+ } else if ((*it)->has(Text::TextPropertyName)) {
+ data2Str = QString("%1 ").
+ arg(strtoqstr((*it)->get
+ <String>
+ (Text::TextPropertyName)));
+ /*!!!
+ } else if ((*it)->has(Indication::
+ IndicationTypePropertyName)) {
+ data2Str = QString("%1 ").
+ arg((*it)->get<Int>(Indication::
+ IndicationDurationPropertyName));
+ */
+ } else if ((*it)->has(PitchBend::LSB)) {
+ data2Str = QString("%1 ").
+ arg((*it)->get
+ <Int>(PitchBend::LSB));
+ } else if ((*it)->has(BaseProperties::BEAMED_GROUP_ID)) {
+ data2Str = i18n("(group %1) ").
+ arg((*it)->get
+ <Int>(BaseProperties::BEAMED_GROUP_ID));
+ }
+
+ if ((*it)->has(ProgramChange::PROGRAM)) {
+ data1Str = QString("%1 ").
+ arg((*it)->get
+ <Int>(ProgramChange::PROGRAM) + 1);
+ }
+
+ if ((*it)->has(ChannelPressure::PRESSURE)) {
+ data1Str = QString("%1 ").
+ arg((*it)->get
+ <Int>(ChannelPressure::PRESSURE));
+ }
+
+ if ((*it)->isa(KeyPressure::EventType) &&
+ (*it)->has(KeyPressure::PITCH)) {
+ data1Str = QString("%1 ").
+ arg((*it)->get
+ <Int>(KeyPressure::PITCH));
+ }
+
+ if ((*it)->has(KeyPressure::PRESSURE)) {
+ data2Str = QString("%1 ").
+ arg((*it)->get
+ <Int>(KeyPressure::PRESSURE));
+ }
+
+
+ if ((*it)->getDuration() > 0 ||
+ (*it)->isa(Note::EventType) ||
+ (*it)->isa(Note::EventRestType)) {
+ durationStr = makeDurationString(eventTime,
+ (*it)->getDuration(),
+ timeMode);
+ }
+
+ QString timeStr = makeTimeString(eventTime, timeMode);
+
+ new EventViewItem(m_segments[i],
+ *it,
+ m_eventList,
+ timeStr,
+ durationStr,
+ strtoqstr((*it)->getType()),
+ pitchStr,
+ velyStr,
+ data1Str,
+ data2Str);
+ }
+ }
+
+
+ if (m_eventList->childCount() == 0) {
+ if (m_segments.size())
+ new QListViewItem(m_eventList,
+ i18n("<no events at this filter level>"));
+ else
+ new QListViewItem(m_eventList, i18n("<no events>"));
+
+ m_eventList->setSelectionMode(QListView::NoSelection);
+ stateChanged("have_selection", KXMLGUIClient::StateReverse);
+ } else {
+ m_eventList->setSelectionMode(QListView::Extended);
+
+ // If no selection then select the first event
+ if (m_listSelection.size() == 0)
+ m_listSelection.push_back(0);
+
+ stateChanged("have_selection", KXMLGUIClient::StateNoReverse);
+ }
+
+ // Set a selection from a range of indexes
+ //
+ std::vector<int>::iterator sIt = m_listSelection.begin();
+ int index = 0;
+
+ for (; sIt != m_listSelection.end(); ++sIt) {
+ index = *sIt;
+
+ while (index > 0 && !m_eventList->itemAtIndex(index))
+ index--;
+
+ m_eventList->setSelected(m_eventList->itemAtIndex(index), true);
+ m_eventList->setCurrentItem(m_eventList->itemAtIndex(index));
+
+ // ensure visible
+ m_eventList->ensureItemVisible(m_eventList->itemAtIndex(index));
+ }
+
+ m_listSelection.clear();
+ m_deletedEvents.clear();
+
+ return true;
+}
+
+void
+EventView::makeInitialSelection(timeT time)
+{
+ m_listSelection.clear();
+
+ EventViewItem *goodItem = 0;
+ int goodItemNo = 0;
+
+ int i = 0;
+
+ for (QListViewItem *child = m_eventList->firstChild();
+ child;
+ child = child->nextSibling()) {
+
+ EventViewItem * item = dynamic_cast<EventViewItem *>(child);
+
+ if (item) {
+ if (item->getEvent()->getAbsoluteTime() > time)
+ break;
+ goodItem = item;
+ goodItemNo = i;
+ }
+
+ ++i;
+ }
+ /*!!!
+ for (int i = 0; m_eventList->itemAtIndex(i); ++i) {
+
+ EventViewItem *item = dynamic_cast<EventViewItem *>
+ (m_eventList->itemAtIndex(i));
+
+ if (item) {
+ if (item->getEvent()->getAbsoluteTime() > time) break;
+ goodItem = item;
+ goodItemNo = i;
+ }
+ }
+ */
+ if (goodItem) {
+ m_listSelection.push_back(goodItemNo);
+ m_eventList->setSelected(goodItem, true);
+ m_eventList->ensureItemVisible(goodItem);
+ }
+}
+
+QString
+EventView::makeTimeString(timeT time, int timeMode)
+{
+ switch (timeMode) {
+
+ case 0: // musical time
+ {
+ int bar, beat, fraction, remainder;
+ getDocument()->getComposition().getMusicalTimeForAbsoluteTime
+ (time, bar, beat, fraction, remainder);
+ ++bar;
+ return QString("%1%2%3-%4%5-%6%7-%8%9 ")
+ .arg(bar / 100)
+ .arg((bar % 100) / 10)
+ .arg(bar % 10)
+ .arg(beat / 10)
+ .arg(beat % 10)
+ .arg(fraction / 10)
+ .arg(fraction % 10)
+ .arg(remainder / 10)
+ .arg(remainder % 10);
+ }
+
+ case 1: // real time
+ {
+ RealTime rt =
+ getDocument()->getComposition().getElapsedRealTime(time);
+ // return QString("%1 ").arg(rt.toString().c_str());
+ return QString("%1 ").arg(rt.toText().c_str());
+ }
+
+ default:
+ return QString("%1 ").arg(time);
+ }
+}
+
+QString
+EventView::makeDurationString(timeT time,
+ timeT duration, int timeMode)
+{
+ switch (timeMode) {
+
+ case 0: // musical time
+ {
+ int bar, beat, fraction, remainder;
+ getDocument()->getComposition().getMusicalTimeForDuration
+ (time, duration, bar, beat, fraction, remainder);
+ return QString("%1%2%3-%4%5-%6%7-%8%9 ")
+ .arg(bar / 100)
+ .arg((bar % 100) / 10)
+ .arg(bar % 10)
+ .arg(beat / 10)
+ .arg(beat % 10)
+ .arg(fraction / 10)
+ .arg(fraction % 10)
+ .arg(remainder / 10)
+ .arg(remainder % 10);
+ }
+
+ case 1: // real time
+ {
+ RealTime rt =
+ getDocument()->getComposition().getRealTimeDifference
+ (time, time + duration);
+ // return QString("%1 ").arg(rt.toString().c_str());
+ return QString("%1 ").arg(rt.toText().c_str());
+ }
+
+ default:
+ return QString("%1 ").arg(duration);
+ }
+}
+
+void
+EventView::refreshSegment(Segment * /*segment*/,
+ timeT /*startTime*/,
+ timeT /*endTime*/)
+{
+ RG_DEBUG << "EventView::refreshSegment" << endl;
+ applyLayout(0);
+}
+
+void
+EventView::updateView()
+{
+ m_eventList->update();
+}
+
+void
+EventView::slotEditTriggerName()
+{
+ bool ok = false;
+ QString newLabel = KLineEditDlg::getText(i18n("Segment label"), i18n("Label:"),
+ strtoqstr(m_segments[0]->getLabel()),
+ &ok, this);
+
+ if (ok) {
+ SegmentSelection selection;
+ selection.insert(m_segments[0]);
+ SegmentLabelCommand *cmd = new SegmentLabelCommand(selection, newLabel);
+ addCommandToHistory(cmd);
+ m_triggerName->setText(newLabel);
+ }
+}
+
+void
+EventView::slotEditTriggerPitch()
+{
+ int id = m_segments[0]->getComposition()->getTriggerSegmentId(m_segments[0]);
+
+ TriggerSegmentRec *rec =
+ m_segments[0]->getComposition()->getTriggerSegmentRec(id);
+
+ PitchDialog *dlg = new PitchDialog(this, i18n("Base pitch"), rec->getBasePitch());
+
+ if (dlg->exec() == QDialog::Accepted) {
+ addCommandToHistory(new SetTriggerSegmentBasePitchCommand
+ (&getDocument()->getComposition(), id, dlg->getPitch()));
+ m_triggerPitch->setText(QString("%1").arg(dlg->getPitch()));
+ }
+}
+
+void
+EventView::slotEditTriggerVelocity()
+{
+ int id = m_segments[0]->getComposition()->getTriggerSegmentId(m_segments[0]);
+
+ TriggerSegmentRec *rec =
+ m_segments[0]->getComposition()->getTriggerSegmentRec(id);
+
+ TrivialVelocityDialog *dlg = new TrivialVelocityDialog
+ (this, i18n("Base velocity"), rec->getBaseVelocity());
+
+ if (dlg->exec() == QDialog::Accepted) {
+ addCommandToHistory(new SetTriggerSegmentBaseVelocityCommand
+ (&getDocument()->getComposition(), id, dlg->getVelocity()));
+ m_triggerVelocity->setText(QString("%1").arg(dlg->getVelocity()));
+ }
+}
+
+void
+EventView::slotTriggerTimeAdjustChanged(int option)
+{
+ std::string adjust = BaseProperties::TRIGGER_SEGMENT_ADJUST_SQUISH;
+
+ switch (option) {
+
+ case 0:
+ adjust = BaseProperties::TRIGGER_SEGMENT_ADJUST_NONE;
+ break;
+ case 1:
+ adjust = BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_START;
+ break;
+ case 2:
+ adjust = BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_END;
+ break;
+ case 3:
+ adjust = BaseProperties::TRIGGER_SEGMENT_ADJUST_SQUISH;
+ break;
+
+ default:
+ break;
+ }
+
+ int id = m_segments[0]->getComposition()->getTriggerSegmentId(m_segments[0]);
+
+ TriggerSegmentRec *rec =
+ m_segments[0]->getComposition()->getTriggerSegmentRec(id);
+
+ addCommandToHistory(new SetTriggerSegmentDefaultTimeAdjustCommand
+ (&getDocument()->getComposition(), id, adjust));
+}
+
+void
+EventView::slotTriggerRetuneChanged()
+{
+ int id = m_segments[0]->getComposition()->getTriggerSegmentId(m_segments[0]);
+
+ TriggerSegmentRec *rec =
+ m_segments[0]->getComposition()->getTriggerSegmentRec(id);
+
+ addCommandToHistory(new SetTriggerSegmentDefaultRetuneCommand
+ (&getDocument()->getComposition(), id, !rec->getDefaultRetune()));
+}
+
+void
+EventView::slotEditCut()
+{
+ QPtrList<QListViewItem> selection = m_eventList->selectedItems();
+
+ if (selection.count() == 0)
+ return ;
+
+ RG_DEBUG << "EventView::slotEditCut - cutting "
+ << selection.count() << " items" << endl;
+
+ QPtrListIterator<QListViewItem> it(selection);
+ QListViewItem *listItem;
+ EventViewItem *item;
+ EventSelection *cutSelection = 0;
+ int itemIndex = -1;
+
+ while ((listItem = it.current()) != 0) {
+ item = dynamic_cast<EventViewItem*>((*it));
+
+ if (itemIndex == -1)
+ itemIndex = m_eventList->itemIndex(*it);
+
+ if (item) {
+ if (cutSelection == 0)
+ cutSelection =
+ new EventSelection(*(item->getSegment()));
+
+ cutSelection->addEvent(item->getEvent());
+ }
+ ++it;
+ }
+
+ if (cutSelection) {
+ if (itemIndex >= 0) {
+ m_listSelection.clear();
+ m_listSelection.push_back(itemIndex);
+ }
+
+ addCommandToHistory(new CutCommand(*cutSelection,
+ getDocument()->getClipboard()));
+ }
+}
+
+void
+EventView::slotEditCopy()
+{
+ QPtrList<QListViewItem> selection = m_eventList->selectedItems();
+
+ if (selection.count() == 0)
+ return ;
+
+ RG_DEBUG << "EventView::slotEditCopy - copying "
+ << selection.count() << " items" << endl;
+
+ QPtrListIterator<QListViewItem> it(selection);
+ QListViewItem *listItem;
+ EventViewItem *item;
+ EventSelection *copySelection = 0;
+
+ // clear the selection for post modification updating
+ //
+ m_listSelection.clear();
+
+ while ((listItem = it.current()) != 0) {
+ item = dynamic_cast<EventViewItem*>((*it));
+
+ m_listSelection.push_back(m_eventList->itemIndex(*it));
+
+ if (item) {
+ if (copySelection == 0)
+ copySelection =
+ new EventSelection(*(item->getSegment()));
+
+ copySelection->addEvent(item->getEvent());
+ }
+ ++it;
+ }
+
+ if (copySelection) {
+ addCommandToHistory(new CopyCommand(*copySelection,
+ getDocument()->getClipboard()));
+ }
+}
+
+void
+EventView::slotEditPaste()
+{
+ if (getDocument()->getClipboard()->isEmpty()) {
+ slotStatusHelpMsg(i18n("Clipboard is empty"));
+ return ;
+ }
+
+ KTmpStatusMsg msg(i18n("Inserting clipboard contents..."), this);
+
+ timeT insertionTime = 0;
+
+ QPtrList<QListViewItem> selection = m_eventList->selectedItems();
+ if (selection.count()) {
+ EventViewItem *item = dynamic_cast<EventViewItem*>(selection.at(0));
+
+ if (item)
+ insertionTime = item->getEvent()->getAbsoluteTime();
+
+ // remember the selection
+ //
+ m_listSelection.clear();
+
+ QPtrListIterator<QListViewItem> it(selection);
+ QListViewItem *listItem;
+
+ while ((listItem = it.current()) != 0) {
+ m_listSelection.push_back(m_eventList->itemIndex(*it));
+ ++it;
+ }
+ }
+
+
+ PasteEventsCommand *command = new PasteEventsCommand
+ (*m_segments[0], getDocument()->getClipboard(),
+ insertionTime, PasteEventsCommand::MatrixOverlay);
+
+ if (!command->isPossible()) {
+ slotStatusHelpMsg(i18n("Couldn't paste at this point"));
+ } else
+ addCommandToHistory(command);
+
+ RG_DEBUG << "EventView::slotEditPaste - pasting "
+ << selection.count() << " items" << endl;
+}
+
+void
+EventView::slotEditDelete()
+{
+ QPtrList<QListViewItem> selection = m_eventList->selectedItems();
+ if (selection.count() == 0)
+ return ;
+
+ RG_DEBUG << "EventView::slotEditDelete - deleting "
+ << selection.count() << " items" << endl;
+
+ QPtrListIterator<QListViewItem> it(selection);
+ QListViewItem *listItem;
+ EventViewItem *item;
+ EventSelection *deleteSelection = 0;
+ int itemIndex = -1;
+
+ while ((listItem = it.current()) != 0) {
+ item = dynamic_cast<EventViewItem*>((*it));
+
+ if (itemIndex == -1)
+ itemIndex = m_eventList->itemIndex(*it);
+
+ if (item) {
+ if (m_deletedEvents.find(item->getEvent()) != m_deletedEvents.end()) {
+ ++it;
+ continue;
+ }
+
+ if (deleteSelection == 0)
+ deleteSelection =
+ new EventSelection(*m_segments[0]);
+
+ deleteSelection->addEvent(item->getEvent());
+ }
+ ++it;
+ }
+
+ if (deleteSelection) {
+
+ if (itemIndex >= 0) {
+ m_listSelection.clear();
+ m_listSelection.push_back(itemIndex);
+ }
+
+ addCommandToHistory(new EraseCommand(*deleteSelection));
+
+ }
+}
+
+void
+EventView::slotEditInsert()
+{
+ RG_DEBUG << "EventView::slotEditInsert" << endl;
+
+ timeT insertTime = m_segments[0]->getStartTime();
+ timeT insertDuration = 960;
+
+ QPtrList<QListViewItem> selection = m_eventList->selectedItems();
+
+ if (selection.count() > 0) {
+ EventViewItem *item =
+ dynamic_cast<EventViewItem*>(selection.getFirst());
+
+ if (item) {
+ insertTime = item->getEvent()->getAbsoluteTime();
+ insertDuration = item->getEvent()->getDuration();
+ }
+ }
+
+ // Create default event
+ //
+ Event *event =
+ new Event(Note::EventType,
+ insertTime,
+ insertDuration);
+ event->set
+ <Int>(BaseProperties::PITCH, 70);
+ event->set
+ <Int>(BaseProperties::VELOCITY, 100);
+
+ SimpleEventEditDialog dialog(this, getDocument(), *event, true);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ EventInsertionCommand *command =
+ new EventInsertionCommand(*m_segments[0],
+ new Event(dialog.getEvent()));
+ addCommandToHistory(command);
+ }
+}
+
+void
+EventView::slotEditEvent()
+{
+ RG_DEBUG << "EventView::slotEditEvent" << endl;
+
+ QPtrList<QListViewItem> selection = m_eventList->selectedItems();
+
+ if (selection.count() > 0) {
+ EventViewItem *item =
+ dynamic_cast<EventViewItem*>(selection.getFirst());
+
+ if (item) {
+ Event *event = item->getEvent();
+ SimpleEventEditDialog dialog(this, getDocument(), *event, false);
+
+ if (dialog.exec() == QDialog::Accepted && dialog.isModified()) {
+ EventEditCommand *command =
+ new EventEditCommand(*(item->getSegment()),
+ event,
+ dialog.getEvent());
+
+ addCommandToHistory(command);
+ }
+ }
+ }
+}
+
+void
+EventView::slotEditEventAdvanced()
+{
+ RG_DEBUG << "EventView::slotEditEventAdvanced" << endl;
+
+ QPtrList<QListViewItem> selection = m_eventList->selectedItems();
+
+ if (selection.count() > 0) {
+ EventViewItem *item =
+ dynamic_cast<EventViewItem*>(selection.getFirst());
+
+ if (item) {
+ Event *event = item->getEvent();
+ EventEditDialog dialog(this, *event);
+
+ if (dialog.exec() == QDialog::Accepted && dialog.isModified()) {
+ EventEditCommand *command =
+ new EventEditCommand(*(item->getSegment()),
+ event,
+ dialog.getEvent());
+
+ addCommandToHistory(command);
+ }
+ }
+ }
+}
+
+void
+EventView::slotSelectAll()
+{
+ m_listSelection.clear();
+ for (int i = 0; m_eventList->itemAtIndex(i); ++i) {
+ m_listSelection.push_back(i);
+ m_eventList->setSelected(m_eventList->itemAtIndex(i), true);
+ }
+}
+
+void
+EventView::slotClearSelection()
+{
+ m_listSelection.clear();
+ for (int i = 0; m_eventList->itemAtIndex(i); ++i) {
+ m_eventList->setSelected(m_eventList->itemAtIndex(i), false);
+ }
+}
+
+void
+EventView::slotFilterSelection()
+{
+ m_listSelection.clear();
+ QPtrList<QListViewItem> selection = m_eventList->selectedItems();
+ if (selection.count() == 0)
+ return ;
+
+ EventFilterDialog dialog(this);
+ if (dialog.exec() == QDialog::Accepted) {
+
+ QPtrListIterator<QListViewItem> it(selection);
+ QListViewItem *listItem;
+
+ while ((listItem = it.current()) != 0) {
+
+ EventViewItem * item = dynamic_cast<EventViewItem*>(*it);
+ if (!item) {
+ ++it;
+ continue;
+ }
+
+ if (!dialog.keepEvent(item->getEvent())) {
+ m_listSelection.push_back(m_eventList->itemIndex(*it));
+ m_eventList->setSelected(item, false);
+ }
+
+ ++it;
+ }
+ }
+}
+
+void
+EventView::setupActions()
+{
+ EditViewBase::setupActions("eventlist.rc");
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QIconSet icon(QPixmap(pixmapDir + "/toolbar/event-insert.png"));
+
+ new KAction(i18n("&Insert Event"), icon, Key_I, this,
+ SLOT(slotEditInsert()), actionCollection(),
+ "insert");
+
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/event-delete.png");
+ icon = QIconSet(pixmap);
+
+ new KAction(i18n("&Delete Event"), icon, Key_Delete, this,
+ SLOT(slotEditDelete()), actionCollection(),
+ "delete");
+
+ pixmap.load(pixmapDir + "/toolbar/event-edit.png");
+ icon = QIconSet(pixmap);
+
+ new KAction(i18n("&Edit Event"), icon, Key_E, this,
+ SLOT(slotEditEvent()), actionCollection(),
+ "edit_simple");
+
+ pixmap.load(pixmapDir + "/toolbar/event-edit-advanced.png");
+ icon = QIconSet(pixmap);
+
+ new KAction(i18n("&Advanced Event Editor"), icon, Key_A, this,
+ SLOT(slotEditEventAdvanced()), actionCollection(),
+ "edit_advanced");
+
+ // icon = QIconSet(QCanvasPixmap(pixmapDir + "/toolbar/eventfilter.xpm"));
+ new KAction(i18n("&Filter Selection"), "filter", Key_F, this,
+ SLOT(slotFilterSelection()), actionCollection(),
+ "filter_selection");
+
+ new KAction(i18n("Select &All"), Key_A + CTRL, this,
+ SLOT(slotSelectAll()), actionCollection(),
+ "select_all");
+
+ new KAction(i18n("Clear Selection"), Key_Escape, this,
+ SLOT(slotClearSelection()), actionCollection(),
+ "clear_selection");
+
+ m_config->setGroup(EventViewConfigGroup);
+ int timeMode = m_config->readNumEntry("timemode", 0);
+
+ KRadioAction *action;
+
+ pixmap.load(pixmapDir + "/toolbar/time-musical.png");
+ icon = QIconSet(pixmap);
+
+ action = new KRadioAction(i18n("&Musical Times"), icon, 0, this,
+ SLOT(slotMusicalTime()),
+ actionCollection(), "time_musical");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 0)
+ action->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/time-real.png");
+ icon = QIconSet(pixmap);
+
+ action = new KRadioAction(i18n("&Real Times"), icon, 0, this,
+ SLOT(slotRealTime()),
+ actionCollection(), "time_real");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 1)
+ action->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/time-raw.png");
+ icon = QIconSet(pixmap);
+
+ action = new KRadioAction(i18n("Ra&w Times"), icon, 0, this,
+ SLOT(slotRawTime()),
+ actionCollection(), "time_raw");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 2)
+ action->setChecked(true);
+
+ if (m_isTriggerSegment) {
+ KAction *action = actionCollection()->action("open_in_matrix");
+ if (action)
+ delete action;
+ action = actionCollection()->action("open_in_notation");
+ if (action)
+ delete action;
+ }
+
+ createGUI(getRCFileName());
+}
+
+void
+EventView::initStatusBar()
+{
+ KStatusBar* sb = statusBar();
+
+ /*
+ m_hoveredOverNoteName = new QLabel(sb);
+ m_hoveredOverAbsoluteTime = new QLabel(sb);
+
+ m_hoveredOverNoteName->setMinimumWidth(32);
+ m_hoveredOverAbsoluteTime->setMinimumWidth(160);
+
+ sb->addWidget(m_hoveredOverAbsoluteTime);
+ sb->addWidget(m_hoveredOverNoteName);
+ */
+
+ sb->insertItem(KTmpStatusMsg::getDefaultMsg(),
+ KTmpStatusMsg::getDefaultId(), 1);
+ sb->setItemAlignment(KTmpStatusMsg::getDefaultId(),
+ AlignLeft | AlignVCenter);
+
+ //m_selectionCounter = new QLabel(sb);
+ //sb->addWidget(m_selectionCounter);
+}
+
+QSize
+EventView::getViewSize()
+{
+ return m_eventList->size();
+}
+
+void
+EventView::setViewSize(QSize s)
+{
+ m_eventList->setFixedSize(s);
+}
+
+void
+EventView::readOptions()
+{
+ m_config->setGroup(EventViewConfigGroup);
+ EditViewBase::readOptions();
+ m_eventFilter = m_config->readNumEntry("eventfilter", m_eventFilter);
+ m_eventList->restoreLayout(m_config, EventViewLayoutConfigGroupName);
+}
+
+void
+EventView::slotSaveOptions()
+{
+ m_config->setGroup(EventViewConfigGroup);
+ m_config->writeEntry("eventfilter", m_eventFilter);
+ m_eventList->saveLayout(m_config, EventViewLayoutConfigGroupName);
+}
+
+Segment *
+EventView::getCurrentSegment()
+{
+ if (m_segments.empty())
+ return 0;
+ else
+ return *m_segments.begin();
+}
+
+void
+EventView::slotModifyFilter(int button)
+{
+ QCheckBox *checkBox = dynamic_cast<QCheckBox*>(m_filterGroup->find(button));
+
+ if (checkBox == 0)
+ return ;
+
+ if (checkBox->isChecked()) {
+ switch (button) {
+ case 0:
+ m_eventFilter |= EventView::Note;
+ break;
+
+ case 1:
+ m_eventFilter |= EventView::ProgramChange;
+ break;
+
+ case 2:
+ m_eventFilter |= EventView::Controller;
+ break;
+
+ case 3:
+ m_eventFilter |= EventView::PitchBend;
+ break;
+
+ case 4:
+ m_eventFilter |= EventView::SystemExclusive;
+ break;
+
+ case 5:
+ m_eventFilter |= EventView::KeyPressure;
+ break;
+
+ case 6:
+ m_eventFilter |= EventView::ChannelPressure;
+ break;
+
+ case 7:
+ m_eventFilter |= EventView::Rest;
+ break;
+
+ case 8:
+ m_eventFilter |= EventView::Indication;
+ break;
+
+ case 9:
+ m_eventFilter |= EventView::Text;
+ break;
+
+ case 10:
+ m_eventFilter |= EventView::Other;
+ break;
+
+ default:
+ break;
+ }
+
+ } else {
+ switch (button) {
+ case 0:
+ m_eventFilter ^= EventView::Note;
+ break;
+
+ case 1:
+ m_eventFilter ^= EventView::ProgramChange;
+ break;
+
+ case 2:
+ m_eventFilter ^= EventView::Controller;
+ break;
+
+ case 3:
+ m_eventFilter ^= EventView::PitchBend;
+ break;
+
+ case 4:
+ m_eventFilter ^= EventView::SystemExclusive;
+ break;
+
+ case 5:
+ m_eventFilter ^= EventView::KeyPressure;
+ break;
+
+ case 6:
+ m_eventFilter ^= EventView::ChannelPressure;
+ break;
+
+ case 7:
+ m_eventFilter ^= EventView::Rest;
+ break;
+
+ case 8:
+ m_eventFilter ^= EventView::Indication;
+ break;
+
+ case 9:
+ m_eventFilter ^= EventView::Text;
+ break;
+
+ case 10:
+ m_eventFilter ^= EventView::Other;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ m_lastSetEventFilter = m_eventFilter;
+
+ applyLayout(0);
+}
+
+void
+EventView::setButtonsToFilter()
+{
+ if (m_eventFilter & Note)
+ m_noteCheckBox->setChecked(true);
+ else
+ m_noteCheckBox->setChecked(false);
+
+ if (m_eventFilter & ProgramChange)
+ m_programCheckBox->setChecked(true);
+ else
+ m_programCheckBox->setChecked(false);
+
+ if (m_eventFilter & Controller)
+ m_controllerCheckBox->setChecked(true);
+ else
+ m_controllerCheckBox->setChecked(false);
+
+ if (m_eventFilter & SystemExclusive)
+ m_sysExCheckBox->setChecked(true);
+ else
+ m_sysExCheckBox->setChecked(false);
+
+ if (m_eventFilter & Text)
+ m_textCheckBox->setChecked(true);
+ else
+ m_textCheckBox->setChecked(false);
+
+ if (m_eventFilter & Rest)
+ m_restCheckBox->setChecked(true);
+ else
+ m_restCheckBox->setChecked(false);
+
+ if (m_eventFilter & PitchBend)
+ m_pitchBendCheckBox->setChecked(true);
+ else
+ m_pitchBendCheckBox->setChecked(false);
+
+ if (m_eventFilter & ChannelPressure)
+ m_channelPressureCheckBox->setChecked(true);
+ else
+ m_channelPressureCheckBox->setChecked(false);
+
+ if (m_eventFilter & KeyPressure)
+ m_keyPressureCheckBox->setChecked(true);
+ else
+ m_keyPressureCheckBox->setChecked(false);
+
+ if (m_eventFilter & Indication) {
+ m_indicationCheckBox->setChecked(true);
+ } else {
+ m_indicationCheckBox->setChecked(false);
+ }
+
+ if (m_eventFilter & Other) {
+ m_otherCheckBox->setChecked(true);
+ } else {
+ m_otherCheckBox->setChecked(false);
+ }
+}
+
+void
+EventView::slotMusicalTime()
+{
+ m_config->setGroup(EventViewConfigGroup);
+ m_config->writeEntry("timemode", 0);
+ applyLayout();
+}
+
+void
+EventView::slotRealTime()
+{
+ m_config->setGroup(EventViewConfigGroup);
+ m_config->writeEntry("timemode", 1);
+ applyLayout();
+}
+
+void
+EventView::slotRawTime()
+{
+ m_config->setGroup(EventViewConfigGroup);
+ m_config->writeEntry("timemode", 2);
+ applyLayout();
+}
+
+void
+EventView::slotPopupEventEditor(QListViewItem *item)
+{
+ EventViewItem *eItem = dynamic_cast<EventViewItem*>(item);
+
+ //!!! trigger events
+
+ if (eItem) {
+ Event *event = eItem->getEvent();
+ SimpleEventEditDialog *dialog =
+ new SimpleEventEditDialog(this, getDocument(), *event, false);
+
+ if (dialog->exec() == QDialog::Accepted && dialog->isModified()) {
+ EventEditCommand *command =
+ new EventEditCommand(*(eItem->getSegment()),
+ event,
+ dialog->getEvent());
+
+ addCommandToHistory(command);
+ }
+
+ }
+}
+
+void
+EventView::slotPopupMenu(QListViewItem *item, const QPoint &pos, int)
+{
+ if (!item)
+ return ;
+
+ EventViewItem *eItem = dynamic_cast<EventViewItem*>(item);
+ if (!eItem || !eItem->getEvent())
+ return ;
+
+ if (!m_menu)
+ createMenu();
+
+ if (m_menu)
+ //m_menu->exec(QCursor::pos());
+ m_menu->exec(pos);
+ else
+ RG_DEBUG << "EventView::showMenu() : no menu to show\n";
+}
+
+void
+EventView::createMenu()
+{
+ m_menu = new QPopupMenu(this);
+ m_menu->insertItem(i18n("Open in Event Editor"), 0);
+ m_menu->insertItem(i18n("Open in Expert Event Editor"), 1);
+
+ connect(m_menu, SIGNAL(activated(int)),
+ SLOT(slotMenuActivated(int)));
+}
+
+void
+EventView::slotMenuActivated(int value)
+{
+ RG_DEBUG << "EventView::slotMenuActivated - value = " << value << endl;
+
+ if (value == 0) {
+ EventViewItem *eItem = dynamic_cast<EventViewItem*>
+ (m_eventList->currentItem());
+
+ if (eItem) {
+ Event *event = eItem->getEvent();
+ SimpleEventEditDialog *dialog =
+ new SimpleEventEditDialog(this, getDocument(), *event, false);
+
+ if (dialog->exec() == QDialog::Accepted && dialog->isModified()) {
+ EventEditCommand *command =
+ new EventEditCommand(*(eItem->getSegment()),
+ event,
+ dialog->getEvent());
+
+ addCommandToHistory(command);
+ }
+
+ }
+ } else if (value == 1) {
+ EventViewItem *eItem = dynamic_cast<EventViewItem*>
+ (m_eventList->currentItem());
+
+ if (eItem) {
+ Event *event = eItem->getEvent();
+ EventEditDialog *dialog = new EventEditDialog(this, *event);
+
+ if (dialog->exec() == QDialog::Accepted && dialog->isModified()) {
+ EventEditCommand *command =
+ new EventEditCommand(*(eItem->getSegment()),
+ event,
+ dialog->getEvent());
+
+ addCommandToHistory(command);
+ }
+
+ }
+ }
+
+ return ;
+}
+
+void
+EventView::updateViewCaption()
+{
+ if (m_isTriggerSegment) {
+
+ setCaption(i18n("%1 - Triggered Segment: %2")
+ .arg(getDocument()->getTitle())
+ .arg(strtoqstr(m_segments[0]->getLabel())));
+
+
+ } else if (m_segments.size() == 1) {
+
+ TrackId trackId = m_segments[0]->getTrack();
+ Track *track =
+ m_segments[0]->getComposition()->getTrackById(trackId);
+
+ int trackPosition = -1;
+ if (track)
+ trackPosition = track->getPosition();
+
+ setCaption(i18n("%1 - Segment Track #%2 - Event List")
+ .arg(getDocument()->getTitle())
+ .arg(trackPosition + 1));
+
+ } else {
+
+ setCaption(i18n("%1 - %2 Segments - Event List")
+ .arg(getDocument()->getTitle())
+ .arg(m_segments.size()));
+ }
+
+}
+
+}
+#include "EventView.moc"
diff --git a/src/gui/editors/eventlist/EventView.h b/src/gui/editors/eventlist/EventView.h
new file mode 100644
index 0000000..4c540e6
--- /dev/null
+++ b/src/gui/editors/eventlist/EventView.h
@@ -0,0 +1,205 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_EVENTVIEW_H_
+#define _RG_EVENTVIEW_H_
+
+#include "base/MidiTypes.h"
+#include "base/NotationTypes.h"
+#include "base/Segment.h"
+#include "gui/general/EditViewBase.h"
+#include <set>
+#include <qsize.h>
+#include <qstring.h>
+#include <vector>
+#include "base/Event.h"
+
+
+class QWidget;
+class QPopupMenu;
+class QPoint;
+class QListViewItem;
+class QLabel;
+class QCheckBox;
+class QButtonGroup;
+class KListView;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RosegardenGUIDoc;
+class Event;
+
+
+class EventView : public EditViewBase, public SegmentObserver
+{
+ Q_OBJECT
+
+ // Event filters
+ //
+ enum EventFilter
+ {
+ None = 0x0000,
+ Note = 0x0001,
+ Rest = 0x0002,
+ Text = 0x0004,
+ SystemExclusive = 0x0008,
+ Controller = 0x0010,
+ ProgramChange = 0x0020,
+ PitchBend = 0x0040,
+ ChannelPressure = 0x0080,
+ KeyPressure = 0x0100,
+ Indication = 0x0200,
+ Other = 0x0400
+ };
+
+public:
+ EventView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ QWidget *parent);
+
+ virtual ~EventView();
+
+ virtual bool applyLayout(int staffNo = -1);
+
+ virtual void refreshSegment(Segment *segment,
+ timeT startTime = 0,
+ timeT endTime = 0);
+
+ virtual void updateView();
+
+ virtual void setupActions();
+ virtual void initStatusBar();
+ virtual QSize getViewSize();
+ virtual void setViewSize(QSize);
+
+ // Set the button states to the current filter positions
+ //
+ void setButtonsToFilter();
+
+ // Menu creation and show
+ //
+ void createMenu();
+
+public slots:
+
+ // standard slots
+ virtual void slotEditCut();
+ virtual void slotEditCopy();
+ virtual void slotEditPaste();
+
+ // other edit slots
+ void slotEditDelete();
+ void slotEditInsert();
+ void slotEditEvent();
+ void slotEditEventAdvanced();
+
+ void slotFilterSelection();
+ void slotSelectAll();
+ void slotClearSelection();
+
+ void slotMusicalTime();
+ void slotRealTime();
+ void slotRawTime();
+
+ // Show RMB menu
+ //
+ void slotPopupMenu(QListViewItem*, const QPoint&, int);
+ void slotMenuActivated(int);
+
+ // on double click on the event list
+ //
+ void slotPopupEventEditor(QListViewItem*);
+
+ // Change filter parameters
+ //
+ void slotModifyFilter(int);
+
+ virtual void eventAdded(const Segment *, Event *) { }
+ virtual void eventRemoved(const Segment *, Event *);
+ virtual void endMarkerTimeChanged(const Segment *, bool) { }
+ virtual void segmentDeleted(const Segment *);
+
+signals:
+ void editTriggerSegment(int);
+
+protected slots:
+ virtual void slotSaveOptions();
+
+ void slotEditTriggerName();
+ void slotEditTriggerPitch();
+ void slotEditTriggerVelocity();
+ void slotTriggerTimeAdjustChanged(int);
+ void slotTriggerRetuneChanged();
+
+protected:
+
+ virtual void readOptions();
+ void makeInitialSelection(timeT);
+ QString makeTimeString(timeT time, int timeMode);
+ QString makeDurationString(timeT time,
+ timeT duration, int timeMode);
+ virtual Segment *getCurrentSegment();
+
+ virtual void updateViewCaption();
+
+ //--------------- Data members ---------------------------------
+
+ bool m_isTriggerSegment;
+ QLabel *m_triggerName;
+ QLabel *m_triggerPitch;
+ QLabel *m_triggerVelocity;
+
+ KListView *m_eventList;
+ int m_eventFilter;
+
+ static int m_lastSetEventFilter;
+
+ QButtonGroup *m_filterGroup;
+ QCheckBox *m_noteCheckBox;
+ QCheckBox *m_textCheckBox;
+ QCheckBox *m_sysExCheckBox;
+ QCheckBox *m_programCheckBox;
+ QCheckBox *m_controllerCheckBox;
+ QCheckBox *m_restCheckBox;
+ QCheckBox *m_pitchBendCheckBox;
+ QCheckBox *m_keyPressureCheckBox;
+ QCheckBox *m_channelPressureCheckBox;
+ QCheckBox *m_indicationCheckBox;
+ QCheckBox *m_otherCheckBox;
+
+ std::vector<int> m_listSelection;
+ std::set<Event *> m_deletedEvents; // deleted since last refresh
+
+ QPopupMenu *m_menu;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/eventlist/EventViewItem.cpp b/src/gui/editors/eventlist/EventViewItem.cpp
new file mode 100644
index 0000000..4435a2b
--- /dev/null
+++ b/src/gui/editors/eventlist/EventViewItem.cpp
@@ -0,0 +1,68 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "EventViewItem.h"
+#include "base/Event.h"
+
+namespace Rosegarden
+{
+
+// Reimplementation of sort for numeric columns - taking the
+// right hand argument from the left is equivalent to the
+// the QString compare().
+//
+int
+EventViewItem::compare(QListViewItem *i, int col, bool ascending) const
+{
+ EventViewItem *ei = dynamic_cast<EventViewItem *>(i);
+ if (!ei) return QListViewItem::compare(i, col, ascending);
+
+ if (col == 0) { // time
+ Rosegarden::Event &e1 = *m_event;
+ Rosegarden::Event &e2 = *ei->m_event;
+ if (e2 < e1) return 1;
+ else if (e1 < e2) return -1;
+ else return 0;
+ } else if (col == 2 || col == 5 || col == 6) { // event type, data1, data2
+ // we have to do string compares even for data1/data2 which are
+ // often numeric, just because they aren't _always_ numeric and
+ // we don't want to prevent the user being able to separate
+ // e.g. crescendo from decrescendo
+ if (key(col, ascending).compare(i->key(col, ascending)) == 0) {
+ return compare(i, 0, ascending);
+ } else {
+ return key(col, ascending).compare(i->key(col, ascending));
+ }
+ } else if (col == 3) { // pitch
+ // numeric comparison for pitch used to work when we only
+ // showed the numeric pitch number, but then we added the MIDI
+ // pitch name as well and that broke plain numeric comparison
+ return key(col, ascending).section(' ', 0, 0).toInt() -
+ i->key(col, ascending).section(' ', 0, 0).toInt();
+ } else { // numeric comparison
+ return key(col, ascending).toInt() - i->key(col, ascending).toInt();
+ }
+}
+
+}
diff --git a/src/gui/editors/eventlist/EventViewItem.h b/src/gui/editors/eventlist/EventViewItem.h
new file mode 100644
index 0000000..832e652
--- /dev/null
+++ b/src/gui/editors/eventlist/EventViewItem.h
@@ -0,0 +1,101 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_EVENTVIEWITEM_H_
+#define _RG_EVENTVIEWITEM_H_
+
+#include <klistview.h>
+
+namespace Rosegarden
+{
+
+class Segment;
+class Event;
+
+// EventView specialisation of a QListViewItem with the
+// addition of a segment pointer
+//
+class EventViewItem : public KListViewItem
+{
+public:
+ EventViewItem(Rosegarden::Segment *segment,
+ Rosegarden::Event *event,
+ KListView *parent) :
+ KListViewItem(parent),
+ m_segment(segment),
+ m_event(event) {;}
+
+ EventViewItem(Rosegarden::Segment *segment,
+ Rosegarden::Event *event,
+ KListViewItem *parent) :
+ KListViewItem(parent),
+ m_segment(segment),
+ m_event(event) {;}
+
+ EventViewItem(Rosegarden::Segment *segment,
+ Rosegarden::Event *event,
+ QListView *parent, QString label1,
+ QString label2 = QString::null,
+ QString label3 = QString::null,
+ QString label4 = QString::null,
+ QString label5 = QString::null,
+ QString label6 = QString::null,
+ QString label7 = QString::null,
+ QString label8 = QString::null) :
+ KListViewItem(parent, label1, label2, label3, label4,
+ label5, label6, label7, label8),
+ m_segment(segment),
+ m_event(event) {;}
+
+ EventViewItem(Rosegarden::Segment *segment,
+ Rosegarden::Event *event,
+ KListViewItem *parent, QString label1,
+ QString label2 = QString::null,
+ QString label3 = QString::null,
+ QString label4 = QString::null,
+ QString label5 = QString::null,
+ QString label6 = QString::null,
+ QString label7 = QString::null,
+ QString label8 = QString::null) :
+ KListViewItem(parent, label1, label2, label3, label4,
+ label5, label6, label7, label8),
+ m_segment(segment),
+ m_event(event) {;}
+
+ Rosegarden::Segment* getSegment() { return m_segment; }
+ Rosegarden::Event* getEvent() { return m_event; }
+
+ // Reimplement so that we can sort numerically
+ //
+ virtual int compare(QListViewItem *i, int col, bool ascending) const;
+
+protected:
+
+ Rosegarden::Segment *m_segment;
+ Rosegarden::Event *m_event;
+};
+
+}
+
+#endif /*EVENTVIEWITEM_H_*/
diff --git a/src/gui/editors/eventlist/TrivialVelocityDialog.cpp b/src/gui/editors/eventlist/TrivialVelocityDialog.cpp
new file mode 100644
index 0000000..4e609d4
--- /dev/null
+++ b/src/gui/editors/eventlist/TrivialVelocityDialog.cpp
@@ -0,0 +1,48 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "TrivialVelocityDialog.h"
+
+#include <qspinbox.h>
+#include <qlabel.h>
+#include <qhbox.h>
+
+namespace Rosegarden {
+
+TrivialVelocityDialog::TrivialVelocityDialog(QWidget *parent, QString label, int deft) :
+ KDialogBase(parent, 0, true, label, Ok | Cancel)
+ {
+ QHBox *hbox = makeHBoxMainWidget();
+ new QLabel(label, hbox);
+ m_spin = new QSpinBox(0, 127, 1, hbox);
+ m_spin->setValue(deft);
+ }
+
+int
+TrivialVelocityDialog::getVelocity()
+{
+ return m_spin->value();
+}
+
+}
diff --git a/src/gui/editors/eventlist/TrivialVelocityDialog.h b/src/gui/editors/eventlist/TrivialVelocityDialog.h
new file mode 100644
index 0000000..ca19de9
--- /dev/null
+++ b/src/gui/editors/eventlist/TrivialVelocityDialog.h
@@ -0,0 +1,48 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRIVIALVELOCITYDIALOG_H_
+#define _RG_TRIVIALVELOCITYDIALOG_H_
+
+#include <kdialogbase.h>
+
+class QHBox;
+class QSpinBox;
+
+namespace Rosegarden {
+
+class TrivialVelocityDialog : public KDialogBase
+{
+public:
+ TrivialVelocityDialog(QWidget *parent, QString label, int deft);
+
+ int getVelocity();
+
+protected:
+ QSpinBox *m_spin;
+};
+
+}
+
+#endif /*TRIVIALVELOCITYDIALOG_H_*/
diff --git a/src/gui/editors/guitar/Chord.cpp b/src/gui/editors/guitar/Chord.cpp
new file mode 100644
index 0000000..23efe7d
--- /dev/null
+++ b/src/gui/editors/guitar/Chord.cpp
@@ -0,0 +1,113 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "Chord.h"
+#include "base/Event.h"
+
+#include <qstring.h>
+
+namespace Rosegarden
+{
+
+namespace Guitar
+{
+const std::string Chord::EventType = "guitarchord";
+const short Chord::EventSubOrdering = -60;
+const PropertyName Chord::RootPropertyName = "root";
+const PropertyName Chord::ExtPropertyName = "ext";
+const PropertyName Chord::FingeringPropertyName = "fingering";
+
+
+Chord::Chord()
+ : m_isUserChord(false)
+{
+}
+
+Chord::Chord(const QString& root, const QString& ext)
+ : m_root(root),
+ m_ext(ext),
+ m_isUserChord(false)
+{
+ if (m_ext.isEmpty())
+ m_ext = QString::null;
+}
+
+Chord::Chord(const Event& e)
+ : m_isUserChord(false)
+{
+ std::string f;
+ bool ok;
+
+ ok = e.get<String>(RootPropertyName, f);
+ if (ok)
+ m_root = f;
+
+ ok = e.get<String>(ExtPropertyName, f);
+ if (ok) {
+ if (f.length() == 0)
+ m_ext = QString::null;
+ else
+ m_ext = f;
+ }
+
+ ok = e.get<String>(FingeringPropertyName, f);
+ if (ok) {
+ QString qf(f);
+ QString errString;
+
+ Fingering fingering = Fingering::parseFingering(qf, errString);
+ setFingering(fingering);
+ }
+}
+
+Event* Chord::getAsEvent(timeT absoluteTime) const
+{
+ Event *e = new Event(EventType, absoluteTime, 0, EventSubOrdering);
+ e->set<String>(RootPropertyName, m_root);
+ e->set<String>(ExtPropertyName, m_ext);
+ e->set<String>(FingeringPropertyName, getFingering().toString());
+ return e;
+}
+
+const QRegExp Chord::ALT_BASS_REGEXP("/[A-G]");
+
+bool operator<(const Chord& a, const Chord& b)
+{
+ if (a.m_root != b.m_root) {
+ return a.m_root < b.m_root;
+ } else if (a.m_ext != b.m_ext) {
+ if (a.m_ext.isEmpty()) // chords with no ext need to be stored first
+ return true;
+ if (b.m_ext.isEmpty())
+ return false;
+ return a.m_ext < b.m_ext;
+ } else {
+ return a.m_fingering < b.m_fingering;
+ }
+
+}
+
+}
+
+}
diff --git a/src/gui/editors/guitar/Chord.h b/src/gui/editors/guitar/Chord.h
new file mode 100644
index 0000000..9e84cc3
--- /dev/null
+++ b/src/gui/editors/guitar/Chord.h
@@ -0,0 +1,106 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_CHORD_H_
+#define _RG_CHORD_H_
+
+#include "Fingering.h"
+#include "base/Event.h"
+#include "misc/Debug.h"
+
+#include <vector>
+#include <qstring.h>
+#include <qregexp.h>
+
+namespace Rosegarden
+{
+
+class Event;
+
+namespace Guitar
+{
+
+class Chord
+{
+ friend bool operator<(const Chord&, const Chord&);
+
+public:
+ static const std::string EventType;
+ static const short EventSubOrdering;
+ static const PropertyName RootPropertyName;
+ static const PropertyName ExtPropertyName;
+ static const PropertyName FingeringPropertyName;
+
+ Chord();
+ Chord(const QString& root, const QString& ext = QString::null);
+ Chord(const Event&);
+
+ Event* getAsEvent(timeT absoluteTime) const;
+
+ bool isEmpty() const { return m_root.isEmpty(); }
+ bool operator!() const { return !m_root; }
+
+ bool isUserChord() const { return m_isUserChord; }
+ void setUserChord(bool c) { m_isUserChord = c; }
+
+ QString getRoot() const { return m_root; }
+ void setRoot(QString r) { m_root = r; }
+
+ QString getExt() const { return m_ext; }
+ void setExt(QString r) { m_ext = r.isEmpty() ? QString::null : r; }
+
+ bool hasAltBass() const { return m_ext.contains(ALT_BASS_REGEXP); }
+
+ Fingering getFingering() const { return m_fingering; }
+ void setFingering(Fingering f) { m_fingering = f; }
+
+ struct ChordCmp
+ {
+ bool operator()(const Chord &e1, const Chord &e2) const {
+ return e1 < e2;
+ }
+ bool operator()(const Chord *e1, const Chord *e2) const {
+ return *e1 < *e2;
+ }
+ };
+
+protected:
+
+ static const QRegExp ALT_BASS_REGEXP;
+
+ QString m_root;
+ QString m_ext;
+
+ Fingering m_fingering;
+
+ bool m_isUserChord;
+};
+
+bool operator<(const Chord&, const Chord&);
+
+}
+
+}
+
+#endif /*_RG_CHORD2_H_*/
diff --git a/src/gui/editors/guitar/ChordMap.cpp b/src/gui/editors/guitar/ChordMap.cpp
new file mode 100644
index 0000000..06662d9
--- /dev/null
+++ b/src/gui/editors/guitar/ChordMap.cpp
@@ -0,0 +1,223 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "misc/Debug.h"
+#include "ChordMap.h"
+
+#include <qfile.h>
+#include <qtextstream.h>
+
+namespace Rosegarden
+{
+
+namespace Guitar
+{
+
+ChordMap::ChordMap()
+ : m_needSave(false)
+{
+}
+
+void ChordMap::insert(const Chord& c)
+{
+ m_map.insert(c);
+ m_needSave = true;
+}
+
+
+ChordMap::chordarray
+ChordMap::getChords(const QString& root, const QString& ext) const
+{
+ chordarray res;
+
+ Chord tmp(root, ext);
+ NOTATION_DEBUG << "ChordMap::getChords : chord = " << tmp << " - ext is empty : " << ext.isEmpty() << endl;
+
+ for (chordset::const_iterator i = m_map.lower_bound(tmp); i != m_map.end(); ++i) {
+ NOTATION_DEBUG << "ChordMap::getChords : checking chord " << *i << endl;
+
+ if (i->getRoot() != root)
+ break;
+
+ if (/* ext.isNull() || */ i->getExt() == ext) {
+ NOTATION_DEBUG << "ChordMap::getChords : adding chord " << *i << endl;
+ res.push_back(*i);
+ } else {
+ break;
+ }
+ }
+
+ return res;
+}
+
+QStringList
+ChordMap::getRootList() const
+{
+ static QStringList rootNotes;
+
+ if (rootNotes.count() == 0) {
+ rootNotes = QStringList::split(QString(","), "A,A#/Bb,B,C,C#/Db,D,D#/Eb,E,F,F#/Gb,G,G#/Ab");
+ }
+
+ // extract roots from map itself - not a very good idea
+ //
+// QString currentRoot;
+//
+// for(chordset::const_iterator i = m_map.begin(); i != m_map.end(); ++i) {
+// const Chord& chord = *i;
+// if (chord.getRoot() != currentRoot) {
+// rootNotes.push_back(chord.getRoot());
+// currentRoot = chord.getRoot();
+// }
+// }
+
+ return rootNotes;
+}
+
+QStringList
+ChordMap::getExtList(const QString& root) const
+{
+ QStringList extList;
+ QString currentExt = "ZZ";
+
+ Chord tmp(root);
+
+ for(chordset::const_iterator i = m_map.lower_bound(tmp); i != m_map.end(); ++i) {
+ const Chord& chord = *i;
+// NOTATION_DEBUG << "ChordMap::getExtList : chord = " << chord << endl;
+
+ if (chord.getRoot() != root)
+ break;
+
+ if (chord.getExt() != currentExt) {
+// NOTATION_DEBUG << "ChordMap::getExtList : adding ext " << chord.getExt() << " for root " << root << endl;
+ extList.push_back(chord.getExt());
+ currentExt = chord.getExt();
+ }
+ }
+
+ return extList;
+}
+
+void
+ChordMap::substitute(const Chord& oldChord, const Chord& newChord)
+{
+ remove(oldChord);
+ insert(newChord);
+}
+
+void
+ChordMap::remove(const Chord& c)
+{
+ m_map.erase(c);
+ m_needSave = true;
+}
+
+bool ChordMap::saveDocument(const QString& filename, bool userChordsOnly, QString& errMsg)
+{
+ QFile file(filename);
+ file.open(IO_WriteOnly);
+
+ QTextStream outStream(&file);
+
+ outStream.setEncoding(QTextStream::UnicodeUTF8);
+
+ outStream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ << "<!DOCTYPE rosegarden-chord-data>\n"
+ << "<rosegarden-chord-data version=\"" << VERSION
+ << "\" format-version-major=\"" << FILE_FORMAT_VERSION_MAJOR
+ << "\" format-version-minor=\"" << FILE_FORMAT_VERSION_MINOR
+ << "\" format-version-point=\"" << FILE_FORMAT_VERSION_POINT
+ << "\">\n";
+
+ outStream << "<chords>\n";
+
+ QString currentExt, currentRoot;
+
+ for(iterator i = begin(); i != end(); ++i) {
+ const Chord& chord = *i;
+
+ if (userChordsOnly && !chord.isUserChord())
+ continue; // skip non-user chords
+
+ if (chord.getRoot() != currentRoot) {
+
+ currentRoot = chord.getRoot();
+
+ // close current chordset (if there was one)
+ if (i != begin())
+ outStream << "\n</chordset>\n";
+
+ // open new chordset
+ outStream << "<chordset root=\"" << chord.getRoot() << "\">\n";
+ currentExt = "NEWEXT"; // to make sure we open a new chord right after that
+ }
+
+ if (chord.getExt() != currentExt) {
+
+ currentExt = chord.getExt();
+
+ // close current chord (if there was one)
+ if (i != begin())
+ outStream << "</chord>\n";
+
+ // open new chord
+ outStream << "<chord";
+ if (!chord.getExt().isEmpty())
+ outStream << " ext=\"" << chord.getExt() << "\"";
+ if (chord.isUserChord())
+ outStream << " user=\"true\"";
+
+ outStream << ">\n";
+ }
+
+ outStream << "<fingering>" << chord.getFingering().toString() << "</fingering>\n";
+ }
+
+ if (!m_map.empty())
+ outStream << "</chord>\n"; // close last written chord
+
+ outStream << "</chords>\n";
+ outStream << "</rosegarden-chord-data>\n";
+
+ return outStream.device()->status() == IO_Ok;
+}
+
+int ChordMap::FILE_FORMAT_VERSION_MAJOR = 1;
+int ChordMap::FILE_FORMAT_VERSION_MINOR = 0;
+int ChordMap::FILE_FORMAT_VERSION_POINT = 0;
+
+
+void
+ChordMap::debugDump() const
+{
+ for(chordset::const_iterator i = m_map.begin(); i != m_map.end(); ++i) {
+ Chord chord = *i;
+ NOTATION_DEBUG << "ChordMap::debugDump " << chord << endl;
+ }
+}
+
+}
+
+}
diff --git a/src/gui/editors/guitar/ChordMap.h b/src/gui/editors/guitar/ChordMap.h
new file mode 100644
index 0000000..5b7488d
--- /dev/null
+++ b/src/gui/editors/guitar/ChordMap.h
@@ -0,0 +1,87 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_CHORDMAP_H_
+#define _RG_CHORDMAP_H_
+
+#include "Chord.h"
+
+#include <qstringlist.h>
+#include <set>
+
+namespace Rosegarden
+{
+
+namespace Guitar
+{
+
+class ChordMap
+{
+ typedef std::set<Chord, Chord::ChordCmp> chordset;
+
+public:
+ typedef std::vector<Chord> chordarray;
+
+ typedef chordset::iterator iterator;
+ typedef chordset::const_iterator const_iterator;
+
+ static int FILE_FORMAT_VERSION_MAJOR;
+ static int FILE_FORMAT_VERSION_MINOR;
+ static int FILE_FORMAT_VERSION_POINT;
+
+ ChordMap();
+
+ void insert(const Chord&);
+ void substitute(const Chord& oldChord, const Chord& newChord);
+ void remove(const Chord&);
+
+ chordarray getChords(const QString& root, const QString& ext) const;
+
+ QStringList getRootList() const;
+ QStringList getExtList(const QString& root) const;
+
+ void debugDump() const;
+
+ bool needSave() const { return m_needSave; }
+ void clearNeedSave() { m_needSave = false; }
+
+ bool saveDocument(const QString& filename, bool userChordsOnly, QString& errMsg);
+
+ iterator begin() { return m_map.begin(); }
+ iterator end() { return m_map.end(); }
+ const_iterator begin() const { return m_map.begin(); }
+ const_iterator end() const { return m_map.end(); }
+
+protected:
+
+ chordset m_map;
+
+ bool m_needSave;
+};
+
+}
+
+}
+
+#endif /*_RG_CHORDMAP2_H_*/
diff --git a/src/gui/editors/guitar/ChordXmlHandler.cpp b/src/gui/editors/guitar/ChordXmlHandler.cpp
new file mode 100644
index 0000000..701c43c
--- /dev/null
+++ b/src/gui/editors/guitar/ChordXmlHandler.cpp
@@ -0,0 +1,154 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral right of the authors to claim authorship of this work
+ has been asserted.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "ChordXmlHandler.h"
+#include "misc/Debug.h"
+
+namespace Rosegarden
+{
+
+ChordXmlHandler::ChordXmlHandler(Guitar::ChordMap& map)
+ : ProgressReporter(0),
+ m_chordMap(map)
+{
+}
+
+ChordXmlHandler::~ChordXmlHandler()
+{
+}
+
+bool ChordXmlHandler::startDocument()
+{
+ // nothing to do ?
+ return true;
+}
+
+bool ChordXmlHandler::startElement(const QString& namespaceURI,
+ const QString& localName,
+ const QString& qName,
+ const QXmlAttributes& atts)
+{
+ QString lcName = qName.lower();
+
+ if (lcName == "chordset") {
+ // start new chord set
+ m_currentRoot = atts.value("root").stripWhiteSpace();
+
+ } else if (lcName == "chord") {
+
+ m_currentChord = Guitar::Chord(m_currentRoot);
+
+ if (atts.index("ext") >= 0)
+ m_currentChord.setExt(atts.value("ext").stripWhiteSpace());
+
+ if (atts.index("user") >= 0) {
+ QString userVal = atts.value("user").stripWhiteSpace().lower();
+ bool res = (userVal == "yes" || userVal == "1" || userVal == "true");
+ m_currentChord.setUserChord(res);
+ } else {
+ m_currentChord.setUserChord(false);
+ }
+
+ } else if (lcName == "fingering") {
+ m_inFingering = true;
+ }
+
+ return true;
+}
+
+bool ChordXmlHandler::endElement(const QString& namespaceURI,
+ const QString& localName,
+ const QString& qName)
+{
+ QString lcName = qName.lower();
+
+ if (lcName == "fingering") {
+
+ m_inFingering = false;
+ m_chordMap.insert(m_currentChord);
+ NOTATION_DEBUG << "ChordXmlHandler::endElement (fingering) : adding chord " << m_currentChord << endl;
+
+ } else if (lcName == "chord") {
+
+ // adding is done after each parsing of fingering
+ //
+// m_chordMap.insert(m_currentChord);
+
+ }
+
+ return true;
+}
+
+bool ChordXmlHandler::characters(const QString& ch)
+{
+ QString ch2 = ch.simplifyWhiteSpace();
+
+ if (!ch2.isEmpty() && m_inFingering) {
+ if (!parseFingering(ch2))
+ return false;
+ }
+
+ return true;
+}
+
+bool ChordXmlHandler::endDocument()
+{
+ return true;
+}
+
+bool ChordXmlHandler::parseFingering(const QString& ch) {
+
+ QString errString;
+
+ Guitar::Fingering fingering = Guitar::Fingering::parseFingering(ch, errString);
+
+ if (m_errorString.isEmpty()) {
+ NOTATION_DEBUG << "ChordXmlHandler::parseFingering : fingering " << ch << endl;
+ m_currentChord.setFingering(fingering);
+ return true;
+ } else {
+ m_errorString = errString;
+ return false;
+ }
+}
+
+bool
+ChordXmlHandler::error(const QXmlParseException& exception)
+{
+ m_errorString = QString("%1 at line %2, column %3")
+ .arg(exception.message())
+ .arg(exception.lineNumber())
+ .arg(exception.columnNumber());
+ return QXmlDefaultHandler::error( exception );
+}
+
+bool
+ChordXmlHandler::fatalError(const QXmlParseException& exception)
+{
+ m_errorString = QString("%1 at line %2, column %3")
+ .arg(exception.message())
+ .arg(exception.lineNumber())
+ .arg(exception.columnNumber());
+ return QXmlDefaultHandler::fatalError( exception );
+}
+
+
+}
diff --git a/src/gui/editors/guitar/ChordXmlHandler.h b/src/gui/editors/guitar/ChordXmlHandler.h
new file mode 100644
index 0000000..ca25168
--- /dev/null
+++ b/src/gui/editors/guitar/ChordXmlHandler.h
@@ -0,0 +1,78 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#ifndef _RG_CHORDXMLHANDLER_H_
+#define _RG_CHORDXMLHANDLER_H_
+
+#include "gui/general/ProgressReporter.h"
+#include "Chord.h"
+#include "ChordMap.h"
+
+#include <qxml.h>
+
+
+namespace Rosegarden
+{
+
+class ChordXmlHandler : public ProgressReporter, public QXmlDefaultHandler
+{
+public:
+ ChordXmlHandler(Guitar::ChordMap&);
+ virtual ~ChordXmlHandler();
+
+ /// overloaded handler functions
+ virtual bool startDocument();
+ virtual bool startElement(const QString& namespaceURI,
+ const QString& localName,
+ const QString& qName,
+ const QXmlAttributes& atts);
+
+ virtual bool endElement(const QString& namespaceURI,
+ const QString& localName,
+ const QString& qName);
+
+ virtual bool characters(const QString& ch);
+
+ virtual bool endDocument ();
+
+ /// Return the error string set during the parsing (if any)
+ QString errorString() { return m_errorString; }
+ bool error(const QXmlParseException& exception);
+ bool fatalError(const QXmlParseException& exception);
+
+protected:
+
+ bool parseFingering(const QString& ch);
+
+ Guitar::Chord m_currentChord;
+ QString m_currentRoot;
+ QString m_errorString;
+ bool m_inFingering;
+ Guitar::ChordMap& m_chordMap;
+};
+
+}
+
+#endif /*_RG_CHORDXMLHANDLER_H_*/
diff --git a/src/gui/editors/guitar/Fingering.cpp b/src/gui/editors/guitar/Fingering.cpp
new file mode 100644
index 0000000..dd1edbd
--- /dev/null
+++ b/src/gui/editors/guitar/Fingering.cpp
@@ -0,0 +1,152 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "Fingering.h"
+
+#include "misc/Debug.h"
+
+#include <qstringlist.h>
+#include <sstream>
+#include <algorithm>
+#include <klocale.h>
+
+namespace Rosegarden
+{
+
+namespace Guitar
+{
+
+Fingering::Fingering(unsigned int nbStrings) :
+ m_strings(nbStrings, MUTED)
+{
+}
+
+Fingering::Fingering(QString s)
+{
+ QString errString;
+ Fingering t = parseFingering(s, errString);
+ m_strings = t.m_strings;
+}
+
+unsigned int
+Fingering::getStartFret() const
+{
+ int min = 999, max = 0;
+ for(std::vector<int>::const_iterator i = m_strings.begin(); i != m_strings.end(); ++i) {
+ if (*i < min && *i > 0)
+ min = *i;
+ if (*i > max)
+ max = *i;
+ }
+
+ if (max < 4)
+ min = 1;
+
+ return min == 999 ? 1 : min;
+}
+
+bool
+Fingering::hasBarre() const
+{
+ int lastStringStatus = m_strings[getNbStrings() - 1];
+
+ return ((m_strings[0] > OPEN && m_strings[0] == lastStringStatus) ||
+ (m_strings[1] > OPEN && m_strings[1] == lastStringStatus) ||
+ (m_strings[2] > OPEN && m_strings[2] == lastStringStatus));
+}
+
+Fingering::Barre
+Fingering::getBarre() const
+{
+ int lastStringStatus = m_strings[getNbStrings() - 1];
+
+ Barre res;
+
+ res.fret = lastStringStatus;
+
+ for(unsigned int i = 0; i < 3; ++i) {
+ if (m_strings[i] > OPEN && m_strings[i] == lastStringStatus)
+ res.start = i;
+ break;
+ }
+
+ res.end = 5;
+
+ return res;
+}
+
+Fingering
+Fingering::parseFingering(const QString& ch, QString& errorString)
+{
+ QStringList tokens = QStringList::split(' ', ch);
+
+ unsigned int idx = 0;
+ Fingering fingering;
+
+ for(QStringList::iterator i = tokens.begin(); i != tokens.end() && idx < fingering.getNbStrings(); ++i, ++idx) {
+ QString t = *i;
+ bool b = false;
+ unsigned int fn = t.toUInt(&b);
+ if (b) {
+// NOTATION_DEBUG << "Fingering::parseFingering : '" << t << "' = " << fn << endl;
+ fingering[idx] = fn;
+ } else if (t.lower() == "x") {
+// NOTATION_DEBUG << "Fingering::parseFingering : '" << t << "' = MUTED\n";
+ fingering[idx] = MUTED;
+ } else {
+ errorString = i18n("couldn't parse fingering '%1' in '%2'").arg(t).arg(ch);
+ }
+ }
+
+ return fingering;
+}
+
+
+std::string Fingering::toString() const
+{
+ std::stringstream s;
+
+ for(std::vector<int>::const_iterator i = m_strings.begin(); i != m_strings.end(); ++i) {
+ if (*i >= 0)
+ s << *i << ' ';
+ else
+ s << "x ";
+ }
+
+ return s.str();
+}
+
+bool operator<(const Fingering& a, const Fingering& b)
+{
+ for(unsigned int i = 0; i < Fingering::DEFAULT_NB_STRINGS; ++i) {
+ if (a.getStringStatus(i) != b.getStringStatus(i)) {
+ return a.getStringStatus(i) < b.getStringStatus(i);
+ }
+ }
+ return false;
+}
+
+}
+
+}
diff --git a/src/gui/editors/guitar/Fingering.h b/src/gui/editors/guitar/Fingering.h
new file mode 100644
index 0000000..41d9799
--- /dev/null
+++ b/src/gui/editors/guitar/Fingering.h
@@ -0,0 +1,95 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_FINGERING_H_
+#define _RG_FINGERING_H_
+
+#include <vector>
+#include <qstring.h>
+#include "base/Event.h"
+
+namespace Rosegarden
+{
+
+namespace Guitar
+{
+
+class Fingering
+{
+public:
+ friend bool operator<(const Fingering&, const Fingering&);
+
+ typedef std::vector<int>::iterator iterator;
+ typedef std::vector<int>::const_iterator const_iterator;
+
+ struct Barre {
+ unsigned int fret;
+ unsigned int start;
+ unsigned int end;
+ };
+
+ static const unsigned int DEFAULT_NB_STRINGS = 6;
+
+ Fingering(unsigned int nbStrings = DEFAULT_NB_STRINGS);
+ Fingering(QString);
+
+ enum { MUTED = -1, OPEN = 0 };
+
+ /**
+ * returns the fret number on which the string is pressed, or one of MUTED and OPEN
+ *
+ */
+ int getStringStatus(int stringNb) const { return m_strings[stringNb]; }
+ void setStringStatus(int stringNb, int status) { m_strings[stringNb] = status; }
+ unsigned int getStartFret() const;
+ unsigned int getNbStrings() const { return m_strings.size(); }
+
+ bool hasBarre() const;
+ Barre getBarre() const;
+
+ int operator[](int i) const { return m_strings[i]; }
+ int& operator[](int i) { return m_strings[i]; }
+
+ bool operator==(const Fingering& o) const { return m_strings == o.m_strings; }
+
+ iterator begin() { return m_strings.begin(); }
+ iterator end() { return m_strings.end(); }
+ const_iterator begin() const { return m_strings.begin(); }
+ const_iterator end() const { return m_strings.end(); }
+
+ static Fingering parseFingering(const QString&, QString& errorString);
+ std::string toString() const;
+
+protected:
+
+ std::vector<int> m_strings;
+};
+
+bool operator<(const Fingering&, const Fingering&);
+
+}
+
+}
+
+#endif /*_RG_FINGERING2_H_*/
diff --git a/src/gui/editors/guitar/FingeringBox.cpp b/src/gui/editors/guitar/FingeringBox.cpp
new file mode 100644
index 0000000..885ba83
--- /dev/null
+++ b/src/gui/editors/guitar/FingeringBox.cpp
@@ -0,0 +1,293 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "FingeringBox.h"
+#include "Fingering.h"
+
+#include "misc/Debug.h"
+
+namespace Rosegarden
+{
+
+FingeringBox::FingeringBox(unsigned int nbFrets, unsigned int nbStrings, bool editable, QWidget *parent, const char* name)
+ : QFrame(parent, name),
+ m_nbFretsDisplayed(nbFrets),
+ m_startFret(1),
+ m_nbStrings(nbStrings),
+ m_transientFretNb(0),
+ m_transientStringNb(0),
+ m_editable(editable),
+ m_noteSymbols(m_nbStrings, m_nbFretsDisplayed)
+{
+ init();
+}
+
+FingeringBox::FingeringBox(bool editable, QWidget *parent, const char* name)
+ : QFrame(parent, name),
+ m_nbFretsDisplayed(DEFAULT_NB_DISPLAYED_FRETS),
+ m_startFret(1),
+ m_nbStrings(Guitar::Fingering::DEFAULT_NB_STRINGS),
+ m_editable(editable),
+ m_noteSymbols(m_nbStrings, m_nbFretsDisplayed)
+{
+ init();
+}
+
+void
+FingeringBox::init()
+{
+ setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
+ setFixedSize(IMG_WIDTH, IMG_HEIGHT);
+ setBackgroundMode(PaletteBase);
+ if (m_editable)
+ setMouseTracking(true);
+
+}
+
+void
+FingeringBox::drawContents(QPainter* p)
+{
+// NOTATION_DEBUG << "FingeringBox::drawContents()" << endl;
+
+ // For all strings on guitar
+ // check state of string
+ // If pressed display note
+ // Else display muted or open symbol
+ // For all bars
+ // display bar
+ // Horizontal separator line
+
+ // draw guitar chord fingering
+ //
+ m_noteSymbols.drawFretNumber(p, m_startFret);
+ m_noteSymbols.drawFrets(p);
+ m_noteSymbols.drawStrings(p);
+
+ unsigned int stringNb = 0;
+
+ // draw notes
+ //
+ for (Guitar::Fingering::const_iterator pos = m_fingering.begin();
+ pos != m_fingering.end();
+ ++pos, ++stringNb) {
+
+ switch (*pos) {
+ case Guitar::Fingering::OPEN:
+// NOTATION_DEBUG << "Fingering::drawContents - drawing Open symbol on string " << stringNb << endl;
+ m_noteSymbols.drawOpenSymbol(p, stringNb);
+ break;
+
+ case Guitar::Fingering::MUTED:
+// NOTATION_DEBUG << "Fingering::drawContents - drawing Mute symbol on string" << stringNb << endl;
+ m_noteSymbols.drawMuteSymbol(p, stringNb);
+ break;
+
+ default:
+// NOTATION_DEBUG << "Fingering::drawContents - drawing note symbol at " << *pos << " on string " << stringNb << endl;
+ m_noteSymbols.drawNoteSymbol(p, stringNb, *pos - (m_startFret - 1), false);
+ break;
+ }
+ }
+
+ // TODO: detect barres and draw them in a special way ?
+
+ // draw transient note (visual feedback for mouse move)
+ //
+ if (hasMouse() &&
+ m_transientFretNb > 0 && m_transientFretNb <= m_nbFretsDisplayed &&
+ m_transientStringNb >= 0 && m_transientStringNb <= m_nbStrings) {
+ m_noteSymbols.drawNoteSymbol(p, m_transientStringNb, m_transientFretNb - (m_startFret - 1), true);
+ }
+
+ // DEBUG
+// p->save();
+// p->setPen(Qt::red);
+// unsigned int topBorderY = m_noteSymbols.getTopBorder(maximumHeight());
+// p->drawLine(0, topBorderY, 20, topBorderY);
+// p->drawRect(m_r1);
+// p->setPen(Qt::blue);
+// p->drawRect(m_r2);
+// p->restore();
+}
+
+void
+FingeringBox::setFingering(const Guitar::Fingering& f) {
+ m_fingering = f;
+ m_startFret = m_fingering.getStartFret();
+ update();
+}
+
+unsigned int
+FingeringBox::getStringNumber(const QPoint& pos)
+{
+ PositionPair result = m_noteSymbols.getStringNumber(maximumHeight(),
+ pos.x(),
+ m_nbStrings);
+ unsigned int stringNum = -1;
+
+ if(result.first){
+ stringNum = result.second;
+// RG_DEBUG << "FingeringBox::getStringNumber : res = " << stringNum << endl;
+ }
+
+ return stringNum;
+}
+
+unsigned int
+FingeringBox::getFretNumber(const QPoint& pos)
+{
+ unsigned int fretNum = 0;
+
+ if(true || pos.y() > m_noteSymbols.getTopBorder(maximumHeight())) {
+ // If fret position is below the top line of the guitar chord image.
+ PositionPair result = m_noteSymbols.getFretNumber(maximumWidth(),
+ pos.y(),
+ m_nbFretsDisplayed);
+
+ if(result.first) {
+ fretNum = result.second + (m_startFret - 1);
+// RG_DEBUG << "FingeringBox::getFretNumber : res = " << fretNum << " startFret = " << m_startFret << endl;
+ } else {
+// RG_DEBUG << "FingeringBox::getFretNumber : no res\n";
+ }
+ }
+
+ return fretNum;
+}
+
+void
+FingeringBox::mousePressEvent(QMouseEvent *event)
+{
+ if (!m_editable)
+ return;
+
+ if((event->button() == LeftButton) && m_editable) {
+
+ // Find string position
+ m_press_string_num = getStringNumber(event->pos());
+
+ // Find fret position
+ m_press_fret_num = getFretNumber(event->pos());
+ }
+}
+
+void
+FingeringBox::mouseReleaseEvent(QMouseEvent *event)
+{
+ if(!m_editable)
+ return ;
+
+ unsigned int release_string_num = getStringNumber(event->pos());
+ unsigned int release_fret_num = getFretNumber(event->pos());
+
+ processMouseRelease(release_string_num, release_fret_num);
+}
+
+void
+FingeringBox::processMouseRelease(unsigned int release_string_num,
+ unsigned int release_fret_num)
+{
+ if(m_press_fret_num == release_fret_num) {
+ // If press string & fret pos == release string & fret position, display chord
+ if(m_press_string_num == release_string_num) {
+
+ if(m_press_fret_num < (m_startFret + m_nbFretsDisplayed)) {
+
+ unsigned int aVal = m_press_fret_num;
+
+ if(m_press_fret_num == 0) {
+
+ int stringStatus = m_fingering.getStringStatus(m_press_string_num);
+
+ if (stringStatus == Guitar::Fingering::OPEN)
+ aVal = Guitar::Fingering::MUTED;
+ else if (stringStatus > Guitar::Fingering::OPEN)
+ aVal = Guitar::Fingering::OPEN;
+
+ }
+
+ m_fingering.setStringStatus(m_press_string_num, aVal);
+
+ update();
+ }
+ }
+ // else if press fret pos == release fret pos & press string pos != release string pos, display bar
+ else {
+ if(((m_press_string_num > 0)&&(release_string_num > 0)) &&
+ (( m_press_string_num <= m_nbStrings)&&
+ (release_string_num <= m_nbStrings)) &&
+ (( m_press_fret_num <(m_startFret + m_nbFretsDisplayed)) &&
+ (release_fret_num <(m_startFret + m_nbFretsDisplayed)))) {
+
+ // TODO deal with barre later on
+
+ }
+ }
+ }
+}
+
+
+void
+FingeringBox::mouseMoveEvent( QMouseEvent *event )
+{
+ if (!m_editable)
+ return;
+
+ unsigned int transientStringNb = getStringNumber(event->pos());
+ unsigned int transientFretNb = getFretNumber(event->pos());
+
+ if (transientStringNb != m_transientStringNb ||
+ transientFretNb != m_transientFretNb) {
+
+ QRect r1 = m_noteSymbols.getTransientNoteSymbolRect(size(),
+ m_transientStringNb,
+ m_transientFretNb - (m_startFret - 1));
+ m_transientStringNb = transientStringNb;
+ m_transientFretNb = transientFretNb;
+ QRect r2 = m_noteSymbols.getTransientNoteSymbolRect(size(),
+ m_transientStringNb,
+ m_transientFretNb - (m_startFret - 1));
+
+ m_r1 = r1;
+ m_r2 = r2;
+
+// RG_DEBUG << "Fingering::updateTransientPos r1 = " << r1 << " - r2 = " << r2 << endl;
+
+// QRect updateRect = r1 | r2;
+// update(updateRect);
+
+ update();
+
+ }
+
+}
+
+void
+FingeringBox::leaveEvent(QEvent*)
+{
+ update();
+}
+
+}
diff --git a/src/gui/editors/guitar/FingeringBox.h b/src/gui/editors/guitar/FingeringBox.h
new file mode 100644
index 0000000..b54c0a8
--- /dev/null
+++ b/src/gui/editors/guitar/FingeringBox.h
@@ -0,0 +1,106 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+
+#ifndef _RG_FINGERINGBOX_H_
+#define _RG_FINGERINGBOX_H_
+
+#include <qframe.h>
+
+#include "NoteSymbols.h"
+#include "Fingering.h"
+
+namespace Rosegarden
+{
+
+class Fingering;
+
+class FingeringBox : public QFrame
+{
+ static const unsigned int IMG_WIDTH = 200;
+ static const unsigned int IMG_HEIGHT = 200;
+
+public:
+ FingeringBox(unsigned int nbFrets, unsigned int nbStrings, bool editable, QWidget *parent, const char* name = 0);
+ FingeringBox(bool editable, QWidget *parent, const char* name = 0);
+
+ void setStartFret(unsigned int f) { m_startFret = f; update(); }
+ unsigned int getStartFret() const { return m_startFret; }
+
+ void setFingering(const Guitar::Fingering&);
+ const Guitar::Fingering& getFingering() { return m_fingering; }
+
+ const Guitar::NoteSymbols& getNoteSymbols() const { return m_noteSymbols; }
+
+ static const unsigned int DEFAULT_NB_DISPLAYED_FRETS = 4;
+
+protected:
+ void init();
+
+ virtual void drawContents(QPainter*);
+
+ virtual void mousePressEvent(QMouseEvent*);
+ virtual void mouseReleaseEvent(QMouseEvent*);
+ virtual void mouseMoveEvent(QMouseEvent*);
+ virtual void leaveEvent(QEvent*);
+
+ void processMouseRelease( unsigned int release_string_num, unsigned int release_fret_num);
+
+ typedef std::pair<bool, unsigned int> PositionPair;
+
+ unsigned int getStringNumber(const QPoint&);
+
+ unsigned int getFretNumber(const QPoint&);
+
+ //! Maximum number of frets displayed by FingeringBox
+ unsigned int m_nbFretsDisplayed;
+
+ unsigned int m_startFret;
+
+ unsigned int m_nbStrings;
+
+ unsigned int m_transientFretNb;
+ unsigned int m_transientStringNb;
+
+ //! Present mode
+ bool m_editable;
+
+ //! Handle to the present fingering
+ Guitar::Fingering m_fingering;
+
+ //! String number where a mouse press event was located
+ unsigned int m_press_string_num;
+
+ //! Fret number where a mouse press event was located
+ unsigned int m_press_fret_num;
+
+ Guitar::NoteSymbols m_noteSymbols;
+
+ QRect m_r1, m_r2;
+};
+
+}
+
+#endif /*_RG_FINGERINGBOX2_H_*/
diff --git a/src/gui/editors/guitar/FingeringListBoxItem.cpp b/src/gui/editors/guitar/FingeringListBoxItem.cpp
new file mode 100644
index 0000000..31b92e9
--- /dev/null
+++ b/src/gui/editors/guitar/FingeringListBoxItem.cpp
@@ -0,0 +1,36 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "FingeringListBoxItem.h"
+
+namespace Rosegarden {
+
+FingeringListBoxItem::FingeringListBoxItem(const Guitar::Chord& chord, QListBox* parent, QPixmap pixmap, QString fingeringString)
+ : QListBoxPixmap(parent, pixmap, fingeringString),
+ m_chord(chord)
+{
+}
+
+}
diff --git a/src/gui/editors/guitar/FingeringListBoxItem.h b/src/gui/editors/guitar/FingeringListBoxItem.h
new file mode 100644
index 0000000..b7625e2
--- /dev/null
+++ b/src/gui/editors/guitar/FingeringListBoxItem.h
@@ -0,0 +1,46 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#ifndef _RG_FINGERINGLISTBOXITEM_H_
+#define _RG_FINGERINGLISTBOXITEM_H_
+
+#include <qlistbox.h>
+#include "Chord.h"
+
+namespace Rosegarden {
+
+class FingeringListBoxItem : public QListBoxPixmap
+{
+public:
+ FingeringListBoxItem(const Guitar::Chord& chord, QListBox* parent, QPixmap pixmap, QString fingeringString);
+
+ const Guitar::Chord& getChord() { return m_chord; }
+protected:
+ Guitar::Chord m_chord;
+};
+
+}
+
+#endif /*_RG_FINGERINGLISTBOXITEM_H_*/
diff --git a/src/gui/editors/guitar/GuitarChordEditorDialog.cpp b/src/gui/editors/guitar/GuitarChordEditorDialog.cpp
new file mode 100644
index 0000000..60da8b6
--- /dev/null
+++ b/src/gui/editors/guitar/GuitarChordEditorDialog.cpp
@@ -0,0 +1,109 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "GuitarChordEditorDialog.h"
+#include "FingeringBox.h"
+#include "Chord.h"
+#include "ChordMap.h"
+
+#include <klineedit.h>
+#include <qcombobox.h>
+#include <qspinbox.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kstddirs.h>
+#include <qlayout.h>
+#include <qlabel.h>
+
+namespace Rosegarden
+{
+
+GuitarChordEditorDialog::GuitarChordEditorDialog(Guitar::Chord& chord, const Guitar::ChordMap& chordMap, QWidget *parent)
+ : KDialogBase(parent, "GuitarChordEditor", true, i18n("Guitar Chord Editor"), Ok|Cancel),
+ m_chord(chord),
+ m_chordMap(chordMap)
+{
+ QWidget *page = new QWidget(this);
+ setMainWidget(page);
+ QGridLayout *topLayout = new QGridLayout(page, 7, 2, spacingHint());
+
+ topLayout->addWidget(new QLabel(i18n("Start fret"), page), 0, 1);
+ m_startFret = new QSpinBox(1, 24, 1, page);
+ topLayout->addWidget(m_startFret, 1, 1);
+
+ connect(m_startFret, SIGNAL(valueChanged(int)),
+ this, SLOT(slotStartFretChanged(int)));
+
+ topLayout->addWidget(new QLabel(i18n("Root"), page), 2, 1);
+ m_rootNotesList = new QComboBox(page);
+ topLayout->addWidget(m_rootNotesList, 3, 1);
+
+ topLayout->addWidget(new QLabel(i18n("Extension"), page), 4, 1);
+ m_ext = new QComboBox(true, page);
+ topLayout->addWidget(m_ext, 5, 1);
+
+ topLayout->addItem(new QSpacerItem(1, 1), 6, 1);
+
+ m_fingeringBox = new FingeringBox(true, page);
+ m_fingeringBox->setFingering(m_chord.getFingering());
+ topLayout->addMultiCellWidget(m_fingeringBox, 0, 7, 0, 0);
+
+ NOTATION_DEBUG << "GuitarChordEditorDialog : chord = " << m_chord << endl;
+
+
+ QStringList rootList = m_chordMap.getRootList();
+ if (rootList.count() > 0) {
+ m_rootNotesList->insertStringList(rootList);
+ m_rootNotesList->setCurrentItem(rootList.findIndex(m_chord.getRoot()));
+ }
+
+ QStringList extList = m_chordMap.getExtList(m_chord.getRoot());
+ if (extList.count() > 0) {
+ m_ext->insertStringList(extList);
+ m_ext->setCurrentItem(extList.findIndex(m_chord.getExt()));
+ }
+
+}
+
+void
+GuitarChordEditorDialog::slotStartFretChanged(int startFret)
+{
+ m_fingeringBox->setStartFret(startFret);
+}
+
+void
+GuitarChordEditorDialog::slotOk()
+{
+ m_chord.setFingering(m_fingeringBox->getFingering());
+ m_chord.setExt(m_ext->currentText());
+ m_chord.setRoot(m_rootNotesList->currentText());
+ m_chord.setUserChord(true);
+ KDialogBase::slotOk();
+}
+
+
+}
+
+#include "GuitarChordEditorDialog.moc"
+
diff --git a/src/gui/editors/guitar/GuitarChordEditorDialog.h b/src/gui/editors/guitar/GuitarChordEditorDialog.h
new file mode 100644
index 0000000..fc01605
--- /dev/null
+++ b/src/gui/editors/guitar/GuitarChordEditorDialog.h
@@ -0,0 +1,67 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_GUITARCHORDEDITOR2_H_
+#define _RG_GUITARCHORDEDITOR2_H_
+
+#include <kdialogbase.h>
+
+#include "Chord.h"
+#include "ChordMap.h"
+
+class QComboBox;
+class QSpinBox;
+
+namespace Rosegarden
+{
+
+class FingeringBox;
+
+
+class GuitarChordEditorDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ GuitarChordEditorDialog(Guitar::Chord&, const Guitar::ChordMap& chordMap, QWidget *parent=0);
+
+protected slots:
+ void slotStartFretChanged(int);
+ virtual void slotOk();
+
+protected:
+
+ void populateExtensions(const QStringList&);
+
+ FingeringBox* m_fingeringBox;
+ QComboBox* m_rootNotesList;
+ QSpinBox* m_startFret;
+ QComboBox* m_ext;
+ Guitar::Chord& m_chord;
+ const Guitar::ChordMap& m_chordMap;
+};
+
+}
+
+#endif /*_RG_GUITARCHORDEDITOR2_H_*/
diff --git a/src/gui/editors/guitar/GuitarChordSelectorDialog.cpp b/src/gui/editors/guitar/GuitarChordSelectorDialog.cpp
new file mode 100644
index 0000000..bd62c1f
--- /dev/null
+++ b/src/gui/editors/guitar/GuitarChordSelectorDialog.cpp
@@ -0,0 +1,475 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "GuitarChordSelectorDialog.h"
+#include "GuitarChordEditorDialog.h"
+#include "ChordXmlHandler.h"
+#include "FingeringBox.h"
+#include "FingeringListBoxItem.h"
+
+#include "misc/Debug.h"
+#include <qlistbox.h>
+#include <qlayout.h>
+#include <qcombobox.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kstddirs.h>
+
+namespace Rosegarden
+{
+
+GuitarChordSelectorDialog::GuitarChordSelectorDialog(QWidget *parent)
+ : KDialogBase(parent, "GuitarChordSelector", true, i18n("Guitar Chord Selector"), Ok|Cancel)
+{
+ QWidget *page = new QWidget(this);
+ setMainWidget(page);
+ QGridLayout *topLayout = new QGridLayout(page, 3, 4, spacingHint());
+
+ topLayout->addWidget(new QLabel(i18n("Root"), page), 0, 0);
+ m_rootNotesList = new QListBox(page);
+ topLayout->addWidget(m_rootNotesList, 1, 0);
+
+ topLayout->addWidget(new QLabel(i18n("Extension"), page), 0, 1);
+ m_chordExtList = new QListBox(page);
+ topLayout->addWidget(m_chordExtList, 1, 1);
+
+ m_newFingeringButton = new QPushButton(i18n("New"), page);
+ m_deleteFingeringButton = new QPushButton(i18n("Delete"), page);
+ m_editFingeringButton = new QPushButton(i18n("Edit"), page);
+
+ m_chordComplexityCombo = new QComboBox(page);
+ m_chordComplexityCombo->insertItem(i18n("beginner"));
+ m_chordComplexityCombo->insertItem(i18n("common"));
+ m_chordComplexityCombo->insertItem(i18n("all"));
+
+ connect(m_chordComplexityCombo, SIGNAL(activated(int)),
+ this, SLOT(slotComplexityChanged(int)));
+
+ QVBoxLayout* vboxLayout = new QVBoxLayout(page, 5);
+ topLayout->addMultiCellLayout(vboxLayout, 1, 3, 2, 2);
+ vboxLayout->addWidget(m_chordComplexityCombo);
+ vboxLayout->addStretch(10);
+ vboxLayout->addWidget(m_newFingeringButton);
+ vboxLayout->addWidget(m_deleteFingeringButton);
+ vboxLayout->addWidget(m_editFingeringButton);
+
+ connect(m_newFingeringButton, SIGNAL(clicked()),
+ this, SLOT(slotNewFingering()));
+ connect(m_deleteFingeringButton, SIGNAL(clicked()),
+ this, SLOT(slotDeleteFingering()));
+ connect(m_editFingeringButton, SIGNAL(clicked()),
+ this, SLOT(slotEditFingering()));
+
+ topLayout->addWidget(new QLabel(i18n("Fingerings"), page), 0, 3);
+ m_fingeringsList = new QListBox(page);
+ topLayout->addMultiCellWidget(m_fingeringsList, 1, 2, 3, 3);
+
+ m_fingeringBox = new FingeringBox(false, page);
+ topLayout->addMultiCellWidget(m_fingeringBox, 2, 2, 0, 1);
+
+ connect(m_rootNotesList, SIGNAL(highlighted(int)),
+ this, SLOT(slotRootHighlighted(int)));
+ connect(m_chordExtList, SIGNAL(highlighted(int)),
+ this, SLOT(slotChordExtHighlighted(int)));
+ connect(m_fingeringsList, SIGNAL(highlighted(QListBoxItem*)),
+ this, SLOT(slotFingeringHighlighted(QListBoxItem*)));
+}
+
+void
+GuitarChordSelectorDialog::init()
+{
+ // populate the listboxes
+ //
+ std::vector<QString> chordFiles = getAvailableChordFiles();
+
+ parseChordFiles(chordFiles);
+
+// m_chordMap.debugDump();
+
+ populate();
+}
+
+void
+GuitarChordSelectorDialog::populate()
+{
+ QStringList rootList = m_chordMap.getRootList();
+ if (rootList.count() > 0) {
+ m_rootNotesList->insertStringList(rootList);
+
+ QStringList extList = m_chordMap.getExtList(rootList.first());
+ populateExtensions(extList);
+
+ Guitar::ChordMap::chordarray chords = m_chordMap.getChords(rootList.first(), extList.first());
+ populateFingerings(chords);
+
+ m_chord.setRoot(rootList.first());
+ m_chord.setExt(extList.first());
+ }
+
+ m_rootNotesList->sort();
+
+ m_rootNotesList->setCurrentItem(0);
+}
+
+void
+GuitarChordSelectorDialog::clear()
+{
+ m_rootNotesList->clear();
+ m_chordExtList->clear();
+ m_fingeringsList->clear();
+}
+
+void
+GuitarChordSelectorDialog::refresh()
+{
+ clear();
+ populate();
+}
+
+void
+GuitarChordSelectorDialog::slotRootHighlighted(int i)
+{
+ NOTATION_DEBUG << "GuitarChordSelectorDialog::slotRootHighlighted " << i << endl;
+
+ m_chord.setRoot(m_rootNotesList->text(i));
+
+ QStringList extList = m_chordMap.getExtList(m_chord.getRoot());
+ populateExtensions(extList);
+ if (m_chordExtList->count() > 0)
+ m_chordExtList->setCurrentItem(0);
+ else
+ m_fingeringsList->clear(); // clear any previous fingerings
+}
+
+void
+GuitarChordSelectorDialog::slotChordExtHighlighted(int i)
+{
+ NOTATION_DEBUG << "GuitarChordSelectorDialog::slotChordExtHighlighted " << i << endl;
+
+ Guitar::ChordMap::chordarray chords = m_chordMap.getChords(m_chord.getRoot(), m_chordExtList->text(i));
+ populateFingerings(chords);
+
+ m_fingeringsList->setCurrentItem(0);
+}
+
+void
+GuitarChordSelectorDialog::slotFingeringHighlighted(QListBoxItem* listBoxItem)
+{
+ NOTATION_DEBUG << "GuitarChordSelectorDialog::slotFingeringHighlighted\n";
+
+ FingeringListBoxItem* fingeringItem = dynamic_cast<FingeringListBoxItem*>(listBoxItem);
+ if (fingeringItem) {
+ m_chord = fingeringItem->getChord();
+ m_fingeringBox->setFingering(m_chord.getFingering());
+ setEditionEnabled(m_chord.isUserChord());
+ }
+}
+
+void
+GuitarChordSelectorDialog::slotComplexityChanged(int)
+{
+ // simply repopulate the extension list box
+ //
+ QStringList extList = m_chordMap.getExtList(m_chord.getRoot());
+ populateExtensions(extList);
+ if (m_chordExtList->count() > 0)
+ m_chordExtList->setCurrentItem(0);
+ else
+ m_fingeringsList->clear(); // clear any previous fingerings
+}
+
+void
+GuitarChordSelectorDialog::slotNewFingering()
+{
+ Guitar::Chord newChord;
+ newChord.setRoot(m_chord.getRoot());
+ newChord.setExt(m_chord.getExt());
+
+ GuitarChordEditorDialog* chordEditorDialog = new GuitarChordEditorDialog(newChord, m_chordMap, this);
+
+ if (chordEditorDialog->exec() == QDialog::Accepted) {
+ m_chordMap.insert(newChord);
+ // populate lists
+ //
+ if (!m_rootNotesList->findItem(newChord.getRoot(), Qt::ExactMatch)) {
+ m_rootNotesList->insertItem(newChord.getRoot());
+ m_rootNotesList->sort();
+ }
+
+ if (!m_chordExtList->findItem(newChord.getExt(), Qt::ExactMatch)) {
+ m_chordExtList->insertItem(newChord.getExt());
+ m_chordExtList->sort();
+ }
+ }
+
+ delete chordEditorDialog;
+
+ refresh();
+}
+
+void
+GuitarChordSelectorDialog::slotDeleteFingering()
+{
+ if (m_chord.isUserChord()) {
+ m_chordMap.remove(m_chord);
+ delete m_fingeringsList->selectedItem();
+ }
+}
+
+void
+GuitarChordSelectorDialog::slotEditFingering()
+{
+ Guitar::Chord newChord = m_chord;
+ GuitarChordEditorDialog* chordEditorDialog = new GuitarChordEditorDialog(newChord, m_chordMap, this);
+
+ if (chordEditorDialog->exec() == QDialog::Accepted) {
+ NOTATION_DEBUG << "GuitarChordSelectorDialog::slotEditFingering() - current map state :\n";
+ m_chordMap.debugDump();
+ m_chordMap.substitute(m_chord, newChord);
+ NOTATION_DEBUG << "GuitarChordSelectorDialog::slotEditFingering() - new map state :\n";
+ m_chordMap.debugDump();
+ setChord(newChord);
+ }
+
+ delete chordEditorDialog;
+
+ refresh();
+}
+
+void
+GuitarChordSelectorDialog::slotOk()
+{
+ if (m_chordMap.needSave()) {
+ saveUserChordMap();
+ m_chordMap.clearNeedSave();
+ }
+
+ KDialogBase::slotOk();
+}
+
+void
+GuitarChordSelectorDialog::setChord(const Guitar::Chord& chord)
+{
+ NOTATION_DEBUG << "GuitarChordSelectorDialog::setChord " << chord << endl;
+
+ m_chord = chord;
+
+ // select the chord's root
+ //
+ m_rootNotesList->setCurrentItem(0);
+ QListBoxItem* correspondingRoot = m_rootNotesList->findItem(chord.getRoot(), Qt::ExactMatch);
+ if (correspondingRoot)
+ m_rootNotesList->setSelected(correspondingRoot, true);
+
+ // update the dialog's complexity setting if needed, then populate the extension list
+ //
+ QString chordExt = chord.getExt();
+ int complexityLevel = m_chordComplexityCombo->currentItem();
+ int chordComplexity = evaluateChordComplexity(chordExt);
+
+ if (chordComplexity > complexityLevel) {
+ m_chordComplexityCombo->setCurrentItem(chordComplexity);
+ }
+
+ QStringList extList = m_chordMap.getExtList(chord.getRoot());
+ populateExtensions(extList);
+
+ // select the chord's extension
+ //
+ if (chordExt.isEmpty()) {
+ chordExt = "";
+ m_chordExtList->setSelected(0, true);
+ } else {
+ QListBoxItem* correspondingExt = m_chordExtList->findItem(chordExt, Qt::ExactMatch);
+ if (correspondingExt)
+ m_chordExtList->setSelected(correspondingExt, true);
+ }
+
+ // populate fingerings and pass the current chord's fingering so it is selected
+ //
+ Guitar::ChordMap::chordarray similarChords = m_chordMap.getChords(chord.getRoot(), chord.getExt());
+ populateFingerings(similarChords, chord.getFingering());
+}
+
+void
+GuitarChordSelectorDialog::populateFingerings(const Guitar::ChordMap::chordarray& chords, const Guitar::Fingering& refFingering)
+{
+ m_fingeringsList->clear();
+
+ for(Guitar::ChordMap::chordarray::const_iterator i = chords.begin(); i != chords.end(); ++i) {
+ const Guitar::Chord& chord = *i;
+ QString fingeringString = chord.getFingering().toString();
+ NOTATION_DEBUG << "GuitarChordSelectorDialog::populateFingerings " << chord << endl;
+ QPixmap fingeringPixmap = getFingeringPixmap(chord.getFingering());
+ FingeringListBoxItem *item = new FingeringListBoxItem(chord, m_fingeringsList, fingeringPixmap, fingeringString);
+ if (refFingering == chord.getFingering()) {
+ NOTATION_DEBUG << "GuitarChordSelectorDialog::populateFingerings - fingering found " << fingeringString << endl;
+ m_fingeringsList->setSelected(item, true);
+ }
+ }
+
+}
+
+
+QPixmap
+GuitarChordSelectorDialog::getFingeringPixmap(const Guitar::Fingering& fingering) const
+{
+ QPixmap pixmap(FINGERING_PIXMAP_WIDTH, FINGERING_PIXMAP_HEIGHT);
+ pixmap.fill();
+
+ QPainter pp(&pixmap);
+ QPainter *p = &pp;
+
+ p->setViewport(FINGERING_PIXMAP_H_MARGIN, FINGERING_PIXMAP_W_MARGIN,
+ FINGERING_PIXMAP_WIDTH - FINGERING_PIXMAP_W_MARGIN,
+ FINGERING_PIXMAP_HEIGHT - FINGERING_PIXMAP_H_MARGIN);
+
+ Guitar::NoteSymbols::drawFingeringPixmap(fingering, m_fingeringBox->getNoteSymbols(), p);
+
+ return pixmap;
+}
+
+void
+GuitarChordSelectorDialog::populateExtensions(const QStringList& extList)
+{
+ m_chordExtList->clear();
+
+ if (m_chordComplexityCombo->currentItem() != COMPLEXITY_ALL) {
+ // some filtering needs to be done
+ int complexityLevel = m_chordComplexityCombo->currentItem();
+
+ QStringList filteredList;
+ for(QStringList::const_iterator i = extList.constBegin(); i != extList.constEnd(); ++i) {
+ if (evaluateChordComplexity((*i).lower().stripWhiteSpace()) <= complexityLevel) {
+ NOTATION_DEBUG << "GuitarChordSelectorDialog::populateExtensions - adding '" << *i << "'\n";
+ filteredList.append(*i);
+ }
+ }
+
+ m_chordExtList->insertStringList(filteredList);
+
+ } else {
+ m_chordExtList->insertStringList(extList);
+ }
+}
+
+int
+GuitarChordSelectorDialog::evaluateChordComplexity(const QString& ext)
+{
+ if (ext.isEmpty() ||
+ ext == "7" ||
+ ext == "m" ||
+ ext == "5")
+ return COMPLEXITY_BEGINNER;
+
+ if (ext == "dim" ||
+ ext == "dim7" ||
+ ext == "aug" ||
+ ext == "sus2" ||
+ ext == "sus4" ||
+ ext == "maj7" ||
+ ext == "m7" ||
+ ext == "mmaj7" ||
+ ext == "m7b5" ||
+ ext == "7sus4")
+
+ return COMPLEXITY_COMMON;
+
+ return COMPLEXITY_ALL;
+}
+
+void
+GuitarChordSelectorDialog::parseChordFiles(const std::vector<QString>& chordFiles)
+{
+ for(std::vector<QString>::const_iterator i = chordFiles.begin(); i != chordFiles.end(); ++i) {
+ parseChordFile(*i);
+ }
+}
+
+void
+GuitarChordSelectorDialog::parseChordFile(const QString& chordFileName)
+{
+ ChordXmlHandler handler(m_chordMap);
+ QFile chordFile(chordFileName);
+ bool ok = chordFile.open(IO_ReadOnly);
+ if (!ok)
+ KMessageBox::error(0, i18n("couldn't open file '%1'").arg(handler.errorString()));
+
+ QXmlInputSource source(chordFile);
+ QXmlSimpleReader reader;
+ reader.setContentHandler(&handler);
+ reader.setErrorHandler(&handler);
+ NOTATION_DEBUG << "GuitarChordSelectorDialog::parseChordFile() parsing " << chordFileName << endl;
+ reader.parse(source);
+ if (!ok)
+ KMessageBox::error(0, i18n("couldn't parse chord dictionnary : %1").arg(handler.errorString()));
+
+}
+
+void
+GuitarChordSelectorDialog::setEditionEnabled(bool enabled)
+{
+ m_deleteFingeringButton->setEnabled(enabled);
+ m_editFingeringButton->setEnabled(enabled);
+}
+
+std::vector<QString>
+GuitarChordSelectorDialog::getAvailableChordFiles()
+{
+ std::vector<QString> names;
+
+ // Read config for default directory
+ QStringList chordDictFiles = KGlobal::dirs()->findAllResources("appdata", "chords/*.xml");
+
+ for(QStringList::iterator i = chordDictFiles.begin(); i != chordDictFiles.end(); ++i) {
+ NOTATION_DEBUG << "GuitarChordSelectorDialog::getAvailableChordFiles : adding file " << *i << endl;
+ names.push_back(*i);
+ }
+
+ return names;
+}
+
+bool
+GuitarChordSelectorDialog::saveUserChordMap()
+{
+ // Read config for user directory
+ QString userDir = KGlobal::dirs()->saveLocation("appdata", "chords/");
+
+ QString userChordDictPath = userDir + "/user_chords.xml";
+
+ NOTATION_DEBUG << "GuitarChordSelectorDialog::saveUserChordMap() : saving user chord map to " << userChordDictPath << endl;
+ QString errMsg;
+
+ m_chordMap.saveDocument(userChordDictPath, true, errMsg);
+
+ return errMsg.isEmpty();
+}
+
+
+}
+
+
+#include "GuitarChordSelectorDialog.moc"
diff --git a/src/gui/editors/guitar/GuitarChordSelectorDialog.h b/src/gui/editors/guitar/GuitarChordSelectorDialog.h
new file mode 100644
index 0000000..6c8f1ad
--- /dev/null
+++ b/src/gui/editors/guitar/GuitarChordSelectorDialog.h
@@ -0,0 +1,120 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#ifndef _RG_GUITARCHORDSELECTORDIALOG_H_
+#define _RG_GUITARCHORDSELECTORDIALOG_H_
+
+#include "Chord.h"
+#include "ChordMap.h"
+
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <vector>
+
+class QListBox;
+class QListBoxItem;
+class QComboBox;
+class QPushButton;
+
+namespace Rosegarden
+{
+
+class FingeringBox;
+
+class GuitarChordSelectorDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ enum { COMPLEXITY_BEGINNER, COMPLEXITY_COMMON, COMPLEXITY_ALL };
+
+public:
+ GuitarChordSelectorDialog(QWidget *parent=0);
+
+ void init();
+
+ const Guitar::Chord& getChord() const { return m_chord; }
+
+ void setChord(const Guitar::Chord&);
+
+protected slots:
+ void slotRootHighlighted(int);
+ void slotChordExtHighlighted(int);
+ void slotFingeringHighlighted(QListBoxItem*);
+ void slotComplexityChanged(int);
+
+ void slotNewFingering();
+ void slotDeleteFingering();
+ void slotEditFingering();
+
+ virtual void slotOk();
+
+protected:
+
+ void parseChordFiles(const std::vector<QString>& chordFiles);
+ void parseChordFile(const QString& chordFileName);
+ void populateFingerings(const Guitar::ChordMap::chordarray&, const Guitar::Fingering& refFingering=Guitar::Fingering(0));
+ void populateExtensions(const QStringList& extList);
+
+ /// set enabled state of edit/delete buttons
+ void setEditionEnabled(bool);
+
+ void populate();
+ void clear();
+ void refresh();
+
+ bool saveUserChordMap();
+ int evaluateChordComplexity(const QString& ext);
+
+ QPixmap getFingeringPixmap(const Guitar::Fingering& fingering) const;
+
+ /// Find all chord list files on the system
+ std::vector<QString> getAvailableChordFiles();
+
+ Guitar::ChordMap m_chordMap;
+
+ /// current selected chord
+ Guitar::Chord m_chord;
+
+ // Chord data
+ QListBox* m_rootNotesList;
+ QListBox* m_chordExtList;
+ QListBox* m_fingeringsList;
+ FingeringBox* m_fingeringBox;
+
+ QComboBox* m_chordComplexityCombo;
+ QPushButton* m_newFingeringButton;
+ QPushButton* m_deleteFingeringButton;
+ QPushButton* m_editFingeringButton;
+
+ static const unsigned int FINGERING_PIXMAP_HEIGHT = 75;
+ static const unsigned int FINGERING_PIXMAP_WIDTH = 75;
+ static const unsigned int FINGERING_PIXMAP_H_MARGIN = 5;
+ static const unsigned int FINGERING_PIXMAP_W_MARGIN = 5;
+
+};
+
+}
+
+#endif /*_RG_GUITARCHORDSELECTORDIALOG_H_*/
diff --git a/src/gui/editors/guitar/NoteSymbols.cpp b/src/gui/editors/guitar/NoteSymbols.cpp
new file mode 100644
index 0000000..14379de
--- /dev/null
+++ b/src/gui/editors/guitar/NoteSymbols.cpp
@@ -0,0 +1,486 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ This file contains code from
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "NoteSymbols.h"
+#include "Fingering.h"
+#include "misc/Debug.h"
+
+namespace Rosegarden
+{
+
+namespace Guitar
+{
+NoteSymbols::posPair
+NoteSymbols::getX ( int imgWidth, unsigned int stringNb, unsigned int nbOfStrings ) const
+{
+ /*
+ std::cout << "NoteSymbols::getX - input values" << std::endl
+ << " position: " << position << std::endl
+ << " string #: " << string_num << std::endl
+ << " scale: " << scale << std::endl;
+ */
+ unsigned int lBorder = getLeftBorder( imgWidth );
+ unsigned int guitarChordWidth = getGuitarChordWidth( imgWidth );
+ unsigned int columnWidth = guitarChordWidth / nbOfStrings;
+ return std::make_pair( ( stringNb * columnWidth + lBorder ), columnWidth );
+}
+
+NoteSymbols::posPair
+NoteSymbols::getY ( int imgHeight, unsigned int fretNb, unsigned int nbOfFrets ) const
+{
+ /*
+ std::cout << "NoteSymbols::getY - input values" << std::endl
+ << " position: " << fret_pos << std::endl
+ << " max frets: " << maxFretNum << std::endl
+ << " scale: " << scale << std::endl;
+ */
+ unsigned int tBorder = getTopBorder( imgHeight );
+ unsigned int guitarChordHeight = getGuitarChordHeight( imgHeight );
+ unsigned int rowHeight = guitarChordHeight / nbOfFrets;
+ return std::make_pair( ( ( fretNb * rowHeight ) + tBorder ), rowHeight );
+}
+
+void
+NoteSymbols::drawMuteSymbol ( QPainter* p,
+ unsigned int position ) const
+{
+ QRect v = p->viewport();
+
+ posPair x_pos = getX ( v.width(), position, m_nbOfStrings );
+ unsigned int y_pos = getTopBorder( v.height() ) / 2;
+ double columnWidth = x_pos.second;
+ unsigned int width = static_cast<unsigned int>( columnWidth * 0.7 );
+ unsigned int height = static_cast<unsigned int>( columnWidth * 0.7 );
+
+ //std::cout << "NoteSymbols::drawMuteSymbol - drawing Mute symbol at string #" << position
+ //<< std::endl;
+
+ p->drawLine ( x_pos.first - ( width / 2 ),
+ y_pos - ( height / 2 ),
+ ( x_pos.first + ( width / 2 ) ),
+ y_pos + ( height / 2 ) );
+
+ p->drawLine( x_pos.first + ( width / 2 ),
+ y_pos - ( height / 2 ),
+ ( x_pos.first - ( width / 2 ) ),
+ y_pos + ( height / 2 ) );
+}
+
+void
+NoteSymbols::drawOpenSymbol ( QPainter* p,
+ unsigned int position ) const
+{
+ QRect v = p->viewport();
+ posPair x_pos = getX ( v.width(), position, m_nbOfStrings );
+ unsigned int y_pos = getTopBorder( v.height() ) / 2;
+ double columnWidth = x_pos.second;
+ unsigned int radius = static_cast<unsigned int>( columnWidth * 0.7 );
+
+ //std::cout << "NoteSymbols::drawOpenSymbol - drawing Open symbol at string #" << position
+ //<< std::endl;
+
+ p->setBrush( QBrush(p->brush().color(), Qt::NoBrush) );
+ p->drawEllipse( x_pos.first - ( radius / 2 ),
+ y_pos - ( radius / 2 ),
+ radius,
+ radius );
+}
+
+void
+NoteSymbols::drawNoteSymbol ( QPainter* p,
+ unsigned int stringNb,
+ int fretNb,
+ bool transient ) const
+{
+// NOTATION_DEBUG << "NoteSymbols::drawNoteSymbol - string: " << stringNb << ", fret:" << fretNb << endl;
+
+ QRect v = p->viewport();
+ posPair x_pos = getX ( v.width(), stringNb, m_nbOfStrings );
+ posPair y_pos = getY ( v.height(), fretNb, m_nbOfFrets );
+ double columnWidth = x_pos.second;
+ unsigned int radius;
+
+ if (transient) {
+ radius = static_cast<unsigned int>( columnWidth /* * 0.9 */ );
+ p->setBrush( QBrush(p->brush().color(), Qt::NoBrush) );
+ } else {
+ radius = static_cast<unsigned int>( columnWidth * 0.7 );
+ p->setBrush( QBrush(p->brush().color(), Qt::SolidPattern) );
+ }
+
+ int x = x_pos.first - ( radius / 2 ),
+ y = y_pos.first + ( (y_pos.second - radius) / 2) - y_pos.second + TOP_GUITAR_CHORD_MARGIN;
+
+// y = y_pos.first - (radius / 2) - y_pos.second + TOP_GUITAR_CHORD_MARGIN;
+
+// RG_DEBUG << "NoteSymbols::drawNoteSymbol : rect = " << QRect(x,y, radius, radius) << endl;
+
+ p->drawEllipse( x,
+ y,
+ radius,
+ radius );
+
+// p->save();
+// p->setPen(Qt::red);
+// p->drawRect( x, y, radius, radius );
+// p->restore();
+}
+
+void
+NoteSymbols::drawBarreSymbol ( QPainter* p,
+ int fretNb,
+ unsigned int start,
+ unsigned int end ) const
+{
+
+ //std::cout << "NoteSymbols::drawBarreSymbol - start: " << start << ", end:" << end << std::endl;
+
+ drawNoteSymbol ( p, start, fretNb );
+
+ if ( ( end - start ) >= 1 ) {
+ QRect v = p->viewport();
+ posPair startXPos = getX ( v.width(), start, m_nbOfStrings );
+ posPair endXPos = getX ( v.width(), end, m_nbOfStrings );
+ posPair y_pos = getY ( v.height(), fretNb, m_nbOfFrets );
+ double columnWidth = startXPos.second;
+ unsigned int thickness = static_cast<unsigned int>( columnWidth * 0.7 );
+
+ p->drawRect( startXPos.first,
+ y_pos.first + ( y_pos.second / 4 ) + TOP_GUITAR_CHORD_MARGIN,
+ endXPos.first - startXPos.first,
+ thickness );
+ }
+
+ drawNoteSymbol ( p, end, fretNb );
+}
+
+void
+NoteSymbols::drawFretNumber ( QPainter* p,
+ unsigned int fret_num ) const
+{
+ if ( fret_num > 1 ) {
+ QRect v = p->viewport();
+ unsigned int imgWidth = v.width();
+ unsigned int imgHeight = v.height();
+
+ p->save();
+ QFont font;
+ font.setPixelSize(getFontPixelSize(v.width(), v.height()));
+ p->setFont(font);
+
+ QString tmp;
+ tmp.setNum( fret_num );
+
+ // Use NoteSymbols to grab X and Y for first fret
+ posPair y_pos = getY( imgHeight, 0, m_nbOfFrets );
+
+ p->drawText( getLeftBorder( imgWidth ) / 4,
+ y_pos.first + ( y_pos.second / 2 ),
+ tmp );
+
+ p->restore();
+ }
+}
+
+void
+NoteSymbols::drawFrets ( QPainter* p ) const
+{
+ /*
+ std::cout << "NoteSymbols::drawFretHorizontalLines" << std::endl
+ << " scale: " << scale << std::endl
+ << " frets: " << fretsDisplayed << std::endl
+ << " max string: " << maxStringNum << std::endl;
+ */
+
+ QRect v = p->viewport();
+ unsigned int imgWidth = v.width();
+ unsigned int imgHeight = v.height();
+ //unsigned int endXPos = getGuitarChordWidth(imgWidth) + getLeftBorder(imgWidth);
+ posPair endXPos = getX ( imgWidth, m_nbOfStrings - 1, m_nbOfStrings );
+
+ unsigned int yGuitarChord = getGuitarChordHeight( imgHeight );
+ unsigned int rowHeight = yGuitarChord / m_nbOfFrets;
+
+ QPen pen(p->pen());
+ pen.setWidth(imgHeight >= 100 ? FRET_PEN_WIDTH : FRET_PEN_WIDTH / 2);
+ p->save();
+ p->setPen(pen);
+ unsigned int y_pos = (getY ( imgHeight, 0, m_nbOfFrets )).first + TOP_GUITAR_CHORD_MARGIN;
+
+// NOTATION_DEBUG << "NoteSymbols::drawFrets : " << m_nbOfFrets << endl;
+
+ // Horizontal lines
+ for ( unsigned int i = 0; i <= m_nbOfFrets; ++i ) {
+
+ /* This code borrowed from KGuitar 0.5 */
+ p->drawLine( getLeftBorder( imgWidth ),
+ y_pos,
+ endXPos.first,
+ y_pos);
+// NOTATION_DEBUG << "NoteSymbols::drawFrets : " << QPoint(getLeftBorder(imgWidth), y_pos)
+// << " to " << QPoint(endXPos.first, y_pos) << endl;
+
+
+ y_pos += rowHeight;
+ }
+
+ p->restore();
+
+}
+
+void
+NoteSymbols::drawStrings ( QPainter* p ) const
+{
+ // Vertical lines
+ QRect v = p->viewport();
+ int imgHeight = v.height();
+ int imgWidth = v.width();
+
+ unsigned int startPos = getTopBorder( imgHeight ) + TOP_GUITAR_CHORD_MARGIN;
+ unsigned int endPos = (getY ( imgHeight, m_nbOfFrets, m_nbOfFrets )).first + TOP_GUITAR_CHORD_MARGIN;
+
+ unsigned int guitarChordWidth = getGuitarChordWidth( imgWidth );
+ unsigned int columnWidth = guitarChordWidth / m_nbOfStrings;
+
+ unsigned int x_pos = (getX ( imgWidth, 0, m_nbOfStrings )).first;
+
+ QPen pen(p->pen());
+ pen.setWidth(imgWidth >= 100 ? STRING_PEN_WIDTH : STRING_PEN_WIDTH / 2);
+ p->save();
+ p->setPen(pen);
+
+ for ( unsigned int i = 0; i < m_nbOfStrings; ++i ) {
+
+ /* This code borrowed from KGuitar 0.5 */
+ p->drawLine( x_pos,
+ startPos,
+ x_pos,
+ endPos );
+
+ x_pos += columnWidth;
+ }
+
+ p->restore();
+
+}
+
+QRect NoteSymbols::getTransientNoteSymbolRect(QSize guitarChordSize,
+ unsigned int stringNb,
+ int fretNb) const
+{
+ posPair x_pos = getX ( guitarChordSize.width(), stringNb, m_nbOfStrings );
+ posPair y_pos = getY ( guitarChordSize.height(), fretNb, m_nbOfFrets );
+ double columnWidth = x_pos.second;
+ unsigned int radius = static_cast<unsigned int>( columnWidth /* * 0.9 */ );
+
+ int x = x_pos.first - ( radius / 2 ),
+ y = y_pos.first + ( (y_pos.second - radius) / 2) - y_pos.second + TOP_GUITAR_CHORD_MARGIN;
+
+ return QRect(x, y, radius, radius);
+}
+
+unsigned int
+NoteSymbols::getTopBorder ( unsigned int imgHeight ) const
+{
+ return static_cast<unsigned int>( TOP_BORDER_PERCENTAGE * imgHeight );
+}
+
+unsigned int
+NoteSymbols::getBottomBorder ( unsigned int imgHeight ) const
+{
+ return static_cast<unsigned int>( imgHeight * BOTTOM_BORDER_PERCENTAGE );
+}
+
+unsigned int
+NoteSymbols::getLeftBorder ( unsigned int imgWidth ) const
+{
+ unsigned int left = static_cast<unsigned int>( imgWidth * LEFT_BORDER_PERCENTAGE );
+ if ( left < 15 ) {
+ left = 15;
+ }
+ return left;
+}
+
+unsigned int
+NoteSymbols::getRightBorder ( unsigned int imgWidth ) const
+{
+ return static_cast<unsigned int>( imgWidth * RIGHT_BORDER_PERCENTAGE );
+}
+
+unsigned int
+NoteSymbols::getGuitarChordWidth ( int imgWidth ) const
+{
+ return static_cast<unsigned int>( imgWidth * GUITAR_CHORD_WIDTH_PERCENTAGE );
+}
+
+unsigned int
+NoteSymbols::getGuitarChordHeight ( int imgHeight ) const
+{
+ return static_cast<unsigned int>( imgHeight * GUITAR_CHORD_HEIGHT_PERCENTAGE );
+}
+
+unsigned int
+NoteSymbols::getFontPixelSize ( int imgWidth, int imgHeight ) const
+{
+ return std::max(8, imgHeight / 10);
+}
+
+std::pair<bool, unsigned int>
+NoteSymbols::getStringNumber ( int imgWidth,
+ unsigned int x_pos,
+ unsigned int maxStringNum ) const
+{
+ /*
+ std::cout << "NoteSymbols::getNumberOfStrings - input values" << std::endl
+ << " X position: " << x_pos << std::endl
+ << " string #: " << maxStringNum << std::endl
+ << " image width: " << imgWidth << std::endl;
+ */
+ bool valueOk = false;
+
+ posPair xPairPos;
+ unsigned int min = 0;
+ unsigned int max = 0;
+ unsigned int result = 0;
+
+ for ( unsigned int i = 0; i < maxStringNum; ++i ) {
+ xPairPos = getX ( imgWidth, i, maxStringNum );
+
+ // If the counter equals zero then we are at the first
+ // string to the left
+ if ( i == 0 ) {
+ // Add 10 pixel buffer to range comparison
+ min = xPairPos.first - 10;
+ } else {
+ min = xPairPos.first - xPairPos.second / 2;
+ }
+
+ // If the counter equals the maxString number -1 then we are at the last
+ // string to the right
+ if ( i == ( maxStringNum - 1 ) ) {
+ // Add 10 pixel buffer to range comparison
+ max = xPairPos.first + 10;
+ } else {
+ max = xPairPos.first + xPairPos.second / 2;
+ }
+
+ if ( ( x_pos >= min ) && ( x_pos <= max ) ) {
+ result = i;
+ valueOk = true;
+ break;
+ }
+ }
+
+ //std::cout << "NoteSymbols::getNumberOfStrings - string: #" << result << std::endl;
+ return std::make_pair( valueOk, result );
+}
+
+std::pair<bool, unsigned int>
+NoteSymbols::getFretNumber ( int imgHeight,
+ unsigned int y_pos,
+ unsigned int maxFretNum ) const
+{
+ /*
+ std::cout << "NoteSymbols::getNumberOfFrets - input values" << std::endl
+ << " Y position: " << y_pos << std::endl
+ << " max frets: " << maxFretNum << std::endl
+ << " image height: " << imgHeight << std::endl;
+ */
+
+ bool valueOk = false;
+ unsigned int tBorder = getTopBorder( imgHeight );
+ unsigned int result = 0;
+
+ if ( y_pos < tBorder ) {
+ // User pressing above the guitar chord to mark line muted or opened
+ valueOk = true;
+ } else {
+ typedef std::pair<unsigned int, unsigned int> RangePair;
+
+ posPair min_pos;
+ posPair max_pos;
+
+ for ( unsigned int i = 0; i < maxFretNum; ++i ) {
+ min_pos = getY ( imgHeight, i, maxFretNum );
+ max_pos = getY ( imgHeight, i + 1, maxFretNum );
+
+ if ( ( y_pos >= min_pos.first ) && y_pos <= max_pos.first - 1 ) {
+ result = i + 1;
+ valueOk = true;
+ break;
+ }
+ }
+ }
+ // std::cout << " fret #: " << result << std::endl;
+ return std::make_pair( valueOk, result );
+}
+
+void
+NoteSymbols::drawFingeringPixmap(const Guitar::Fingering& fingering, const Guitar::NoteSymbols& noteSymbols, QPainter *p)
+{
+ unsigned int startFret = fingering.getStartFret();
+
+ noteSymbols.drawFretNumber(p, startFret);
+ noteSymbols.drawFrets(p);
+ noteSymbols.drawStrings(p);
+
+ unsigned int stringNb = 0;
+
+ for (Fingering::const_iterator pos = fingering.begin();
+ pos != fingering.end();
+ ++pos, ++stringNb) {
+
+ switch (*pos) {
+ case Fingering::OPEN:
+ noteSymbols.drawOpenSymbol(p, stringNb);
+ break;
+
+ case Fingering::MUTED:
+ noteSymbols.drawMuteSymbol(p, stringNb);
+ break;
+
+ default:
+ noteSymbols.drawNoteSymbol(p, stringNb, *pos - (startFret - 1), false);
+ break;
+ }
+ }
+
+}
+
+
+float const NoteSymbols::LEFT_BORDER_PERCENTAGE = 0.2;
+float const NoteSymbols::RIGHT_BORDER_PERCENTAGE = 0.1;
+float const NoteSymbols::GUITAR_CHORD_WIDTH_PERCENTAGE = 0.8;
+float const NoteSymbols::TOP_BORDER_PERCENTAGE = 0.1;
+float const NoteSymbols::BOTTOM_BORDER_PERCENTAGE = 0.1;
+float const NoteSymbols::GUITAR_CHORD_HEIGHT_PERCENTAGE = 0.8;
+int const NoteSymbols::TOP_GUITAR_CHORD_MARGIN = 5;
+int const NoteSymbols::FRET_PEN_WIDTH = 2;
+int const NoteSymbols::STRING_PEN_WIDTH = 2;
+
+} /* namespace Guitar */
+
+}
+
diff --git a/src/gui/editors/guitar/NoteSymbols.h b/src/gui/editors/guitar/NoteSymbols.h
new file mode 100644
index 0000000..f90fefb
--- /dev/null
+++ b/src/gui/editors/guitar/NoteSymbols.h
@@ -0,0 +1,192 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ This file contains code from
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#ifndef _RG_SYMBOLS_H_
+#define _RG_SYMBOLS_H_
+
+#include <qbrush.h>
+#include <qpainter.h>
+
+namespace Rosegarden
+{
+
+/**
+ *----------------------------------------
+ * Finding X position on guitar chord pixmap
+ *----------------------------------------
+ *
+ * Originally x = position * scale + FC::BORDER + FC::CIRCBORD + FC::FRETTEXT
+ *
+ * The last three can be condense into on term called XBorder
+ * XBorder = FC::BORDER + FC::CIRCBORD + FC::FRETTEXT
+ * = 5 + 2 + 10 (see fingers.h)
+ * = 17
+ *
+ * The drawable guitar chord space on the x-axis:
+ * XGuitarChord = pixmap width - XBorder
+ * = width - 17
+ *
+ * The guitar chord x-axis is broken up into colums which represent the drawable
+ * space for a guitar chord component (e.g. note, barre)
+ * Column Width = XGuitarChord / number of strings
+ *
+ * Therefore a new x can be calculated from the position and the column width
+ * x = (position * Column Width) + XBorder
+ *
+ *-------------------------------------------
+ * Finding Y position on guitar chord pixmap
+ *-------------------------------------------
+ *
+ * Originally y = (FC::BORDER * scale) + (2 * FC::SPACER) + (fret * scale) + FC::CIRCBORD
+ *
+ * As with the x-axis the equation can be separated into the position plus the border. In
+ * this case YBorder
+ * YBorder = (FC::BORDER*scale) + (2*FC::SPACER) + FC::CIRCBORD
+ * = 17 (If we want to use the same border as the x-axis)
+ *
+ * The drawable guitar chord space on the y-axis:
+ * YGuitarChord = pixmap height - YBorder
+ *
+ * The guitar chord y-axis is broken up into rows which represent the drawable
+ * space for a guitar chord component (e.g. note, barre)
+ * Row Height = YGuitarChord / number of frets
+ *
+ * Therefore a new y can be calculated from the fret position and the row height
+ * y = fret * Row Height
+ **/
+
+namespace Guitar
+{
+
+class Fingering;
+
+
+class NoteSymbols
+{
+private:
+ typedef std::pair<unsigned int, unsigned int> posPair;
+
+ static float const LEFT_BORDER_PERCENTAGE;
+ static float const RIGHT_BORDER_PERCENTAGE;
+ static float const GUITAR_CHORD_WIDTH_PERCENTAGE;
+ static float const TOP_BORDER_PERCENTAGE;
+ static float const BOTTOM_BORDER_PERCENTAGE;
+ static float const GUITAR_CHORD_HEIGHT_PERCENTAGE;
+ static int const TOP_GUITAR_CHORD_MARGIN;
+ static int const FRET_PEN_WIDTH;
+ static int const STRING_PEN_WIDTH;
+
+public:
+
+ NoteSymbols(unsigned int nbOfStrings, unsigned int nbOfFrets) :
+ m_nbOfStrings(nbOfStrings),
+ m_nbOfFrets(nbOfFrets) {};
+
+ //! Display a mute symbol in the QPainter object
+ void
+ drawMuteSymbol ( QPainter* p,
+ unsigned int position ) const;
+
+ /* This code borrowed from KGuitar 0.5 */
+ //! Display a open symbol in the QPainter object (KGuitar)
+ void drawOpenSymbol ( QPainter* p,
+ unsigned int position ) const;
+
+ /* This code borrowed from KGuitar 0.5 */
+ //! Display a note symbol in the QPainter object (KGuitar)
+ void drawNoteSymbol ( QPainter* p,
+ unsigned int stringNb,
+ int fretNb,
+ bool transient = false ) const;
+
+ /* This code borrowed from KGuitar 0.5 */
+ /**
+ * Display a bar symbol in the QPainter object (KGuitar)
+ * The code from the KGuitar project was modified to display a bar. This feature was not
+ * available in that project
+ */
+ void drawBarreSymbol ( QPainter* p,
+ int fretNb,
+ unsigned int start,
+ unsigned int end ) const;
+
+ void drawFretNumber ( QPainter* p,
+ unsigned int fret_num ) const;
+
+ void drawFrets ( QPainter* p ) const;
+
+ void drawStrings ( QPainter* p ) const;
+
+ unsigned int getTopBorder ( unsigned int imgHeight ) const;
+
+ unsigned int getBottomBorder ( unsigned int imgHeight ) const;
+
+ unsigned int getLeftBorder ( unsigned int imgWidth ) const;
+
+ unsigned int getRightBorder ( unsigned int imgWidth ) const;
+
+ unsigned int getGuitarChordWidth ( int imgWidth ) const;
+
+ unsigned int getGuitarChordHeight ( int imgHeight ) const;
+
+ unsigned int getFontPixelSize ( int imgWidth, int imgHeight ) const;
+
+ std::pair<bool, unsigned int>
+ getStringNumber ( int imgWidth,
+ unsigned int x_pos,
+ unsigned int string_num ) const;
+
+ std::pair<bool, unsigned int>
+ getFretNumber ( int imgHeight,
+ unsigned int y_pos,
+ unsigned int maxFretNum ) const;
+
+ QRect getTransientNoteSymbolRect(QSize guitarChordSize,
+ unsigned int stringNb,
+ int fretNb) const;
+
+ static void drawFingeringPixmap(const Fingering& fingering, const NoteSymbols& noteSymbols, QPainter *p);
+
+private:
+
+ posPair
+ getX ( int imgWidth, unsigned int stringNb, unsigned int nbOfStrings ) const;
+
+ posPair
+ getY ( int imgHeight, unsigned int fretNb, unsigned int nbOfFrets ) const;
+
+
+ unsigned int m_nbOfStrings;
+ unsigned int m_nbOfFrets;
+
+};
+
+} /* namespace Guitar */
+
+}
+
+#endif /* SYMBOLS_H_ */
+
diff --git a/src/gui/editors/matrix/MatrixCanvasView.cpp b/src/gui/editors/matrix/MatrixCanvasView.cpp
new file mode 100644
index 0000000..c92b4aa
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixCanvasView.cpp
@@ -0,0 +1,302 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MatrixCanvasView.h"
+
+#include "base/SnapGrid.h"
+#include "gui/general/MidiPitchLabel.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "MatrixElement.h"
+#include "MatrixStaff.h"
+#include "QCanvasMatrixRectangle.h"
+#include "QCanvasMatrixDiamond.h"
+#include <qcanvas.h>
+#include <qpoint.h>
+#include <qwidget.h>
+#include "misc/Debug.h"
+
+
+
+namespace Rosegarden
+{
+
+MatrixCanvasView::MatrixCanvasView(MatrixStaff& staff,
+ SnapGrid *snapGrid,
+ bool drumMode,
+ QCanvas *viewing, QWidget *parent,
+ const char *name, WFlags f)
+ : RosegardenCanvasView(viewing, parent, name, f),
+ m_staff(staff),
+ m_snapGrid(snapGrid),
+ m_drumMode(drumMode),
+ m_previousEvTime(0),
+ m_previousEvPitch(0),
+ m_mouseWasPressed(false),
+ m_ignoreClick(false),
+ m_smoothModifier(Qt::ShiftButton),
+ m_lastSnap(SnapGrid::SnapToBeat),
+ m_isSnapTemporary(false)
+{
+ viewport()->setMouseTracking(true);
+}
+
+MatrixCanvasView::~MatrixCanvasView()
+{}
+
+void MatrixCanvasView::contentsMousePressEvent(QMouseEvent* e)
+{
+ QPoint p = inverseMapPoint(e->pos());
+
+ updateGridSnap(e);
+
+ MATRIX_DEBUG << "MatrixCanvasView::contentsMousePressEvent: snap time is " << m_snapGrid->getSnapTime(double(p.x())) << endl;
+
+ timeT evTime;
+
+ if (m_drumMode) {
+ evTime = m_snapGrid->snapX(p.x(), SnapGrid::SnapEither);
+ MATRIX_DEBUG << "MatrixCanvasView: drum mode: snapEither " << p.x() << " -> " << evTime << endl;
+ } else {
+ evTime = m_snapGrid->snapX(p.x(), SnapGrid::SnapLeft);
+ MATRIX_DEBUG << "MatrixCanvasView: normal mode: snapLeft " << p.x() << " -> " << evTime << endl;
+ }
+
+ int evPitch = m_staff.getHeightAtCanvasCoords(p.x(), p.y());
+
+ timeT emTime = m_staff.getSegment().getEndMarkerTime();
+ if (evTime > emTime)
+ evTime = emTime;
+ timeT esTime = m_staff.getSegment().getStartTime();
+ if (evTime < esTime)
+ evTime = esTime;
+
+// std::cerr << "MatrixCanvasView::contentsMousePressEvent() at pitch "
+// << evPitch << ", time " << evTime << std::endl;
+
+ QCanvasItemList itemList = canvas()->collisions(p);
+ QCanvasItemList::Iterator it;
+ MatrixElement* mel = 0;
+ QCanvasItem* activeItem = 0;
+
+ for (it = itemList.begin(); it != itemList.end(); ++it) {
+
+ QCanvasItem *item = *it;
+
+ QCanvasMatrixRectangle *mRect = 0;
+
+ if (item->active()) {
+ activeItem = item;
+ break;
+ }
+
+ if ((mRect = dynamic_cast<QCanvasMatrixRectangle*>(item))) {
+
+// std::cerr << "MatrixCanvasView: looking at element with rect " << mRect->rect().x() << "," << mRect->rect().y() << " (" << mRect->rect().width() << "x" << mRect->rect().height() << ")" << std::endl;
+
+// std::cerr << "MatrixCanvasView: point is " << p.x() << "," << p.y()<< std::endl;
+
+ QRect rect = mRect->rect();
+ if (dynamic_cast<QCanvasMatrixDiamond*>(mRect)) {
+ rect = QRect(rect.x() - rect.height()/2,
+ rect.y(),
+ rect.width(),
+ rect.height());
+ }
+
+// std::cerr << "MatrixCanvasView: adjusted rect " << rect.x() << "," << rect.y() << " (" << rect.width() << "x" << rect.height() << ")" << std::endl;
+
+ // QCanvas::collisions() can be a bit optimistic and report
+ // items which are close to the point but not actually under it.
+ // So a little sanity check helps.
+ if (!rect.contains(p, true)) continue;
+
+ mel = &(mRect->getMatrixElement());
+// std::cerr << "MatrixCanvasView::contentsMousePressEvent: collision with an existing matrix element" << std::endl;
+ break;
+ }
+ }
+
+ if (activeItem) { // active item takes precedence over notation elements
+ emit activeItemPressed(e, activeItem);
+ m_mouseWasPressed = true;
+ return ;
+ }
+
+ emit mousePressed(evTime, evPitch, e, mel);
+ m_mouseWasPressed = true;
+
+ // Ignore click if it was above the staff and not
+ // on an active item
+ //
+ if (!m_staff.containsCanvasCoords(p.x(), p.y()) && !activeItem)
+ m_ignoreClick = true;
+}
+
+void MatrixCanvasView::contentsMouseMoveEvent(QMouseEvent* e)
+{
+ QPoint p = inverseMapPoint(e->pos());
+ /*
+ if (m_snapGrid->getSnapTime(double(p.x())))
+ m_lastSnap = m_snapGrid->getSnapTime(double(p.x()));
+ */
+ updateGridSnap(e);
+
+ if (m_ignoreClick)
+ return ;
+
+ timeT evTime;
+ if (m_drumMode) {
+ evTime = m_snapGrid->snapX(p.x(), SnapGrid::SnapEither);
+ } else {
+ evTime = m_snapGrid->snapX(p.x(), SnapGrid::SnapLeft);
+ }
+
+ int evPitch = m_staff.getHeightAtCanvasCoords(p.x(), p.y());
+
+ timeT emTime = m_staff.getSegment().getEndMarkerTime();
+ if (evTime > emTime)
+ evTime = emTime;
+
+ timeT stTime = m_staff.getSegment().getStartTime();
+ if (evTime < stTime)
+ evTime = stTime;
+
+ if (evTime != m_previousEvTime) {
+ emit hoveredOverAbsoluteTimeChanged(evTime);
+ m_previousEvTime = evTime;
+ }
+
+ QCanvasItemList itemList = canvas()->collisions(p);
+ MatrixElement* mel = 0;
+
+ for (QCanvasItemList::iterator it = itemList.begin();
+ it != itemList.end(); ++it) {
+
+ QCanvasItem *item = *it;
+ QCanvasMatrixRectangle *mRect = 0;
+
+ if ((mRect = dynamic_cast<QCanvasMatrixRectangle*>(item))) {
+ if (!mRect->rect().contains(p, true))
+ continue;
+ mel = &(mRect->getMatrixElement());
+ MATRIX_DEBUG << "have element" << endl;
+ break;
+ }
+ }
+
+ if (!m_mouseWasPressed && // if mouse pressed, leave this to the tool
+ (evPitch != m_previousEvPitch || mel)) {
+ MidiPitchLabel label(evPitch);
+ if (mel) {
+ emit hoveredOverNoteChanged(evPitch, true,
+ mel->event()->getAbsoluteTime());
+ } else {
+ emit hoveredOverNoteChanged(evPitch, false, 0);
+ }
+ m_previousEvPitch = evPitch;
+ }
+
+// if (m_mouseWasPressed)
+ emit mouseMoved(evTime, evPitch, e);
+
+}
+
+void MatrixCanvasView::contentsMouseDoubleClickEvent (QMouseEvent* e)
+{
+ QPoint p = inverseMapPoint(e->pos());
+
+ if (!m_staff.containsCanvasCoords(p.x(), p.y())) {
+ m_ignoreClick = true;
+ return ;
+ }
+
+ contentsMousePressEvent(e);
+}
+
+void MatrixCanvasView::contentsMouseReleaseEvent(QMouseEvent* e)
+{
+ QPoint p = inverseMapPoint(e->pos());
+
+ if (m_ignoreClick) {
+ m_ignoreClick = false;
+ return ;
+ }
+
+ timeT evTime;
+ if (m_drumMode) {
+ evTime = m_snapGrid->snapX(p.x(), SnapGrid::SnapEither);
+ } else {
+ evTime = m_snapGrid->snapX(p.x(), SnapGrid::SnapLeft);
+ }
+
+ int evPitch = m_staff.getHeightAtCanvasCoords(p.x(), p.y());
+
+ timeT emTime = m_staff.getSegment().getEndMarkerTime();
+ if (evTime > emTime)
+ evTime = emTime;
+
+ emit mouseReleased(evTime, evPitch, e);
+ m_mouseWasPressed = false;
+}
+
+void MatrixCanvasView::slotExternalWheelEvent(QWheelEvent* e)
+{
+ wheelEvent(e);
+}
+
+void MatrixCanvasView::updateGridSnap(QMouseEvent *e)
+{
+ Qt::ButtonState bs = e->state();
+
+ // MATRIX_DEBUG << "MatrixCanvasView::updateGridSnap : bs = "
+ // << bs << " - sm = " << getSmoothModifier() << ", is temporary " << m_isSnapTemporary << ", saved is " << m_lastSnap << endl;
+
+ if (bs & getSmoothModifier()) {
+
+ if (!m_isSnapTemporary) {
+ m_lastSnap = m_snapGrid->getSnapSetting();
+ }
+ m_snapGrid->setSnapTime(SnapGrid::NoSnap);
+ m_isSnapTemporary = true;
+
+ } else if (m_isSnapTemporary) {
+
+ m_snapGrid->setSnapTime(m_lastSnap);
+ m_isSnapTemporary = false;
+ }
+}
+
+void MatrixCanvasView::enterEvent(QEvent *e)
+{
+ emit mouseEntered();
+}
+
+void MatrixCanvasView::leaveEvent(QEvent *e)
+{
+ emit mouseLeft();
+}
+
+}
+#include "MatrixCanvasView.moc"
diff --git a/src/gui/editors/matrix/MatrixCanvasView.h b/src/gui/editors/matrix/MatrixCanvasView.h
new file mode 100644
index 0000000..2ec4c7e
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixCanvasView.h
@@ -0,0 +1,162 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MATRIXCANVASVIEW_H_
+#define _RG_MATRIXCANVASVIEW_H_
+
+#include "gui/general/RosegardenCanvasView.h"
+#include "base/Event.h"
+
+
+class QWidget;
+class QWheelEvent;
+class QMouseEvent;
+class QCanvasItem;
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+class SnapGrid;
+class MatrixStaff;
+class MatrixElement;
+
+
+class MatrixCanvasView : public RosegardenCanvasView
+{
+ Q_OBJECT
+
+public:
+ MatrixCanvasView(MatrixStaff&,
+ SnapGrid *,
+ bool drumMode,
+ QCanvas *viewing,
+ QWidget *parent=0, const char *name=0, WFlags f=0);
+
+ ~MatrixCanvasView();
+
+ void setSmoothModifier(Qt::ButtonState s) { m_smoothModifier = s; }
+ Qt::ButtonState getSmoothModifier() { return m_smoothModifier; }
+
+signals:
+
+ /**
+ * Emitted when the user clicks on a QCanvasItem which is active
+ *
+ * @see QCanvasItem#setActive
+ */
+ void activeItemPressed(QMouseEvent*,
+ QCanvasItem* item);
+
+ /**
+ * Emitted when the mouse cursor moves to a different height
+ * on the staff. Returns the new pitch.
+ */
+ void hoveredOverNoteChanged(int evPitch, bool haveEvent,
+ timeT evTime);
+
+ /**
+ * Emitted when the mouse cursor moves to a note which is at a
+ * different time
+ *
+ * \a time is set to the absolute time of the note the cursor is
+ * hovering on
+ */
+ void hoveredOverAbsoluteTimeChanged(unsigned int time);
+
+ void mousePressed(timeT time, int pitch,
+ QMouseEvent*, MatrixElement*);
+
+ void mouseMoved(timeT time, int pitch, QMouseEvent*);
+
+ void mouseReleased(timeT time, int pitch, QMouseEvent*);
+
+ void mouseEntered();
+ void mouseLeft();
+
+public slots:
+ void slotExternalWheelEvent(QWheelEvent*);
+
+protected:
+ /**
+ * Callback for a mouse button press event in the canvas
+ */
+ virtual void contentsMousePressEvent(QMouseEvent*);
+
+ /**
+ * Callback for a mouse move event in the canvas
+ */
+ virtual void contentsMouseMoveEvent(QMouseEvent*);
+
+ /**
+ * Callback for a mouse button release event in the canvas
+ */
+ virtual void contentsMouseReleaseEvent(QMouseEvent*);
+
+ /**
+ * Callback for a mouse double-click event in the canvas
+ *
+ * NOTE: a double click event is always preceded by a mouse press
+ * event
+ */
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent*);
+
+ virtual void enterEvent(QEvent *);
+ virtual void leaveEvent(QEvent *);
+
+ /**
+ * Update the value of snap grid according to the button's state
+ *
+ * If the button was pressed with the 'smooth' modifier, set the
+ * grid so it won't snap time.
+ *
+ * @see #setSmoothModifier
+ * @see #getSmoothModifier
+ */
+ void updateGridSnap(QMouseEvent *e);
+
+ //--------------- Data members ---------------------------------
+
+ MatrixStaff &m_staff;
+ SnapGrid *m_snapGrid;
+ bool m_drumMode;
+
+ timeT m_previousEvTime;
+ int m_previousEvPitch;
+
+ bool m_mouseWasPressed;
+ bool m_ignoreClick;
+
+ Qt::ButtonState m_smoothModifier;
+ timeT m_lastSnap;
+ bool m_isSnapTemporary;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/MatrixElement.cpp b/src/gui/editors/matrix/MatrixElement.cpp
new file mode 100644
index 0000000..1101284
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixElement.cpp
@@ -0,0 +1,160 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MatrixElement.h"
+#include "misc/Debug.h"
+
+#include "base/Event.h"
+#include "base/NotationTypes.h"
+#include "base/ViewElement.h"
+#include "gui/general/GUIPalette.h"
+#include "QCanvasMatrixDiamond.h"
+#include "QCanvasMatrixRectangle.h"
+#include <qbrush.h>
+#include <qcanvas.h>
+#include <qcolor.h>
+
+
+namespace Rosegarden
+{
+
+MatrixElement::MatrixElement(Event *event, bool drum) :
+ ViewElement(event),
+ m_canvasRect(drum ?
+ new QCanvasMatrixDiamond(*this, 0) :
+ new QCanvasMatrixRectangle(*this, 0)),
+ m_overlapRectangles(NULL)
+{
+ // MATRIX_DEBUG << "new MatrixElement "
+ // << this << " wrapping " << event << endl;
+}
+
+MatrixElement::~MatrixElement()
+{
+ // MATRIX_DEBUG << "MatrixElement " << this << "::~MatrixElement() wrapping "
+ // << event() << endl;
+
+ m_canvasRect->hide();
+ delete m_canvasRect;
+
+ removeOverlapRectangles();
+}
+
+void MatrixElement::setCanvas(QCanvas* c)
+{
+ if (!m_canvasRect->canvas()) {
+
+ m_canvasRect->setCanvas(c);
+
+ // We set this by velocity now (matrixstaff.cpp)
+ //
+ //m_canvasRect->setBrush(RosegardenGUIColours::MatrixElementBlock);
+
+ m_canvasRect->setPen(GUIPalette::getColour(GUIPalette::MatrixElementBorder));
+ m_canvasRect->show();
+ }
+}
+
+bool MatrixElement::isNote() const
+{
+ return event()->isa(Note::EventType);
+}
+
+void MatrixElement::drawOverlapRectangles()
+{
+ if (m_overlapRectangles) removeOverlapRectangles();
+
+ QRect elRect = m_canvasRect->rect();
+ QCanvasItemList
+ itemList = m_canvasRect->canvas()->collisions(elRect);
+ QCanvasItemList::Iterator it;
+ MatrixElement* mel = 0;
+
+
+ for (it = itemList.begin(); it != itemList.end(); ++it) {
+
+ QCanvasMatrixRectangle *mRect = 0;
+ if ((mRect = dynamic_cast<QCanvasMatrixRectangle*>(*it))) {
+
+ // Element does'nt collide with itself
+ if (mRect == m_canvasRect) continue;
+
+ QRect rect = mRect->rect() & elRect;
+ if (!rect.isEmpty()) {
+ if (!m_overlapRectangles) {
+ m_overlapRectangles = new OverlapRectangles();
+ }
+
+ QCanvasRectangle *
+ overlap = new QCanvasRectangle(rect, m_canvasRect->canvas());
+ overlap->setBrush(GUIPalette::getColour(GUIPalette::MatrixOverlapBlock));
+ overlap->setZ(getCanvasZ() + 1);
+ overlap->show();
+ m_overlapRectangles->push_back(overlap);
+ }
+ }
+ }
+}
+
+void MatrixElement::redrawOverlaps(QRect rect)
+{
+ QCanvasItemList
+ itemList = m_canvasRect->canvas()->collisions(rect);
+ QCanvasItemList::Iterator it;
+ MatrixElement* mel = 0;
+
+ for (it = itemList.begin(); it != itemList.end(); ++it) {
+ QCanvasMatrixRectangle *mRect = 0;
+ if ((mRect = dynamic_cast<QCanvasMatrixRectangle*>(*it))) {
+ mRect->getMatrixElement().drawOverlapRectangles();
+ }
+ }
+}
+
+void MatrixElement::removeOverlapRectangles()
+{
+ if (!m_overlapRectangles) return;
+
+ OverlapRectangles::iterator it;
+ for (it = m_overlapRectangles->begin(); it != m_overlapRectangles->end(); ++it) {
+ (*it)->hide();
+ delete *it;
+ }
+
+ delete m_overlapRectangles;
+ m_overlapRectangles = NULL;
+}
+
+bool MatrixElement::getVisibleRectangle(QRect &rectangle)
+{
+ if (m_canvasRect && m_canvasRect->isVisible()) {
+ rectangle = m_canvasRect->rect();
+ return true;
+ }
+ return false;
+}
+
+
+}
diff --git a/src/gui/editors/matrix/MatrixElement.h b/src/gui/editors/matrix/MatrixElement.h
new file mode 100644
index 0000000..d330991
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixElement.h
@@ -0,0 +1,138 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MATRIXELEMENT_H_
+#define _RG_MATRIXELEMENT_H_
+
+#include "base/ViewElement.h"
+#include <qbrush.h>
+#include <qcanvas.h>
+#include "QCanvasMatrixRectangle.h"
+
+class QColor;
+
+
+namespace Rosegarden
+{
+
+class Event;
+
+class MatrixElement : public ViewElement
+{
+
+ typedef std::vector <QCanvasRectangle *> OverlapRectangles;
+
+
+public:
+ MatrixElement(Event *event, bool drum);
+
+ virtual ~MatrixElement();
+
+ void setCanvas(QCanvas* c);
+
+ /**
+ * Returns the actual x coordinate of the element on the canvas
+ */
+ double getCanvasX() const { return m_canvasRect->x(); }
+
+ /**
+ * Returns the actual y coordinate of the element on the canvas
+ */
+ double getCanvasY() const { return m_canvasRect->y(); }
+
+ double getCanvasZ() const { return m_canvasRect->z(); }
+
+ /**
+ * Sets the x coordinate of the element on the canvas
+ */
+ void setCanvasX(double x) { m_canvasRect->setX(x); }
+
+ /**
+ * Sets the y coordinate of the element on the canvas
+ */
+ void setCanvasY(double y) { m_canvasRect->setY(y); }
+
+ void setCanvasZ(double z) { m_canvasRect->setZ(z); }
+
+ /**
+ * Sets the width of the rectangle on the canvas
+ */
+ void setWidth(int w) { m_canvasRect->setSize(w, m_canvasRect->height()); }
+ int getWidth() { return m_canvasRect->width(); }
+
+ /**
+ * Sets the height of the rectangle on the canvas
+ */
+ void setHeight(int h) { m_canvasRect->setSize(m_canvasRect->width(), h); }
+ int getHeight() { return m_canvasRect->height(); }
+
+ /// Returns true if the wrapped event is a note
+ bool isNote() const;
+
+ /*
+ * Set the colour of the element
+ */
+ void setColour(const QColor &colour)
+ { m_canvasRect->setBrush(QBrush(colour)); }
+
+ /**
+ * Draws overlap rectangles (if any)
+ * (should not be called in drum mode)
+ */
+ void drawOverlapRectangles();
+
+ /**
+ * Removes overlap rectangles if any
+ */
+ void removeOverlapRectangles();
+
+ /**
+ * If element rectangle is currently visible gets its size and returns true.
+ * Returns false if element rectangle is undefined or not visible.
+ */
+ bool getVisibleRectangle(QRect &rectangle);
+
+ /**
+ * Redraw overlap rectangles of all matrix elements colliding with rect
+ */
+ void redrawOverlaps(QRect rect);
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ QCanvasMatrixRectangle *m_canvasRect;
+
+ OverlapRectangles *m_overlapRectangles;
+
+};
+
+
+typedef ViewElementList MatrixElementList;
+
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/MatrixEraser.cpp b/src/gui/editors/matrix/MatrixEraser.cpp
new file mode 100644
index 0000000..6c2373e
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixEraser.cpp
@@ -0,0 +1,110 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MatrixEraser.h"
+#include "misc/Debug.h"
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include "base/ViewElement.h"
+#include "commands/matrix/MatrixEraseCommand.h"
+#include "gui/general/EditTool.h"
+#include "MatrixStaff.h"
+#include "MatrixTool.h"
+#include "MatrixView.h"
+#include <kaction.h>
+#include <kglobal.h>
+#include <qiconset.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+MatrixEraser::MatrixEraser(MatrixView* parent)
+ : MatrixTool("MatrixEraser", parent),
+ m_currentStaff(0)
+{
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/select.xpm");
+ QIconSet icon = QIconSet(pixmap);
+
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ new KAction(i18n("Switch to Draw Tool"), "pencil", 0, this,
+ SLOT(slotDrawSelected()), actionCollection(),
+ "draw");
+
+ new KAction(i18n("Switch to Move Tool"), "move", 0, this,
+ SLOT(slotMoveSelected()), actionCollection(),
+ "move");
+
+ pixmap.load(pixmapDir + "/toolbar/resize.xpm");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Switch to Resize Tool"), icon, 0, this,
+ SLOT(slotResizeSelected()), actionCollection(),
+ "resize");
+
+ createMenu("matrixeraser.rc");
+}
+
+void MatrixEraser::handleLeftButtonPress(timeT,
+ int,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement* el)
+{
+ MATRIX_DEBUG << "MatrixEraser::handleLeftButtonPress : el = "
+ << el << endl;
+
+ if (!el)
+ return ; // nothing to erase
+
+ m_currentStaff = m_mParentView->getStaff(staffNo);
+
+ MatrixEraseCommand* command =
+ new MatrixEraseCommand(m_currentStaff->getSegment(), el->event());
+
+ m_mParentView->addCommandToHistory(command);
+
+ m_mParentView->update();
+}
+
+void MatrixEraser::ready()
+{
+ m_mParentView->setCanvasCursor(Qt::pointingHandCursor);
+ setBasicContextHelp();
+}
+
+void MatrixEraser::setBasicContextHelp()
+{
+ setContextHelp(i18n("Click on a note to delete it"));
+}
+
+const QString MatrixEraser::ToolName = "eraser";
+
+}
diff --git a/src/gui/editors/matrix/MatrixEraser.h b/src/gui/editors/matrix/MatrixEraser.h
new file mode 100644
index 0000000..4e3d65f
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixEraser.h
@@ -0,0 +1,69 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MATRIXERASER_H_
+#define _RG_MATRIXERASER_H_
+
+#include "MatrixTool.h"
+#include <qstring.h>
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class MatrixView;
+class MatrixStaff;
+
+
+class MatrixEraser : public MatrixTool
+{
+ friend class MatrixToolBox;
+
+public:
+
+ virtual void handleLeftButtonPress(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent *event,
+ ViewElement*);
+
+ static const QString ToolName;
+
+ virtual void ready();
+
+protected:
+ MatrixEraser(MatrixView*);
+
+ void setBasicContextHelp();
+
+ MatrixStaff* m_currentStaff;
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/MatrixHLayout.cpp b/src/gui/editors/matrix/MatrixHLayout.cpp
new file mode 100644
index 0000000..99b89c2
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixHLayout.cpp
@@ -0,0 +1,220 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MatrixHLayout.h"
+#include "MatrixElement.h"
+#include "misc/Debug.h"
+
+#include "base/Composition.h"
+#include "base/LayoutEngine.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/Segment.h"
+#include "base/Staff.h"
+#include "MatrixStaff.h"
+
+#include <cmath>
+
+
+namespace Rosegarden
+{
+
+MatrixHLayout::MatrixHLayout(Composition *c) :
+ HorizontalLayoutEngine(c),
+ m_totalWidth(0.0),
+ m_firstBar(0)
+{}
+
+MatrixHLayout::~MatrixHLayout()
+{}
+
+void MatrixHLayout::reset()
+{}
+
+void MatrixHLayout::resetStaff(Staff&, timeT, timeT)
+{}
+
+void MatrixHLayout::scanStaff(Staff &staffBase,
+ timeT startTime, timeT endTime)
+{
+ Profiler profiler("MatrixHLayout::scanStaff", true);
+
+ // The Matrix layout is not currently designed to be able to lay
+ // out more than one staff, because we have no requirement to show
+ // more than one at once in the Matrix view. To make it work for
+ // multiple staffs should be straightforward; we just need to bear
+ // in mind that they might start and end at different times (hence
+ // the total width and bar list can't just be calculated from the
+ // last staff scanned as they are now).
+
+ MatrixStaff &staff = static_cast<MatrixStaff &>(staffBase);
+ bool isFullScan = (startTime == endTime);
+
+ MatrixElementList *notes = staff.getViewElementList();
+ MatrixElementList::iterator startItr = notes->begin();
+ MatrixElementList::iterator endItr = notes->end();
+
+ if (!isFullScan) {
+ startItr = notes->findNearestTime(startTime);
+ if (startItr == notes->end())
+ startItr = notes->begin();
+ endItr = notes->findTime(endTime);
+ }
+
+ if (endItr == notes->end() && startItr == notes->begin()) {
+ isFullScan = true;
+ }
+
+ // Do this in two parts: bar lines separately from elements.
+ // (We don't need to do all that stuff notationhlayout has to do,
+ // scanning the notes bar-by-bar; we can just place the bar lines
+ // in the theoretically-correct places and do the same with the
+ // notes quite independently.)
+
+ Segment &segment = staff.getSegment();
+ Composition *composition = segment.getComposition();
+ m_firstBar = composition->getBarNumber(segment.getStartTime());
+ timeT from = composition->getBarStart(m_firstBar),
+ to = composition->getBarEndForTime(segment.getEndMarkerTime());
+
+ double startPosition = from;
+
+ // 1. Bar lines and time signatures. We only re-make these on
+ // full scans.
+
+ if (isFullScan || m_barData.size() == 0) {
+
+ m_barData.clear();
+ int barNo = m_firstBar;
+
+ MATRIX_DEBUG << "MatrixHLayout::scanStaff() : start time = " << startTime << ", first bar = " << m_firstBar << ", end time = " << endTime << ", end marker time = " << segment.getEndMarkerTime() << ", from = " << from << ", to = " << to << endl;
+
+ // hack for partial bars
+ //
+ timeT adjTo = to;
+
+ if (composition->getBarStartForTime(segment.getEndMarkerTime())
+ != segment.getEndMarkerTime())
+ adjTo++;
+
+ while (from < adjTo) {
+
+ bool isNew = false;
+ TimeSignature timeSig =
+ composition->getTimeSignatureInBar(barNo, isNew);
+
+ if (isNew || barNo == m_firstBar) {
+ m_barData.push_back(BarData((from - startPosition) *
+ staff.getTimeScaleFactor(),
+ TimeSigData(true, timeSig)));
+ } else {
+ m_barData.push_back(BarData((from - startPosition) *
+ staff.getTimeScaleFactor(),
+ TimeSigData(false, timeSig)));
+ }
+
+ from = composition->getBarEndForTime(from);
+ ++barNo;
+ }
+
+ m_barData.push_back(BarData(to * staff.getTimeScaleFactor(),
+ TimeSigData(false, TimeSignature())));
+ }
+
+ // 2. Elements
+
+ m_totalWidth = 0.0;
+ MatrixElementList::iterator i = startItr;
+
+ while (i != endItr) {
+
+ (*i)->setLayoutX(((*i)->getViewAbsoluteTime() - startPosition)
+ * staff.getTimeScaleFactor());
+
+ double width = (*i)->getViewDuration() * staff.getTimeScaleFactor();
+
+ // Make sure that very small elements can still be seen
+ //
+ if (width < 3) width = 3;
+ else width += 1; // fiddle factor
+
+ static_cast<MatrixElement*>((*i))->setWidth(lrint(width));
+
+ if (isFullScan) {
+ m_totalWidth = (*i)->getLayoutX() + width;
+ } else {
+ m_totalWidth = std::max(m_totalWidth, (*i)->getLayoutX() + width);
+ }
+
+ ++i;
+ }
+}
+
+double MatrixHLayout::getTotalWidth() const
+{
+ return m_totalWidth;
+}
+
+int MatrixHLayout::getFirstVisibleBar() const
+{
+ return m_firstBar;
+}
+
+int MatrixHLayout::getLastVisibleBar() const
+{
+ int barNo = m_firstBar + m_barData.size() - 2;
+ if (barNo < m_firstBar + 1)
+ barNo = m_firstBar + 1;
+
+ return barNo;
+}
+
+double MatrixHLayout::getBarPosition(int barNo) const
+{
+ if (barNo < getFirstVisibleBar()) {
+ return getBarPosition(getFirstVisibleBar());
+ }
+
+ if (barNo > getLastVisibleBar()) {
+ return getBarPosition(getLastVisibleBar());
+ }
+
+ return m_barData[barNo - m_firstBar].first;
+}
+
+bool MatrixHLayout::getTimeSignaturePosition(Staff &,
+ int barNo,
+ TimeSignature &timeSig,
+ double &timeSigX)
+{
+ timeSig = m_barData[barNo - m_firstBar].second.second;
+ timeSigX = m_barData[barNo - m_firstBar].first;
+ return m_barData[barNo - m_firstBar].second.first;
+}
+
+void MatrixHLayout::finishLayout(timeT, timeT)
+{}
+
+}
diff --git a/src/gui/editors/matrix/MatrixHLayout.h b/src/gui/editors/matrix/MatrixHLayout.h
new file mode 100644
index 0000000..76f1b31
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixHLayout.h
@@ -0,0 +1,150 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MATRIXHLAYOUT_H_
+#define _RG_MATRIXHLAYOUT_H_
+
+#include "base/FastVector.h"
+#include "base/LayoutEngine.h"
+#include <utility>
+#include "base/Event.h"
+
+#include "gui/general/HZoomable.h"
+
+
+
+namespace Rosegarden
+{
+
+class TimeSignature;
+class Staff;
+class Composition;
+
+
+class MatrixHLayout : public HorizontalLayoutEngine
+{
+public:
+ MatrixHLayout(Composition *c);
+ virtual ~MatrixHLayout();
+
+ /**
+ * Resets internal data stores for all staffs
+ */
+ virtual void reset();
+
+ /**
+ * Resets internal data stores for a specific staff
+ */
+ virtual void resetStaff(Staff &staff,
+ timeT = 0,
+ timeT = 0);
+
+ /**
+ * Returns the total length of all elements once layout is done.
+ * This is the x-coord of the end of the last element on the
+ * longest staff
+ */
+ virtual double getTotalWidth() const;
+
+ /**
+ * Returns the number of the first visible bar line
+ */
+ virtual int getFirstVisibleBar() const;
+
+ /**
+ * Returns the number of the first visible bar line
+ */
+ virtual int getLastVisibleBar() const;
+
+ /**
+ * Returns the x-coordinate of the given bar number
+ */
+ virtual double getBarPosition(int barNo) const;
+
+ /**
+ * Precomputes layout data for a single staff, updating any
+ * internal data stores associated with that staff and updating
+ * any layout-related properties in the events on the staff's
+ * segment.
+ */
+ virtual void scanStaff(Staff&,
+ timeT = 0,
+ timeT = 0);
+
+ /**
+ * Computes any layout data that may depend on the results of
+ * scanning more than one staff. This may mean doing most of
+ * the layout (likely for horizontal layout) or nothing at all
+ * (likely for vertical layout).
+ */
+ virtual void finishLayout(timeT = 0,
+ timeT = 0);
+
+ /**
+ * Returns true if there is a new time signature in the given bar,
+ * setting timeSignature appropriately and setting timeSigX to its
+ * x-coord
+ */
+ virtual bool getTimeSignaturePosition(Staff &staff,
+ int barNo,
+ TimeSignature &timeSig,
+ double &timeSigX);
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ // pair of has-time-sig and time-sig
+ typedef std::pair<bool, TimeSignature> TimeSigData;
+ // pair of layout-x and time-signature if there is one
+ typedef std::pair<double, TimeSigData> BarData;
+ typedef FastVector<BarData> BarDataList;
+ BarDataList m_barData;
+ double m_totalWidth;
+ int m_firstBar;
+};
+
+/**
+ * "zoomable" version of the above, used in the MatrixView
+ * to properly scale Tempo and Chord rulers.
+ *
+ */
+class ZoomableMatrixHLayoutRulerScale : public RulerScale, public HZoomable {
+public:
+ ZoomableMatrixHLayoutRulerScale(MatrixHLayout& layout) : RulerScale(layout.getComposition()), m_referenceHLayout(layout) {};
+
+ virtual double getBarPosition(int n) const { return m_referenceHLayout.getBarPosition(n) * getHScaleFactor(); }
+ virtual double getXForTime(timeT time) const { return m_referenceHLayout.getXForTime(time) * getHScaleFactor(); }
+ virtual timeT getTimeForX(double x) const { return m_referenceHLayout.getTimeForX(x / getHScaleFactor()); }
+ virtual double getBarWidth(int n) const { return m_referenceHLayout.getBarWidth(n) * getHScaleFactor(); }
+ virtual int getLastVisibleBar() const { return m_referenceHLayout.getLastVisibleBar(); }
+
+protected:
+ MatrixHLayout& m_referenceHLayout;
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/MatrixMover.cpp b/src/gui/editors/matrix/MatrixMover.cpp
new file mode 100644
index 0000000..d725f16
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixMover.cpp
@@ -0,0 +1,481 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MatrixMover.h"
+
+#include "base/BaseProperties.h"
+#include <klocale.h>
+#include <kstddirs.h>
+#include "base/Event.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/SnapGrid.h"
+#include "base/ViewElement.h"
+#include "commands/matrix/MatrixModifyCommand.h"
+#include "commands/matrix/MatrixInsertionCommand.h"
+#include "commands/notation/NormalizeRestsCommand.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "MatrixElement.h"
+#include "MatrixStaff.h"
+#include "MatrixTool.h"
+#include "MatrixView.h"
+#include "MatrixVLayout.h"
+#include <kaction.h>
+#include <kglobal.h>
+#include <qiconset.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include "misc/Debug.h"
+
+
+namespace Rosegarden
+{
+
+MatrixMover::MatrixMover(MatrixView* parent) :
+ MatrixTool("MatrixMover", parent),
+ m_currentElement(0),
+ m_currentStaff(0),
+ m_lastPlayedPitch(-1)
+{
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/select.xpm");
+ QIconSet icon = QIconSet(pixmap);
+
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ new KAction(i18n("Switch to Draw Tool"), "pencil", 0, this,
+ SLOT(slotDrawSelected()), actionCollection(),
+ "draw");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ pixmap.load(pixmapDir + "/toolbar/resize.xpm");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Switch to Resize Tool"), icon, 0, this,
+ SLOT(slotResizeSelected()), actionCollection(),
+ "resize");
+
+ createMenu("matrixmover.rc");
+}
+
+void MatrixMover::handleEventRemoved(Event *event)
+{
+ if (m_currentElement && m_currentElement->event() == event) {
+ m_currentElement = 0;
+ }
+}
+
+void MatrixMover::handleLeftButtonPress(timeT time,
+ int pitch,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement* el)
+{
+ MATRIX_DEBUG << "MatrixMover::handleLeftButtonPress() : time = " << time << ", el = " << el << endl;
+ if (!el) return;
+
+ m_quickCopy = (e->state() & Qt::ControlButton);
+
+ if (!m_duplicateElements.empty()) {
+ for (size_t i = 0; i < m_duplicateElements.size(); ++i) {
+ delete m_duplicateElements[i]->event();
+ delete m_duplicateElements[i];
+ }
+ m_duplicateElements.clear();
+ }
+
+ m_currentElement = dynamic_cast<MatrixElement*>(el);
+ m_currentStaff = m_mParentView->getStaff(staffNo);
+
+ if (m_currentElement) {
+
+ // Add this element and allow movement
+ //
+ EventSelection* selection = m_mParentView->getCurrentSelection();
+
+ if (selection) {
+ EventSelection *newSelection;
+
+ if ((e->state() & Qt::ShiftButton) ||
+ selection->contains(m_currentElement->event()))
+ newSelection = new EventSelection(*selection);
+ else
+ newSelection = new EventSelection(m_currentStaff->getSegment());
+
+ // if the selection already contains the event, remove it from the
+ // selection if shift is pressed
+ if (selection->contains(m_currentElement->event())){
+ if (e->state() & Qt::ShiftButton)
+ newSelection->removeEvent(m_currentElement->event());
+ } else {
+ newSelection->addEvent(m_currentElement->event());
+ }
+ m_mParentView->setCurrentSelection(newSelection, true, true);
+ m_mParentView->canvas()->update();
+ selection = newSelection;
+ } else {
+ m_mParentView->setSingleSelectedEvent(m_currentStaff->getSegment(),
+ m_currentElement->event(),
+ true);
+ m_mParentView->canvas()->update();
+ }
+
+ long velocity = m_mParentView->getCurrentVelocity();
+ m_currentElement->event()->get<Int>(BaseProperties::VELOCITY, velocity);
+ m_mParentView->playNote(m_currentStaff->getSegment(), pitch, velocity);
+ m_lastPlayedPitch = pitch;
+
+ if (m_quickCopy && selection) {
+ for (EventSelection::eventcontainer::iterator i =
+ selection->getSegmentEvents().begin();
+ i != selection->getSegmentEvents().end(); ++i) {
+
+ MatrixElement *element = m_currentStaff->getElement(*i);
+ if (!element) continue;
+
+ MatrixElement *duplicate = new MatrixElement
+ (new Event(**i), m_mParentView->isDrumMode());
+ duplicate->setLayoutY(element->getLayoutY());
+ duplicate->setLayoutX(element->getLayoutX());
+ duplicate->setWidth(element->getWidth());
+ duplicate->setHeight(element->getHeight());
+ duplicate->setCanvasZ(-1);
+ m_currentStaff->positionElement(duplicate);
+ m_duplicateElements.push_back(duplicate);
+ }
+ }
+ }
+
+ m_clickX = m_mParentView->inverseMapPoint(e->pos()).x();
+}
+
+timeT
+MatrixMover::getDragTime(QMouseEvent *e, timeT candidate)
+{
+ int x = m_mParentView->inverseMapPoint(e->pos()).x();
+ int xdiff = x - m_clickX;
+
+ const SnapGrid &grid = getSnapGrid();
+ const RulerScale &scale = *grid.getRulerScale();
+
+ timeT eventTime = m_currentElement->getViewAbsoluteTime();
+ int eventX = scale.getXForTime(eventTime);
+ timeT preSnapTarget = scale.getTimeForX(eventX + xdiff);
+
+ candidate = grid.snapTime(preSnapTarget, SnapGrid::SnapEither);
+
+ if (xdiff == 0 ||
+ (abs(eventTime - preSnapTarget) < abs(candidate - preSnapTarget))) {
+ candidate = eventTime;
+ }
+
+ return candidate;
+}
+
+int MatrixMover::handleMouseMove(timeT newTime,
+ int newPitch,
+ QMouseEvent *e)
+{
+ MATRIX_DEBUG << "MatrixMover::handleMouseMove() time = "
+ << newTime << endl;
+
+ if (e) {
+ setBasicContextHelp(e->state() & Qt::ControlButton);
+ }
+
+ if (!m_currentElement || !m_currentStaff)
+ return RosegardenCanvasView::NoFollow;
+
+ if (getSnapGrid().getSnapSetting() != SnapGrid::NoSnap) {
+ setContextHelp(i18n("Hold Shift to avoid snapping to beat grid"));
+ } else {
+ clearContextHelp();
+ }
+
+ if (e) newTime = getDragTime(e, newTime);
+
+ emit hoveredOverNoteChanged(newPitch, true, newTime);
+
+ using BaseProperties::PITCH;
+ int diffPitch = 0;
+ if (m_currentElement->event()->has(PITCH)) {
+ diffPitch = newPitch - m_currentElement->event()->get<Int>(PITCH);
+ }
+
+ int diffY =
+ int(((m_currentStaff->getLayoutYForHeight(newPitch) -
+ m_currentStaff->getElementHeight() / 2) -
+ m_currentElement->getLayoutY()));
+
+ EventSelection* selection = m_mParentView->getCurrentSelection();
+ EventSelection::eventcontainer::iterator it =
+ selection->getSegmentEvents().begin();
+
+ MatrixElement *element = 0;
+ int maxY = m_currentStaff->getCanvasYForHeight(0);
+
+ for (; it != selection->getSegmentEvents().end(); it++) {
+ element = m_currentStaff->getElement(*it);
+
+ if (element) {
+
+ timeT diffTime = element->getViewAbsoluteTime() -
+ m_currentElement->getViewAbsoluteTime();
+
+ int newX = getSnapGrid().getRulerScale()->
+ getXForTime(newTime + diffTime);
+
+ if (newX < 0) newX = 0;
+
+ int newY = int(element->getLayoutY() + diffY);
+
+ if (newY < 0) newY = 0;
+ if (newY > maxY) newY = maxY;
+
+ element->setLayoutX(newX);
+ element->setLayoutY(newY);
+
+ m_currentStaff->positionElement(element);
+ }
+ }
+
+ if (newPitch != m_lastPlayedPitch) {
+ long velocity = m_mParentView->getCurrentVelocity();
+ m_currentElement->event()->get<Int>(BaseProperties::VELOCITY, velocity);
+ m_mParentView->playNote(m_currentStaff->getSegment(), newPitch, velocity);
+ m_lastPlayedPitch = newPitch;
+ }
+
+ m_mParentView->canvas()->update();
+ return RosegardenCanvasView::FollowHorizontal |
+ RosegardenCanvasView::FollowVertical;
+}
+
+void MatrixMover::handleMouseRelease(timeT newTime,
+ int newPitch,
+ QMouseEvent *e)
+{
+ MATRIX_DEBUG << "MatrixMover::handleMouseRelease() - newPitch = "
+ << newPitch << endl;
+
+ if (!m_currentElement || !m_currentStaff)
+ return;
+
+ if (newPitch > MatrixVLayout::maxMIDIPitch)
+ newPitch = MatrixVLayout::maxMIDIPitch;
+ if (newPitch < 0)
+ newPitch = 0;
+
+ if (e) newTime = getDragTime(e, newTime);
+
+ using BaseProperties::PITCH;
+ timeT diffTime = newTime - m_currentElement->getViewAbsoluteTime();
+ int diffPitch = 0;
+ if (m_currentElement->event()->has(PITCH)) {
+ diffPitch = newPitch - m_currentElement->event()->get<Int>(PITCH);
+ }
+
+ EventSelection *selection = m_mParentView->getCurrentSelection();
+
+ if ((diffTime == 0 && diffPitch == 0) || selection->getAddedEvents() == 0) {
+ for (size_t i = 0; i < m_duplicateElements.size(); ++i) {
+ delete m_duplicateElements[i]->event();
+ delete m_duplicateElements[i];
+ }
+ m_duplicateElements.clear();
+ m_mParentView->canvas()->update();
+ m_currentElement = 0;
+ return;
+ }
+
+ if (newPitch != m_lastPlayedPitch) {
+ long velocity = m_mParentView->getCurrentVelocity();
+ m_currentElement->event()->get<Int>(BaseProperties::VELOCITY, velocity);
+ m_mParentView->playNote(m_currentStaff->getSegment(), newPitch, velocity);
+ m_lastPlayedPitch = newPitch;
+ }
+
+ QString commandLabel;
+ if (m_quickCopy) {
+ if (selection->getAddedEvents() < 2) {
+ commandLabel = i18n("Copy and Move Event");
+ } else {
+ commandLabel = i18n("Copy and Move Events");
+ }
+ } else {
+ if (selection->getAddedEvents() < 2) {
+ commandLabel = i18n("Move Event");
+ } else {
+ commandLabel = i18n("Move Events");
+ }
+ }
+
+ KMacroCommand *macro = new KMacroCommand(commandLabel);
+
+ EventSelection::eventcontainer::iterator it =
+ selection->getSegmentEvents().begin();
+
+ Segment &segment = m_currentStaff->getSegment();
+
+ EventSelection *newSelection = new EventSelection(segment);
+
+ timeT normalizeStart = selection->getStartTime();
+ timeT normalizeEnd = selection->getEndTime();
+
+ if (m_quickCopy) {
+ for (size_t i = 0; i < m_duplicateElements.size(); ++i) {
+ timeT time = m_duplicateElements[i]->getViewAbsoluteTime();
+ timeT endTime = time + m_duplicateElements[i]->getViewDuration();
+ if (time < normalizeStart) normalizeStart = time;
+ if (endTime > normalizeEnd) normalizeEnd = endTime;
+ macro->addCommand(new MatrixInsertionCommand
+ (segment, time, endTime,
+ m_duplicateElements[i]->event()));
+ delete m_duplicateElements[i]->event();
+ delete m_duplicateElements[i];
+ }
+ m_duplicateElements.clear();
+ m_quickCopy = false;
+ }
+
+ for (; it != selection->getSegmentEvents().end(); it++) {
+
+ timeT newTime = (*it)->getAbsoluteTime() + diffTime;
+
+ int newPitch = 60;
+ if ((*it)->has(PITCH)) {
+ newPitch = (*it)->get<Int>(PITCH) + diffPitch;
+ }
+
+ Event *newEvent = 0;
+
+ if (newTime < segment.getStartTime()) {
+ newTime = segment.getStartTime();
+ }
+
+ if (newTime + (*it)->getDuration() >= segment.getEndMarkerTime()) {
+ timeT limit = getSnapGrid().snapTime
+ (segment.getEndMarkerTime() - 1, SnapGrid::SnapLeft);
+ if (newTime > limit) newTime = limit;
+ timeT newDuration = std::min
+ ((*it)->getDuration(), segment.getEndMarkerTime() - newTime);
+ newEvent = new Event(**it, newTime, newDuration);
+ } else {
+ newEvent = new Event(**it, newTime);
+ }
+
+ newEvent->set<Int>(BaseProperties::PITCH, newPitch);
+
+ macro->addCommand(new MatrixModifyCommand(segment,
+ (*it),
+ newEvent,
+ true,
+ false));
+ newSelection->addEvent(newEvent);
+ }
+
+ normalizeStart = std::min(normalizeStart, newSelection->getStartTime());
+ normalizeEnd = std::max(normalizeEnd, newSelection->getEndTime());
+
+ macro->addCommand(new NormalizeRestsCommand(segment,
+ normalizeStart,
+ normalizeEnd));
+
+ m_mParentView->setCurrentSelection(0, false, false);
+ m_mParentView->addCommandToHistory(macro);
+ m_mParentView->setCurrentSelection(newSelection, false, false);
+
+ m_mParentView->canvas()->update();
+ m_currentElement = 0;
+
+ setBasicContextHelp();
+}
+
+void MatrixMover::ready()
+{
+ connect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotMatrixScrolled(int, int)));
+ connect(this, SIGNAL(hoveredOverNoteChanged(int, bool, timeT)),
+ m_mParentView, SLOT(slotHoveredOverNoteChanged(int, bool, timeT)));
+ m_mParentView->setCanvasCursor(Qt::sizeAllCursor);
+ setBasicContextHelp();
+}
+
+void MatrixMover::stow()
+{
+ disconnect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotMatrixScrolled(int, int)));
+ disconnect(this, SIGNAL(hoveredOverNoteChanged(int, bool, timeT)),
+ m_mParentView, SLOT(slotHoveredOverNoteChanged(int, bool, timeT)));
+}
+
+void MatrixMover::slotMatrixScrolled(int newX, int newY)
+{
+ if (!m_currentElement)
+ return ;
+
+ QPoint newP1(newX, newY), oldP1(m_parentView->getCanvasView()->contentsX(),
+ m_parentView->getCanvasView()->contentsY());
+
+ QPoint offset = newP1 - oldP1;
+
+ offset = m_mParentView->inverseMapPoint(offset);
+
+ QPoint p(m_currentElement->getCanvasX(), m_currentElement->getCanvasY());
+ p += offset;
+
+ timeT newTime = getSnapGrid().snapX(p.x());
+ int newPitch = m_currentStaff->getHeightAtCanvasCoords(p.x(), p.y());
+
+ handleMouseMove(newTime, newPitch, 0);
+}
+
+void MatrixMover::setBasicContextHelp(bool ctrlPressed)
+{
+ EventSelection *selection = m_mParentView->getCurrentSelection();
+ if (!selection || selection->getAddedEvents() < 2) {
+ if (!ctrlPressed) {
+ setContextHelp(i18n("Click and drag to move a note; hold Ctrl as well to copy it"));
+ } else {
+ setContextHelp(i18n("Click and drag to copy a note"));
+ }
+ } else {
+ if (!ctrlPressed) {
+ setContextHelp(i18n("Click and drag to move selected notes; hold Ctrl as well to copy"));
+ } else {
+ setContextHelp(i18n("Click and drag to copy selected notes"));
+ }
+ }
+}
+
+const QString MatrixMover::ToolName = "mover";
+
+}
+#include "MatrixMover.moc"
diff --git a/src/gui/editors/matrix/MatrixMover.h b/src/gui/editors/matrix/MatrixMover.h
new file mode 100644
index 0000000..ac95c5f
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixMover.h
@@ -0,0 +1,112 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MATRIXMOVER_H_
+#define _RG_MATRIXMOVER_H_
+
+#include "MatrixTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class MatrixView;
+class MatrixStaff;
+class MatrixElement;
+class Event;
+
+
+class MatrixMover : public MatrixTool
+{
+ Q_OBJECT
+
+ friend class MatrixToolBox;
+
+public:
+ virtual void handleLeftButtonPress(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent *event,
+ ViewElement*);
+
+ /**
+ * Set the duration of the element
+ */
+ virtual int handleMouseMove(timeT,
+ int height,
+ QMouseEvent*);
+
+ /**
+ * Actually insert the new element
+ */
+ virtual void handleMouseRelease(timeT,
+ int height,
+ QMouseEvent*);
+
+ static const QString ToolName;
+
+ /**
+ * Respond to an event being deleted -- it may be the one the tool
+ * is remembering as the current event.
+ */
+ virtual void handleEventRemoved(Event *event);
+
+ virtual void ready();
+ virtual void stow();
+
+signals:
+ void hoveredOverNoteChanged(int evPitch, bool haveEvent, timeT evTime);
+
+protected slots:
+ void slotMatrixScrolled(int x, int y);
+
+protected:
+ MatrixMover(MatrixView*);
+
+ void setBasicContextHelp(bool ctrlPressed = false);
+
+ timeT getDragTime(QMouseEvent *e, timeT candidate);
+
+ MatrixElement* m_currentElement;
+ MatrixStaff* m_currentStaff;
+
+ std::vector<MatrixElement *> m_duplicateElements;
+ bool m_quickCopy;
+
+ int m_lastPlayedPitch;
+ int m_clickX;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/MatrixPainter.cpp b/src/gui/editors/matrix/MatrixPainter.cpp
new file mode 100644
index 0000000..be63bd7
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixPainter.cpp
@@ -0,0 +1,370 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MatrixPainter.h"
+
+#include "base/BaseProperties.h"
+#include <klocale.h>
+#include <kstddirs.h>
+#include "base/Event.h"
+#include "base/NotationTypes.h"
+#include "base/SegmentMatrixHelper.h"
+#include "base/SnapGrid.h"
+#include "base/ViewElement.h"
+#include "commands/matrix/MatrixInsertionCommand.h"
+#include "commands/matrix/MatrixEraseCommand.h"
+#include "commands/matrix/MatrixPercussionInsertionCommand.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "MatrixElement.h"
+#include "MatrixStaff.h"
+#include "MatrixTool.h"
+#include "MatrixView.h"
+#include <kaction.h>
+#include <kglobal.h>
+#include <qiconset.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include "misc/Debug.h"
+
+
+namespace Rosegarden
+{
+
+MatrixPainter::MatrixPainter(MatrixView* parent)
+ : MatrixTool("MatrixPainter", parent),
+ m_currentElement(0),
+ m_currentStaff(0)
+{
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/select.xpm");
+ QIconSet icon = QIconSet(pixmap);
+
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ new KAction(i18n("Switch to Move Tool"), "move", 0, this,
+ SLOT(slotMoveSelected()), actionCollection(),
+ "move");
+
+ pixmap.load(pixmapDir + "/toolbar/resize.xpm");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Switch to Resize Tool"), icon, 0, this,
+ SLOT(slotResizeSelected()), actionCollection(),
+ "resize");
+
+ createMenu("matrixpainter.rc");
+}
+
+MatrixPainter::MatrixPainter(QString name, MatrixView* parent)
+ : MatrixTool(name, parent),
+ m_currentElement(0),
+ m_currentStaff(0)
+{}
+
+void MatrixPainter::handleEventRemoved(Event *event)
+{
+ if (m_currentElement && m_currentElement->event() == event) {
+ m_currentElement = 0;
+ }
+}
+
+void MatrixPainter::handleLeftButtonPress(timeT time,
+ int pitch,
+ int staffNo,
+ QMouseEvent *e,
+ ViewElement *element)
+{
+ MATRIX_DEBUG << "MatrixPainter::handleLeftButtonPress : pitch = "
+ << pitch << ", time : " << time << endl;
+
+ QPoint p = m_mParentView->inverseMapPoint(e->pos());
+
+ m_currentStaff = m_mParentView->getStaff(staffNo);
+
+ // Don't create an overlapping event on the same note on the same channel
+ if (dynamic_cast<MatrixElement*>(element)) {
+ std::cerr << "MatrixPainter::handleLeftButtonPress : overlap with an other matrix element" << std::endl;
+ // In percussion matrix, we delete the existing event rather
+ // than just ignoring it -- this is reasonable as the event
+ // has no meaningful duration, so we can just toggle it on and
+ // off with repeated clicks
+ if (m_mParentView->isDrumMode()) {
+ if (element->event()) {
+ MatrixEraseCommand *command =
+ new MatrixEraseCommand(m_currentStaff->getSegment(),
+ element->event());
+ m_mParentView->addCommandToHistory(command);
+ }
+ }
+ m_currentElement = 0;
+ return ;
+ }
+
+ // This is needed for the event duration rounding
+ SnapGrid grid(getSnapGrid());
+
+ Event *ev = new Event(Note::EventType, time,
+ grid.getSnapTime(double(p.x())));
+ ev->set<Int>(BaseProperties::PITCH, pitch);
+ ev->set<Int>(BaseProperties::VELOCITY, m_mParentView->getCurrentVelocity());
+
+ m_currentElement = new MatrixElement(ev, m_mParentView->isDrumMode());
+
+ int y = m_currentStaff->getLayoutYForHeight(pitch) -
+ m_currentStaff->getElementHeight() / 2;
+
+ m_currentElement->setLayoutY(y);
+ m_currentElement->setLayoutX(grid.getRulerScale()->getXForTime(time));
+ m_currentElement->setHeight(m_currentStaff->getElementHeight());
+
+ int width = grid.getRulerScale()->getXForTime(time + ev->getDuration())
+ - m_currentElement->getLayoutX() + 1;
+
+ m_currentElement->setWidth(width);
+
+ m_currentStaff->positionElement(m_currentElement);
+ m_mParentView->update();
+
+ // preview
+ m_mParentView->playNote(ev);
+}
+
+int MatrixPainter::handleMouseMove(timeT time,
+ int pitch,
+ QMouseEvent *e)
+{
+ // sanity check
+ if (!m_currentElement)
+ return RosegardenCanvasView::NoFollow;
+
+ if (getSnapGrid().getSnapSetting() != SnapGrid::NoSnap) {
+ setContextHelp(i18n("Hold Shift to avoid snapping to beat grid"));
+ } else {
+ clearContextHelp();
+ }
+
+ // We don't want to use the time passed in, because it's snapped
+ // to the left and we want a more particular policy
+
+ if (e) {
+ QPoint p = m_mParentView->inverseMapPoint(e->pos());
+ time = getSnapGrid().snapX(p.x(), SnapGrid::SnapEither);
+ if (time >= m_currentElement->getViewAbsoluteTime()) {
+ time = getSnapGrid().snapX(p.x(), SnapGrid::SnapRight);
+ } else {
+ time = getSnapGrid().snapX(p.x(), SnapGrid::SnapLeft);
+ }
+ }
+
+ MATRIX_DEBUG << "MatrixPainter::handleMouseMove : pitch = "
+ << pitch << ", time : " << time << endl;
+
+ using BaseProperties::PITCH;
+
+ if (time == m_currentElement->getViewAbsoluteTime()) {
+ time =
+ m_currentElement->getViewAbsoluteTime() +
+ m_currentElement->getViewDuration();
+ }
+
+ int width = getSnapGrid().getRulerScale()->getXForTime(time)
+ - getSnapGrid().getRulerScale()->getXForTime
+ (m_currentElement->getViewAbsoluteTime()) + 1;
+
+ m_currentElement->setWidth(width);
+// std::cerr << "current element width "<< width << std::endl;
+
+ if (m_currentElement->event()->has(PITCH) &&
+ pitch != m_currentElement->event()->get<Int>(PITCH)) {
+
+ m_currentElement->event()->set<Int>(PITCH, pitch);
+
+ int y = m_currentStaff->getLayoutYForHeight(pitch) -
+ m_currentStaff->getElementHeight() / 2;
+
+ m_currentElement->setLayoutY(y);
+
+ m_currentStaff->positionElement(m_currentElement);
+
+ // preview
+ m_mParentView->playNote(m_currentElement->event());
+ }
+
+ m_mParentView->update();
+
+ return RosegardenCanvasView::FollowHorizontal |
+ RosegardenCanvasView::FollowVertical;
+}
+
+void MatrixPainter::handleMouseRelease(timeT endTime,
+ int,
+ QMouseEvent *e)
+{
+ // This can happen in case of screen/window capture -
+ // we only get a mouse release, the window snapshot tool
+ // got the mouse down
+ if (!m_currentElement)
+ return ;
+
+ // We don't want to use the time passed in, because it's snapped
+ // to the left and we want a more particular policy
+
+ if (e) {
+ QPoint p = m_mParentView->inverseMapPoint(e->pos());
+ endTime = getSnapGrid().snapX(p.x(), SnapGrid::SnapEither);
+ if (endTime >= m_currentElement->getViewAbsoluteTime()) {
+ endTime = getSnapGrid().snapX(p.x(), SnapGrid::SnapRight);
+ } else {
+ endTime = getSnapGrid().snapX(p.x(), SnapGrid::SnapLeft);
+ }
+ }
+
+ timeT time = m_currentElement->getViewAbsoluteTime();
+ timeT segmentEndTime = m_currentStaff->getSegment().getEndMarkerTime();
+
+ if (m_mParentView->isDrumMode()) {
+
+ if (time > segmentEndTime)
+ time = segmentEndTime;
+
+ MatrixPercussionInsertionCommand *command =
+ new MatrixPercussionInsertionCommand(m_currentStaff->getSegment(),
+ time,
+ m_currentElement->event());
+ m_mParentView->addCommandToHistory(command);
+
+ Event* ev = m_currentElement->event();
+ delete m_currentElement;
+ delete ev;
+
+ ev = command->getLastInsertedEvent();
+ if (ev)
+ m_mParentView->setSingleSelectedEvent(m_currentStaff->getSegment(),
+ ev);
+ } else {
+
+ // Insert element if it has a non null duration,
+ // discard it otherwise
+ //
+ if (time > endTime)
+ std::swap(time, endTime);
+
+ if (endTime == time)
+ endTime = time + m_currentElement->getViewDuration();
+
+ if (time < segmentEndTime) {
+
+ if (endTime > segmentEndTime)
+ endTime = segmentEndTime;
+
+ SegmentMatrixHelper helper(m_currentStaff->getSegment());
+ MATRIX_DEBUG << "MatrixPainter::handleMouseRelease() : helper.insertNote()" << endl;
+
+ MatrixInsertionCommand* command =
+ new MatrixInsertionCommand(m_currentStaff->getSegment(),
+ time,
+ endTime,
+ m_currentElement->event());
+
+ m_mParentView->addCommandToHistory(command);
+
+ Event* ev = m_currentElement->event();
+ delete m_currentElement;
+ delete ev;
+
+ ev = command->getLastInsertedEvent();
+ if (ev)
+ m_mParentView->setSingleSelectedEvent(m_currentStaff->getSegment(),
+ ev);
+ } else {
+
+ Event* ev = m_currentElement->event();
+ delete m_currentElement;
+ delete ev;
+ }
+ }
+
+ m_mParentView->update();
+ m_currentElement = 0;
+
+ setBasicContextHelp();
+}
+
+void MatrixPainter::ready()
+{
+ connect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotMatrixScrolled(int, int)));
+
+ m_mParentView->setCanvasCursor(Qt::crossCursor);
+
+ setBasicContextHelp();
+}
+
+void MatrixPainter::stow()
+{
+ disconnect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotMatrixScrolled(int, int)));
+}
+
+void MatrixPainter::slotMatrixScrolled(int newX, int newY)
+{
+ if (!m_currentElement)
+ return ;
+
+ QPoint newP1(newX, newY), oldP1(m_parentView->getCanvasView()->contentsX(),
+ m_parentView->getCanvasView()->contentsY());
+
+ QPoint offset = newP1 - oldP1;
+
+ offset = m_mParentView->inverseMapPoint(offset);
+
+ QPoint p(m_currentElement->getCanvasX() + m_currentElement->getWidth(), m_currentElement->getCanvasY());
+ p += offset;
+
+ timeT newTime = getSnapGrid().snapX(p.x());
+ int newPitch = m_currentStaff->getHeightAtCanvasCoords(p.x(), p.y());
+
+ handleMouseMove(newTime, newPitch, 0);
+}
+
+void MatrixPainter::setBasicContextHelp()
+{
+ if (getSnapGrid().getSnapSetting() != SnapGrid::NoSnap) {
+ setContextHelp(i18n("Click and drag to draw a note; Shift to avoid snapping to grid"));
+ } else {
+ setContextHelp(i18n("Click and drag to draw a note"));
+ }
+}
+
+const QString MatrixPainter::ToolName = "painter";
+
+}
+#include "MatrixPainter.moc"
diff --git a/src/gui/editors/matrix/MatrixPainter.h b/src/gui/editors/matrix/MatrixPainter.h
new file mode 100644
index 0000000..570243a
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixPainter.h
@@ -0,0 +1,105 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MATRIXPAINTER_H_
+#define _RG_MATRIXPAINTER_H_
+
+#include "MatrixTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class MatrixView;
+class MatrixStaff;
+class MatrixElement;
+class Event;
+
+
+class MatrixPainter : public MatrixTool
+{
+ Q_OBJECT
+
+ friend class MatrixToolBox;
+
+public:
+
+ virtual void handleLeftButtonPress(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent *event,
+ ViewElement*);
+
+ /**
+ * Set the duration of the element
+ */
+ virtual int handleMouseMove(timeT,
+ int height,
+ QMouseEvent*);
+
+ /**
+ * Actually insert the new element
+ */
+ virtual void handleMouseRelease(timeT,
+ int height,
+ QMouseEvent*);
+
+ static const QString ToolName;
+
+ /**
+ * Respond to an event being deleted -- it may be the one the tool
+ * is remembering as the current event.
+ */
+ virtual void handleEventRemoved(Event *event);
+
+ virtual void ready();
+ virtual void stow();
+
+protected slots:
+
+ void slotMatrixScrolled(int x, int y);
+
+protected:
+ MatrixPainter(MatrixView*);
+ MatrixPainter(QString name, MatrixView*);
+
+ void setBasicContextHelp();
+
+ MatrixElement* m_currentElement;
+ MatrixStaff* m_currentStaff;
+};
+
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/MatrixParameterBox.cpp b/src/gui/editors/matrix/MatrixParameterBox.cpp
new file mode 100644
index 0000000..c330b94
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixParameterBox.cpp
@@ -0,0 +1,99 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MatrixParameterBox.h"
+
+#include "base/Instrument.h"
+#include "base/BasicQuantizer.h"
+#include "base/Selection.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/editors/parameters/InstrumentParameterBox.h"
+#include <kcombobox.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qframe.h>
+#include <qlayout.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+MatrixParameterBox::MatrixParameterBox(RosegardenGUIDoc *doc,
+ QWidget *parent, const char* name):
+ QFrame(parent, name),
+ m_quantizations(BasicQuantizer::getStandardQuantizations()),
+ m_doc(doc)
+{
+ setFrameStyle(NoFrame);
+ initBox();
+}
+
+MatrixParameterBox::~MatrixParameterBox()
+{}
+
+void
+MatrixParameterBox::initBox()
+{
+ QFont boldFont;
+ boldFont.setPointSize(int(boldFont.pointSize() * 9.5 / 10.0 + 0.5));
+ boldFont.setBold(true);
+
+ QFont plainFont;
+ plainFont.setPointSize(plainFont.pointSize() * 9 / 10);
+ QFont font = plainFont;
+
+ QFontMetrics fontMetrics(font);
+ // magic numbers: 13 is the height of the menu pixmaps, 10 is just 10
+ //int comboHeight = std::max(fontMetrics.height(), 13) + 10;
+
+ QGridLayout *gridLayout = new QGridLayout(this, 20, 3, 8, 1);
+
+ m_instrumentParameterBox = new InstrumentParameterBox(m_doc, this);
+ gridLayout->addMultiCellWidget(m_instrumentParameterBox, 0, 7, 0, 2);
+
+}
+
+void
+MatrixParameterBox::setSelection(EventSelection *selection)
+{
+ if (!selection)
+ return ;
+
+ EventSelection::eventcontainer::iterator
+ it = selection->getSegmentEvents().begin();
+
+for (; it != selection->getSegmentEvents().end(); it++) {}
+
+}
+
+void
+MatrixParameterBox::useInstrument(Instrument *instrument)
+{
+ m_instrumentParameterBox->useInstrument(instrument);
+}
+
+}
+#include "MatrixParameterBox.moc"
diff --git a/src/gui/editors/matrix/MatrixParameterBox.h b/src/gui/editors/matrix/MatrixParameterBox.h
new file mode 100644
index 0000000..d8d4a4d
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixParameterBox.h
@@ -0,0 +1,76 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MATRIXPARAMETERBOX_H_
+#define _RG_MATRIXPARAMETERBOX_H_
+
+#include <qframe.h>
+#include <vector>
+#include "base/Event.h"
+
+
+class QWidget;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class InstrumentParameterBox;
+class Instrument;
+class EventSelection;
+
+
+class MatrixParameterBox : public QFrame
+{
+ Q_OBJECT
+
+public:
+ MatrixParameterBox(RosegardenGUIDoc *doc=0, QWidget *parent=0, const char* name=0);
+ ~MatrixParameterBox();
+
+ void initBox();
+ void setSelection(EventSelection *);
+ void useInstrument(Instrument *instrument);
+
+protected:
+
+ KComboBox *m_quantizeCombo;
+ KComboBox *m_snapGridCombo;
+ InstrumentParameterBox *m_instrumentParameterBox;
+
+ std::vector<timeT> m_quantizations;
+ std::vector<timeT> m_snapValues;
+
+ RosegardenGUIDoc *m_doc;
+
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/MatrixResizer.cpp b/src/gui/editors/matrix/MatrixResizer.cpp
new file mode 100644
index 0000000..2fab5e8
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixResizer.cpp
@@ -0,0 +1,333 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MatrixResizer.h"
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include "base/Event.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/SnapGrid.h"
+#include "base/ViewElement.h"
+#include "commands/matrix/MatrixModifyCommand.h"
+#include "commands/notation/NormalizeRestsCommand.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "MatrixElement.h"
+#include "MatrixStaff.h"
+#include "MatrixTool.h"
+#include "MatrixView.h"
+#include <kaction.h>
+#include <kglobal.h>
+#include <qiconset.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include "misc/Debug.h"
+
+
+namespace Rosegarden
+{
+
+MatrixResizer::MatrixResizer(MatrixView* parent)
+ : MatrixTool("MatrixResizer", parent),
+ m_currentElement(0),
+ m_currentStaff(0)
+{
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/select.xpm");
+ QIconSet icon = QIconSet(pixmap);
+
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ new KAction(i18n("Switch to Draw Tool"), "pencil", 0, this,
+ SLOT(slotDrawSelected()), actionCollection(),
+ "draw");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ new KAction(i18n("Switch to Move Tool"), "move", 0, this,
+ SLOT(slotMoveSelected()), actionCollection(),
+ "move");
+
+ createMenu("matrixresizer.rc");
+}
+
+void MatrixResizer::handleEventRemoved(Event *event)
+{
+ if (m_currentElement && m_currentElement->event() == event) {
+ m_currentElement = 0;
+ }
+}
+
+void MatrixResizer::handleLeftButtonPress(timeT,
+ int,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement* el)
+{
+ MATRIX_DEBUG << "MatrixResizer::handleLeftButtonPress() : el = "
+ << el << endl;
+
+ if (!el)
+ return ; // nothing to erase
+
+ m_currentElement = dynamic_cast<MatrixElement*>(el);
+ m_currentStaff = m_mParentView->getStaff(staffNo);
+
+ if (m_currentElement) {
+
+ // Add this element and allow movement
+ //
+ EventSelection* selection = m_mParentView->getCurrentSelection();
+
+ if (selection) {
+ EventSelection *newSelection;
+
+ if ((e->state() & Qt::ShiftButton) ||
+ selection->contains(m_currentElement->event()))
+ newSelection = new EventSelection(*selection);
+ else
+ newSelection = new EventSelection(m_currentStaff->getSegment());
+
+ newSelection->addEvent(m_currentElement->event());
+ m_mParentView->setCurrentSelection(newSelection, true, true);
+ m_mParentView->canvas()->update();
+ } else {
+ m_mParentView->setSingleSelectedEvent(m_currentStaff->getSegment(),
+ m_currentElement->event(),
+ true);
+ m_mParentView->canvas()->update();
+ }
+ }
+}
+
+int MatrixResizer::handleMouseMove(timeT newTime,
+ int,
+ QMouseEvent *e)
+{
+ setBasicContextHelp();
+
+ if (!m_currentElement || !m_currentStaff)
+ return RosegardenCanvasView::NoFollow;
+
+ if (getSnapGrid().getSnapSetting() != SnapGrid::NoSnap) {
+ setContextHelp(i18n("Hold Shift to avoid snapping to beat grid"));
+ } else {
+ clearContextHelp();
+ }
+
+ // For the resizer we normally don't want to use the official
+ // time, because it's snapped to the left and we want to snap in
+ // the closest direction instead
+
+ if (e) {
+ QPoint p = m_mParentView->inverseMapPoint(e->pos());
+ newTime = getSnapGrid().snapX(p.x(), SnapGrid::SnapEither);
+ }
+
+ timeT newDuration = newTime - m_currentElement->getViewAbsoluteTime();
+
+ if (newDuration == 0) {
+ newDuration += getSnapGrid().getSnapTime
+ (m_currentElement->getViewAbsoluteTime());
+ }
+
+ int width = getSnapGrid().getRulerScale()->getXForTime
+ (m_currentElement->getViewAbsoluteTime() + newDuration)
+ - m_currentElement->getLayoutX() + 1;
+
+ int initialWidth = m_currentElement->getWidth();
+
+ int diffWidth = initialWidth - width;
+
+ EventSelection* selection = m_mParentView->getCurrentSelection();
+ EventSelection::eventcontainer::iterator it =
+ selection->getSegmentEvents().begin();
+
+ MatrixElement *element = 0;
+ for (; it != selection->getSegmentEvents().end(); it++) {
+ element = m_currentStaff->getElement(*it);
+
+ if (element) {
+ int newWidth = element->getWidth() - diffWidth;
+
+ MATRIX_DEBUG << "MatrixResizer::handleMouseMove - "
+ << "new width = " << newWidth << endl;
+
+ element->setWidth(newWidth);
+ m_currentStaff->positionElement(element);
+ }
+ }
+
+ m_mParentView->canvas()->update();
+ return RosegardenCanvasView::FollowHorizontal;
+}
+
+void MatrixResizer::handleMouseRelease(timeT newTime,
+ int,
+ QMouseEvent *e)
+{
+ if (!m_currentElement || !m_currentStaff)
+ return ;
+
+ // For the resizer we don't want to use the time passed in,
+ // because it's snapped to the left and we want to snap in the
+ // closest direction instead
+
+ if (e) {
+ QPoint p = m_mParentView->inverseMapPoint(e->pos());
+ newTime = getSnapGrid().snapX(p.x(), SnapGrid::SnapEither);
+ }
+
+ timeT diffDuration =
+ newTime - m_currentElement->getViewAbsoluteTime() -
+ m_currentElement->getViewDuration();
+
+ EventSelection *selection = m_mParentView->getCurrentSelection();
+
+ if (selection->getAddedEvents() == 0)
+ return ;
+ else {
+ QString commandLabel = i18n("Resize Event");
+
+ if (selection->getAddedEvents() > 1)
+ commandLabel = i18n("Resize Events");
+
+ KMacroCommand *macro = new KMacroCommand(commandLabel);
+
+ EventSelection::eventcontainer::iterator it =
+ selection->getSegmentEvents().begin();
+
+ Segment &segment = m_currentStaff->getSegment();
+
+ EventSelection *newSelection = new EventSelection(segment);
+
+ timeT normalizeStart = selection->getStartTime();
+ timeT normalizeEnd = selection->getEndTime();
+
+ for (; it != selection->getSegmentEvents().end(); it++) {
+ timeT eventTime = (*it)->getAbsoluteTime();
+ timeT eventDuration = (*it)->getDuration() + diffDuration;
+
+
+ MATRIX_DEBUG << "MatrixResizer::handleMouseRelease - "
+ << "Time = " << eventTime
+ << ", Duration = " << eventDuration << endl;
+
+
+ if (eventDuration < 0) {
+ eventTime += eventDuration;
+ eventDuration = -eventDuration;
+ }
+
+ if (eventDuration == 0) {
+ eventDuration += getSnapGrid().getSnapTime(eventTime);
+ }
+
+ if (eventTime + eventDuration >= segment.getEndMarkerTime()) {
+ eventDuration = std::min(eventDuration,
+ segment.getEndMarkerTime() - eventTime);
+ }
+
+ Event *newEvent =
+ new Event(**it,
+ eventTime,
+ eventDuration);
+
+ macro->addCommand(new MatrixModifyCommand(segment,
+ *it,
+ newEvent,
+ false,
+ false));
+
+ newSelection->addEvent(newEvent);
+ }
+
+ normalizeStart = std::min(normalizeStart, newSelection->getStartTime());
+ normalizeEnd = std::max(normalizeEnd, newSelection->getEndTime());
+
+ macro->addCommand(new NormalizeRestsCommand(segment,
+ normalizeStart,
+ normalizeEnd));
+
+ m_mParentView->setCurrentSelection(0, false, false);
+ m_mParentView->addCommandToHistory(macro);
+ m_mParentView->setCurrentSelection(newSelection, false, false);
+ }
+
+ m_mParentView->update();
+ m_currentElement = 0;
+ setBasicContextHelp();
+}
+
+void MatrixResizer::ready()
+{
+ connect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotMatrixScrolled(int, int)));
+ m_mParentView->setCanvasCursor(Qt::sizeHorCursor);
+ setBasicContextHelp();
+}
+
+void MatrixResizer::stow()
+{
+ disconnect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotMatrixScrolled(int, int)));
+}
+
+void MatrixResizer::slotMatrixScrolled(int newX, int newY)
+{
+ QPoint newP1(newX, newY), oldP1(m_parentView->getCanvasView()->contentsX(),
+ m_parentView->getCanvasView()->contentsY());
+
+ QPoint p(newX, newY);
+
+ if (newP1.x() > oldP1.x()) {
+ p.setX(newX + m_parentView->getCanvasView()->visibleWidth());
+ }
+
+ p = m_mParentView->inverseMapPoint(p);
+ int newTime = getSnapGrid().snapX(p.x());
+ handleMouseMove(newTime, 0, 0);
+}
+
+void MatrixResizer::setBasicContextHelp()
+{
+ EventSelection *selection = m_mParentView->getCurrentSelection();
+ if (selection && selection->getAddedEvents() > 1) {
+ setContextHelp(i18n("Click and drag to resize selected notes"));
+ } else {
+ setContextHelp(i18n("Click and drag to resize a note"));
+ }
+}
+
+const QString MatrixResizer::ToolName = "resizer";
+
+}
+#include "MatrixResizer.moc"
diff --git a/src/gui/editors/matrix/MatrixResizer.h b/src/gui/editors/matrix/MatrixResizer.h
new file mode 100644
index 0000000..e623cac
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixResizer.h
@@ -0,0 +1,102 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MATRIXRESIZER_H_
+#define _RG_MATRIXRESIZER_H_
+
+#include "MatrixTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class MatrixView;
+class MatrixStaff;
+class MatrixElement;
+class Event;
+
+
+class MatrixResizer : public MatrixTool
+{
+ Q_OBJECT
+
+ friend class MatrixToolBox;
+
+public:
+ virtual void handleLeftButtonPress(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent *event,
+ ViewElement*);
+
+ /**
+ * Set the duration of the element
+ */
+ virtual int handleMouseMove(timeT,
+ int height,
+ QMouseEvent*);
+
+ /**
+ * Actually insert the new element
+ */
+ virtual void handleMouseRelease(timeT,
+ int height,
+ QMouseEvent*);
+
+ static const QString ToolName;
+
+ /**
+ * Respond to an event being deleted -- it may be the one the tool
+ * is remembering as the current event.
+ */
+ virtual void handleEventRemoved(Event *event);
+
+ virtual void ready();
+ virtual void stow();
+
+protected slots:
+
+ void slotMatrixScrolled(int x, int y);
+
+protected:
+ MatrixResizer(MatrixView*);
+
+ void setBasicContextHelp();
+
+ MatrixElement* m_currentElement;
+ MatrixStaff* m_currentStaff;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/MatrixSelector.cpp b/src/gui/editors/matrix/MatrixSelector.cpp
new file mode 100644
index 0000000..fbb9689
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixSelector.cpp
@@ -0,0 +1,629 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MatrixSelector.h"
+
+#include "base/BaseProperties.h"
+#include <klocale.h>
+#include <kstddirs.h>
+#include "base/Event.h"
+#include "base/NotationTypes.h"
+#include "base/Selection.h"
+#include "base/ViewElement.h"
+#include "commands/edit/EventEditCommand.h"
+#include "gui/dialogs/EventEditDialog.h"
+#include "gui/dialogs/SimpleEventEditDialog.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/EditToolBox.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "MatrixElement.h"
+#include "MatrixMover.h"
+#include "MatrixPainter.h"
+#include "MatrixResizer.h"
+#include "MatrixStaff.h"
+#include "MatrixTool.h"
+#include "MatrixView.h"
+#include <kaction.h>
+#include <kglobal.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <qdialog.h>
+#include <qiconset.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include "misc/Debug.h"
+
+
+namespace Rosegarden
+{
+
+MatrixSelector::MatrixSelector(MatrixView* view)
+ : MatrixTool("MatrixSelector", view),
+ m_selectionRect(0),
+ m_updateRect(false),
+ m_currentStaff(0),
+ m_clickedElement(0),
+ m_dispatchTool(0),
+ m_justSelectedBar(false),
+ m_matrixView(view),
+ m_selectionToMerge(0)
+{
+ connect(m_parentView, SIGNAL(usedSelection()),
+ this, SLOT(slotHideSelection()));
+
+ new KAction(i18n("Switch to Draw Tool"), "pencil", 0, this,
+ SLOT(slotDrawSelected()), actionCollection(),
+ "draw");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ new KAction(i18n("Switch to Move Tool"), "move", 0, this,
+ SLOT(slotMoveSelected()), actionCollection(),
+ "move");
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/resize.xpm");
+ QIconSet icon = QIconSet(pixmap);
+
+ new KAction(i18n("Switch to Resize Tool"), icon, 0, this,
+ SLOT(slotResizeSelected()), actionCollection(),
+ "resize");
+
+ createMenu("matrixselector.rc");
+}
+
+void MatrixSelector::handleEventRemoved(Event *event)
+{
+ if (m_dispatchTool)
+ m_dispatchTool->handleEventRemoved(event);
+ if (m_clickedElement && m_clickedElement->event() == event) {
+ m_clickedElement = 0;
+ }
+}
+
+void MatrixSelector::slotClickTimeout()
+{
+ m_justSelectedBar = false;
+}
+
+void MatrixSelector::handleLeftButtonPress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element)
+{
+ MATRIX_DEBUG << "MatrixSelector::handleMousePress" << endl;
+
+ if (m_justSelectedBar) {
+ handleMouseTripleClick(time, height, staffNo, e, element);
+ m_justSelectedBar = false;
+ return ;
+ }
+
+ QPoint p = m_mParentView->inverseMapPoint(e->pos());
+
+ m_currentStaff = m_mParentView->getStaff(staffNo);
+
+ // Do the merge selection thing
+ //
+ delete m_selectionToMerge; // you can safely delete 0, you know?
+ const EventSelection *selectionToMerge = 0;
+ if (e->state() & Qt::ShiftButton)
+ selectionToMerge = m_mParentView->getCurrentSelection();
+
+ m_selectionToMerge =
+ (selectionToMerge ? new EventSelection(*selectionToMerge) : 0);
+
+ // Now the rest of the element stuff
+ //
+ m_clickedElement = dynamic_cast<MatrixElement*>(element);
+
+ if (m_clickedElement) {
+ int x = int(m_clickedElement->getLayoutX());
+ int width = m_clickedElement->getWidth();
+ int resizeStart = int(double(width) * 0.85) + x;
+
+ // max size of 10
+ if ((x + width ) - resizeStart > 10)
+ resizeStart = x + width - 10;
+
+ if (p.x() > resizeStart) {
+ m_dispatchTool = m_parentView->
+ getToolBox()->getTool(MatrixResizer::ToolName);
+ } else {
+ m_dispatchTool = m_parentView->
+ getToolBox()->getTool(MatrixMover::ToolName);
+ }
+
+ m_dispatchTool->ready();
+
+ m_dispatchTool->handleLeftButtonPress(time,
+ height,
+ staffNo,
+ e,
+ element);
+ return ;
+
+ } else if (e->state() & Qt::ControlButton) {
+
+ handleMidButtonPress(time, height, staffNo, e, element);
+ return;
+
+ } else {
+
+ // Workaround for #930420 Positional error in sweep-selection box
+ // boundary
+ int zoomValue = (int)m_matrixView->m_hZoomSlider->getCurrentSize();
+ MatrixStaff *staff = m_mParentView->getStaff(staffNo);
+ int pitch = m_currentStaff->getHeightAtCanvasCoords(p.x(), p.y());
+ int pitchCentreHeight = staff->getTotalHeight() -
+ pitch * staff->getLineSpacing() - 2; // 2 or ?
+ int pitchLineHeight = pitchCentreHeight + staff->getLineSpacing() / 2;
+ int drawHeight = p.y();
+ if (drawHeight <= pitchLineHeight + 1 &&
+ drawHeight >= pitchLineHeight - 1) {
+ if (drawHeight == pitchLineHeight)
+ drawHeight += 2;
+ else
+ drawHeight += 2 * (drawHeight - pitchLineHeight);
+ }
+ MATRIX_DEBUG << "#### MatrixSelector::handleLeftButtonPress() : zoom "
+ << zoomValue
+ << " pitch " << pitch
+ << " pitchCentreHeight " << pitchCentreHeight
+ << " pitchLineHeight " << pitchLineHeight
+ << " lineSpacing " << staff->getLineSpacing()
+ << " drawHeight " << drawHeight << endl;
+ m_selectionRect->setX(int(p.x() / 4)*4); // more workaround for #930420
+ m_selectionRect->setY(drawHeight);
+ m_selectionRect->setSize(0, 0);
+
+ m_selectionRect->show();
+ m_updateRect = true;
+
+ // Clear existing selection if we're not merging
+ //
+ if (!m_selectionToMerge) {
+ m_mParentView->setCurrentSelection(0, false, true);
+ m_mParentView->canvas()->update();
+ }
+ }
+
+ //m_parentView->setCursorPosition(p.x());
+}
+
+void MatrixSelector::handleMidButtonPress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element)
+{
+ m_clickedElement = 0; // should be used for left-button clicks only
+
+ // Don't allow overlapping elements on the same channel
+ if (dynamic_cast<MatrixElement*>(element))
+ return ;
+
+ m_dispatchTool = m_parentView->
+ getToolBox()->getTool(MatrixPainter::ToolName);
+
+ m_dispatchTool->ready();
+
+ m_dispatchTool->handleLeftButtonPress(time, height, staffNo, e, element);
+}
+
+void MatrixSelector::handleMouseDoubleClick(timeT ,
+ int ,
+ int staffNo,
+ QMouseEvent *ev,
+ ViewElement *element)
+{
+ /*
+ if (m_dispatchTool)
+ {
+ m_dispatchTool->handleMouseDoubleClick(time, height, staffNo, e, element);
+ }
+ */
+
+ m_clickedElement = dynamic_cast<MatrixElement*>(element);
+
+ MatrixStaff *staff = m_mParentView->getStaff(staffNo);
+ if (!staff)
+ return ;
+
+ if (m_clickedElement) {
+
+ if (m_clickedElement->event()->isa(Note::EventType) &&
+ m_clickedElement->event()->has(BaseProperties::TRIGGER_SEGMENT_ID)) {
+
+ int id = m_clickedElement->event()->get
+ <Int>
+ (BaseProperties::TRIGGER_SEGMENT_ID);
+ emit editTriggerSegment(id);
+ return ;
+ }
+
+ if (ev->state() & ShiftButton) { // advanced edit
+
+ EventEditDialog dialog(m_mParentView, *m_clickedElement->event(), true);
+
+ if (dialog.exec() == QDialog::Accepted &&
+ dialog.isModified()) {
+
+ EventEditCommand *command = new EventEditCommand
+ (staff->getSegment(),
+ m_clickedElement->event(),
+ dialog.getEvent());
+
+ m_mParentView->addCommandToHistory(command);
+ }
+ } else {
+
+ SimpleEventEditDialog dialog(m_mParentView, m_mParentView->getDocument(),
+ *m_clickedElement->event(), false);
+
+ if (dialog.exec() == QDialog::Accepted &&
+ dialog.isModified()) {
+
+ EventEditCommand *command = new EventEditCommand
+ (staff->getSegment(),
+ m_clickedElement->event(),
+ dialog.getEvent());
+
+ m_mParentView->addCommandToHistory(command);
+ }
+ }
+
+ } /*
+
+ #988167: Matrix:Multiclick select methods don't work in matrix editor
+ Postponing this, as it falls foul of world-matrix transformation
+ etiquette and other such niceties
+
+ else {
+
+ QRect rect = staff->getBarExtents(ev->x(), ev->y());
+
+ m_selectionRect->setX(rect.x() + 2);
+ m_selectionRect->setY(rect.y());
+ m_selectionRect->setSize(rect.width() - 4, rect.height());
+
+ m_selectionRect->show();
+ m_updateRect = false;
+
+ m_justSelectedBar = true;
+ QTimer::singleShot(QApplication::doubleClickInterval(), this,
+ SLOT(slotClickTimeout()));
+ } */
+}
+
+void MatrixSelector::handleMouseTripleClick(timeT t,
+ int height,
+ int staffNo,
+ QMouseEvent *ev,
+ ViewElement *element)
+{
+ if (!m_justSelectedBar)
+ return ;
+ m_justSelectedBar = false;
+
+ MatrixStaff *staff = m_mParentView->getStaff(staffNo);
+ if (!staff)
+ return ;
+
+ if (m_clickedElement) {
+
+ // should be safe, as we've already set m_justSelectedBar false
+ handleLeftButtonPress(t, height, staffNo, ev, element);
+ return ;
+
+ } else {
+
+ m_selectionRect->setX(staff->getX());
+ m_selectionRect->setY(staff->getY());
+ m_selectionRect->setSize(int(staff->getTotalWidth()) - 1,
+ staff->getTotalHeight() - 1);
+
+ m_selectionRect->show();
+ m_updateRect = false;
+ }
+}
+
+int MatrixSelector::handleMouseMove(timeT time, int height,
+ QMouseEvent *e)
+{
+ QPoint p = m_mParentView->inverseMapPoint(e->pos());
+
+ if (m_dispatchTool) {
+ return m_dispatchTool->handleMouseMove(time, height, e);
+ }
+
+
+ if (!m_updateRect) {
+ setContextHelpFor(e->pos(),
+ getSnapGrid().getSnapSetting() == SnapGrid::NoSnap);
+ return RosegardenCanvasView::NoFollow;
+ } else {
+ clearContextHelp();
+ }
+
+ int w = int(p.x() - m_selectionRect->x());
+ int h = int(p.y() - m_selectionRect->y());
+
+ // Qt rectangle dimensions appear to be 1-based
+ if (w > 0)
+ ++w;
+ else
+ --w;
+ if (h > 0)
+ ++h;
+ else
+ --h;
+
+ // Workaround for #930420 Positional error in sweep-selection box boundary
+ int wFix = (w > 0) ? 3 : 0;
+ int hFix = (h > 0) ? 3 : 0;
+ int xFix = (w < 0) ? 3 : 0;
+ m_selectionRect->setSize(w - wFix, h - hFix);
+ m_selectionRect->setX(m_selectionRect->x() + xFix);
+ setViewCurrentSelection();
+ m_selectionRect->setSize(w, h);
+ m_selectionRect->setX(m_selectionRect->x() - xFix);
+ m_mParentView->canvas()->update();
+
+ return RosegardenCanvasView::FollowHorizontal | RosegardenCanvasView::FollowVertical;
+}
+
+void MatrixSelector::handleMouseRelease(timeT time, int height, QMouseEvent *e)
+{
+ MATRIX_DEBUG << "MatrixSelector::handleMouseRelease" << endl;
+
+ if (m_dispatchTool) {
+ m_dispatchTool->handleMouseRelease(time, height, e);
+
+ m_dispatchTool->stow();
+ ready();
+
+ // don't delete the tool as it's still part of the toolbox
+ m_dispatchTool = 0;
+
+ return ;
+ }
+
+ m_updateRect = false;
+
+ if (m_clickedElement) {
+ m_mParentView->setSingleSelectedEvent(m_currentStaff->getSegment(),
+ m_clickedElement->event(),
+ false, true);
+ m_mParentView->canvas()->update();
+ m_clickedElement = 0;
+
+ } else if (m_selectionRect) {
+ setViewCurrentSelection();
+ m_selectionRect->hide();
+ m_mParentView->canvas()->update();
+ }
+
+ // Tell anyone who's interested that the selection has changed
+ emit gotSelection();
+
+ setContextHelpFor(e->pos());
+}
+
+void MatrixSelector::ready()
+{
+ if (m_mParentView) {
+ m_selectionRect = new QCanvasRectangle(m_mParentView->canvas());
+ m_selectionRect->hide();
+ m_selectionRect->setPen(QPen(GUIPalette::getColour(GUIPalette::SelectionRectangle), 2));
+
+ m_mParentView->setCanvasCursor(Qt::arrowCursor);
+ //m_mParentView->setPositionTracking(false);
+ }
+
+ connect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotMatrixScrolled(int, int)));
+
+ setContextHelp(i18n("Click and drag to select; middle-click and drag to draw new note"));
+}
+
+void MatrixSelector::stow()
+{
+ if (m_selectionRect) {
+ delete m_selectionRect;
+ m_selectionRect = 0;
+ m_mParentView->canvas()->update();
+ }
+
+ disconnect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotMatrixScrolled(int, int)));
+
+}
+
+void MatrixSelector::slotHideSelection()
+{
+ if (!m_selectionRect)
+ return ;
+ m_selectionRect->hide();
+ m_selectionRect->setSize(0, 0);
+ m_mParentView->canvas()->update();
+}
+
+void MatrixSelector::slotMatrixScrolled(int newX, int newY)
+{
+ if (m_updateRect) {
+ int offsetX = newX - m_parentView->getCanvasView()->contentsX();
+ int offsetY = newY - m_parentView->getCanvasView()->contentsY();
+
+ int w = int(m_selectionRect->width() + offsetX);
+ int h = int(m_selectionRect->height() + offsetY);
+
+ // Qt rectangle dimensions appear to be 1-based
+ if (w > 0)
+ ++w;
+ else
+ --w;
+ if (h > 0)
+ ++h;
+ else
+ --h;
+
+ m_selectionRect->setSize(w, h);
+ setViewCurrentSelection();
+ m_mParentView->canvas()->update();
+ }
+}
+
+void MatrixSelector::setViewCurrentSelection()
+{
+ EventSelection* selection = getSelection();
+
+ if (m_selectionToMerge && selection &&
+ m_selectionToMerge->getSegment() == selection->getSegment()) {
+
+ selection->addFromSelection(m_selectionToMerge);
+ m_mParentView->setCurrentSelection(selection, true, true);
+
+ } else if (!m_selectionToMerge) {
+
+ m_mParentView->setCurrentSelection(selection, true, true);
+
+ }
+
+}
+
+EventSelection* MatrixSelector::getSelection()
+{
+ if (!m_selectionRect->visible()) return 0;
+
+ Segment& originalSegment = m_currentStaff->getSegment();
+ EventSelection* selection = new EventSelection(originalSegment);
+
+ // get the selections
+ //
+ QCanvasItemList l = m_selectionRect->collisions(true);
+
+ if (l.count())
+ {
+ for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it)
+ {
+ QCanvasItem *item = *it;
+ QCanvasMatrixRectangle *matrixRect = 0;
+
+ if ((matrixRect = dynamic_cast<QCanvasMatrixRectangle*>(item)))
+ {
+ MatrixElement *mE = &matrixRect->getMatrixElement();
+ selection->addEvent(mE->event());
+ }
+ }
+ }
+
+ if (selection->getAddedEvents() > 0) {
+ return selection;
+ } else {
+ delete selection;
+ return 0;
+ }
+}
+
+void MatrixSelector::setContextHelpFor(QPoint p, bool ctrlPressed)
+{
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ if (!kapp->config()->readBoolEntry("toolcontexthelp", true)) return;
+
+ p = m_mParentView->inverseMapPoint(p);
+
+ // same logic as in MatrixCanvasView::contentsMousePressEvent
+
+ QCanvasItemList itemList = m_mParentView->canvas()->collisions(p);
+ QCanvasItemList::Iterator it;
+ MatrixElement* mel = 0;
+ QCanvasItem* activeItem = 0;
+
+ for (it = itemList.begin(); it != itemList.end(); ++it) {
+
+ QCanvasItem *item = *it;
+ QCanvasMatrixRectangle *mRect = 0;
+
+ if (item->active()) {
+ break;
+ }
+
+ if ((mRect = dynamic_cast<QCanvasMatrixRectangle*>(item))) {
+ if (! mRect->rect().contains(p, true)) continue;
+ mel = &(mRect->getMatrixElement());
+ break;
+ }
+ }
+
+ if (!mel) {
+ setContextHelp(i18n("Click and drag to select; middle-click and drag to draw new note"));
+
+ } else {
+
+ // same logic as in handleMouseButtonPress
+
+ int x = int(mel->getLayoutX());
+ int width = mel->getWidth();
+ int resizeStart = int(double(width) * 0.85) + x;
+
+ // max size of 10
+ if ((x + width ) - resizeStart > 10)
+ resizeStart = x + width - 10;
+
+ EventSelection *s = m_mParentView->getCurrentSelection();
+
+ if (p.x() > resizeStart) {
+ if (s && s->getAddedEvents() > 1) {
+ setContextHelp(i18n("Click and drag to resize selected notes"));
+ } else {
+ setContextHelp(i18n("Click and drag to resize note"));
+ }
+ } else {
+ if (s && s->getAddedEvents() > 1) {
+ if (!ctrlPressed) {
+ setContextHelp(i18n("Click and drag to move selected notes; hold Ctrl as well to copy"));
+ } else {
+ setContextHelp(i18n("Click and drag to copy selected notes"));
+ }
+ } else {
+ if (!ctrlPressed) {
+ setContextHelp(i18n("Click and drag to move note; hold Ctrl as well to copy"));
+ } else {
+ setContextHelp(i18n("Click and drag to copy note"));
+ }
+ }
+ }
+ }
+}
+
+const QString MatrixSelector::ToolName = "selector";
+
+}
+#include "MatrixSelector.moc"
diff --git a/src/gui/editors/matrix/MatrixSelector.h b/src/gui/editors/matrix/MatrixSelector.h
new file mode 100644
index 0000000..a1d1ca4
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixSelector.h
@@ -0,0 +1,177 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MATRIXSELECTOR_H_
+#define _RG_MATRIXSELECTOR_H_
+
+#include "MatrixTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+class QCanvasRectangle;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class MatrixView;
+class MatrixStaff;
+class MatrixElement;
+class EventSelection;
+class Event;
+class EditTool;
+
+
+class MatrixSelector : public MatrixTool
+{
+ Q_OBJECT
+
+ friend class MatrixToolBox;
+
+public:
+
+ virtual void handleLeftButtonPress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent *event,
+ ViewElement *element);
+
+ virtual void handleMidButtonPress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent *event,
+ ViewElement *element);
+
+ virtual int handleMouseMove(timeT time,
+ int height,
+ QMouseEvent *event);
+
+ virtual void handleMouseRelease(timeT,
+ int height,
+ QMouseEvent *event);
+
+ /**
+ * Double-click: edit an event or make a whole-bar selection
+ */
+ virtual void handleMouseDoubleClick(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent* event,
+ ViewElement *element);
+
+ /**
+ * Triple-click: maybe make a whole-staff selection
+ */
+ virtual void handleMouseTripleClick(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent* event,
+ ViewElement *element);
+
+
+ /**
+ * Create the selection rect
+ *
+ * We need this because MatrixView deletes all QCanvasItems
+ * along with it. This happens before the MatrixSelector is
+ * deleted, so we can't delete the selection rect in
+ * ~MatrixSelector because that leads to double deletion.
+ */
+ virtual void ready();
+
+ /**
+ * Delete the selection rect.
+ */
+ virtual void stow();
+
+ /**
+ * Returns the currently selected events
+ *
+ * The returned result is owned by the caller
+ */
+ EventSelection* getSelection();
+
+ /**
+ * Respond to an event being deleted -- it may be the one the tool
+ * is remembering as the current event.
+ */
+ virtual void handleEventRemoved(Event *event);
+
+ static const QString ToolName;
+
+public slots:
+ /**
+ * Hide the selection rectangle
+ *
+ * Should be called after a cut or a copy has been
+ * performed
+ */
+ void slotHideSelection();
+
+ void slotClickTimeout();
+
+protected slots:
+
+ void slotMatrixScrolled(int x, int y);
+
+signals:
+ void gotSelection(); // inform that we've got a new selection
+ void editTriggerSegment(int);
+
+protected:
+ MatrixSelector(MatrixView*);
+
+ void setContextHelpFor(QPoint p, bool ctrlPressed = false);
+
+ void setViewCurrentSelection();
+
+ //--------------- Data members ---------------------------------
+
+ QCanvasRectangle* m_selectionRect;
+ bool m_updateRect;
+
+ int m_clickedStaff;
+ MatrixStaff* m_currentStaff;
+
+ MatrixElement* m_clickedElement;
+
+ // tool to delegate to
+ EditTool* m_dispatchTool;
+
+ bool m_justSelectedBar;
+
+ MatrixView * m_matrixView;
+
+ EventSelection *m_selectionToMerge;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/MatrixStaff.cpp b/src/gui/editors/matrix/MatrixStaff.cpp
new file mode 100644
index 0000000..b6be79f
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixStaff.cpp
@@ -0,0 +1,232 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MatrixStaff.h"
+#include "misc/Debug.h"
+
+#include "base/BaseProperties.h"
+#include "base/Composition.h"
+#include "base/Event.h"
+#include "base/Instrument.h"
+#include "base/MidiProgram.h"
+#include "base/NotationTypes.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/SnapGrid.h"
+#include "base/Staff.h"
+#include "base/Track.h"
+#include "base/ViewElement.h"
+#include "base/SegmentMatrixHelper.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/LinedStaff.h"
+#include "gui/rulers/DefaultVelocityColour.h"
+#include "MatrixElement.h"
+#include "MatrixView.h"
+#include "MatrixVLayout.h"
+#include <qcanvas.h>
+
+
+namespace Rosegarden
+{
+
+MatrixStaff::MatrixStaff(QCanvas *canvas,
+ Segment *segment,
+ SnapGrid *snapGrid,
+ int id,
+ int vResolution,
+ MatrixView *view) :
+ LinedStaff(canvas, segment, snapGrid, id, vResolution, 1),
+ m_scaleFactor(2.0 /
+ Note(Note::Shortest).getDuration()),
+ m_view(view)
+{}
+
+MatrixStaff::~MatrixStaff()
+{
+ // nothing
+}
+
+int MatrixStaff::getLineCount() const
+{
+ // MATRIX_DEBUG << "MatrixStaff::getLineCount: isDrumMode " << m_view->isDrumMode() << ", key mapping " << (getKeyMapping() ? getKeyMapping()->getName() : "<none>") << endl;
+
+ if (m_view->isDrumMode()) {
+ const MidiKeyMapping *km = getKeyMapping();
+ if (km)
+ return km->getPitchExtent() + 1;
+ }
+ return MatrixVLayout::maxMIDIPitch + 2;
+}
+
+int MatrixStaff::getLegerLineCount() const
+{
+ return 0;
+}
+
+int MatrixStaff::getBottomLineHeight() const
+{
+ if (m_view->isDrumMode()) {
+ const MidiKeyMapping *km = getKeyMapping();
+ if (km)
+ return km->getPitchForOffset(0);
+ }
+ return 0;
+}
+
+int MatrixStaff::getHeightPerLine() const
+{
+ return 1;
+}
+
+bool MatrixStaff::elementsInSpaces() const
+{
+ return true;
+}
+
+bool MatrixStaff::showBeatLines() const
+{
+ return true;
+}
+
+bool MatrixStaff::wrapEvent(Event* e)
+{
+ // Changed from "Note or Time signature" to just "Note" because
+ // there should be no time signature events in any ordinary
+ // segments, they're only in the composition's ref segment
+
+ return e->isa(Note::EventType) &&
+ Staff::wrapEvent(e);
+}
+
+void
+MatrixStaff::positionElements(timeT from, timeT to)
+{
+ MatrixElementList *mel = getViewElementList();
+
+ MatrixElementList::iterator beginAt = mel->findTime(from);
+ if (beginAt != mel->begin())
+ --beginAt;
+
+ MatrixElementList::iterator endAt = mel->findTime(to);
+
+ for (MatrixElementList::iterator i = beginAt; i != endAt; ++i) {
+ positionElement(*i);
+ }
+}
+
+void MatrixStaff::positionElement(ViewElement* vel)
+{
+ MatrixElement* el = dynamic_cast<MatrixElement*>(vel);
+
+ // Memorize initial rectangle position. May be some overlap rectangles
+ // belonging to other notes are here and should be refreshed after
+ // current element is moved.
+ QRect initialRect;
+ bool rectWasVisible;
+ if (! m_view->isDrumMode())
+ rectWasVisible = el->getVisibleRectangle(initialRect);
+
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords
+ (el->getLayoutX(), int(el->getLayoutY()));
+
+ // Get velocity for colouring
+ //
+ using BaseProperties::VELOCITY;
+ long velocity = 127;
+ if (el->event()->has(VELOCITY))
+ el->event()->get
+ <Int>(VELOCITY, velocity);
+
+ el->setCanvas(m_canvas);
+
+ // Is the event currently selected? Colour accordingly.
+ //
+ EventSelection *selection = m_view->getCurrentSelection();
+
+ if (selection && selection->contains(el->event()))
+ el->setColour(GUIPalette::getColour(GUIPalette::SelectedElement));
+ else if (el->event()->has(BaseProperties::TRIGGER_SEGMENT_ID))
+ el->setColour(Qt::gray);
+ else
+ el->setColour(DefaultVelocityColour::getInstance()->getColour(velocity));
+
+ el->setCanvasX(coords.first);
+ el->setCanvasY((double)coords.second);
+
+ // Display overlaps
+ if (m_view->isDrumMode()) {
+ SegmentMatrixHelper helper(m_segment);
+ if (helper.isDrumColliding(el->event()))
+ el->setColour(GUIPalette::getColour(GUIPalette::MatrixOverlapBlock));
+ } else {
+ el->drawOverlapRectangles();
+
+ // Refresh other overlap rectangles
+ if (rectWasVisible) el->redrawOverlaps(initialRect);
+ }
+
+}
+
+MatrixElement*
+MatrixStaff::getElement(Event *event)
+{
+ ViewElementList::iterator i = findEvent(event);
+ if (i == m_viewElementList->end())
+ return 0;
+ return dynamic_cast<MatrixElement*>(*i);
+}
+
+void
+MatrixStaff::eventRemoved(const Segment *segment,
+ Event *event)
+{
+ LinedStaff::eventRemoved(segment, event);
+ m_view->handleEventRemoved(event);
+}
+
+ViewElement*
+MatrixStaff::makeViewElement(Event* e)
+{
+ return new MatrixElement(e, m_view->isDrumMode());
+}
+
+const MidiKeyMapping*
+MatrixStaff::getKeyMapping() const
+{
+ Composition *comp = getSegment().getComposition();
+ if (!comp)
+ return 0;
+ TrackId trackId = getSegment().getTrack();
+ Track *track = comp->getTrackById(trackId);
+ Instrument *instr = m_view->getDocument()->getStudio().
+ getInstrumentById(track->getInstrument());
+ if (!instr)
+ return 0;
+ return m_view->getKeyMapping();
+}
+
+
+}
diff --git a/src/gui/editors/matrix/MatrixStaff.h b/src/gui/editors/matrix/MatrixStaff.h
new file mode 100644
index 0000000..cd0a9dc
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixStaff.h
@@ -0,0 +1,111 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MATRIXSTAFF_H_
+#define _RG_MATRIXSTAFF_H_
+
+#include "base/Staff.h"
+#include "gui/general/LinedStaff.h"
+#include "base/Event.h"
+
+
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class SnapGrid;
+class Segment;
+class MidiKeyMapping;
+class MatrixView;
+class MatrixElement;
+class Event;
+
+
+class MatrixStaff : public LinedStaff
+{
+public:
+ MatrixStaff(QCanvas *canvas,
+ Segment *segment,
+ SnapGrid *snapGrid,
+ int id,
+ int vResolution,
+ MatrixView *view);
+ virtual ~MatrixStaff();
+
+protected:
+ virtual int getLineCount() const;
+ virtual int getLegerLineCount() const;
+ virtual int getBottomLineHeight() const;
+ virtual int getHeightPerLine() const;
+ virtual bool elementsInSpaces() const;
+ virtual bool showBeatLines() const;
+
+ const MidiKeyMapping *getKeyMapping() const;
+
+ /**
+ * Override from Staff<T>
+ * Wrap only notes
+ */
+ virtual bool wrapEvent(Event*);
+
+ /**
+ * Override from Staff<T>
+ * Let tools know if their current element has gone
+ */
+ virtual void eventRemoved(const Segment *, Event *);
+
+ virtual ViewElement* makeViewElement(Event*);
+
+public:
+ LinedStaff::setResolution;
+
+// double getTimeScaleFactor() const { return m_scaleFactor * 2; } // TODO: GROSS HACK to enhance matrix resolution (see also in matrixview.cpp) - BREAKS MATRIX VIEW, see bug 1000595
+ double getTimeScaleFactor() const { return m_scaleFactor; }
+ void setTimeScaleFactor(double f) { m_scaleFactor = f; }
+
+ int getElementHeight() { return m_resolution; }
+
+ virtual void positionElements(timeT from,
+ timeT to);
+
+ virtual void positionElement(ViewElement*);
+
+ // Get an element for an Event
+ //
+ MatrixElement* getElement(Event *event);
+
+private:
+ double m_scaleFactor;
+
+ MatrixView *m_view;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/MatrixTool.cpp b/src/gui/editors/matrix/MatrixTool.cpp
new file mode 100644
index 0000000..b036559
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixTool.cpp
@@ -0,0 +1,79 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MatrixTool.h"
+
+#include "gui/general/EditTool.h"
+#include "MatrixView.h"
+#include <kaction.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+MatrixTool::MatrixTool(const QString& menuName, MatrixView* parent)
+ : EditTool(menuName, parent),
+ m_mParentView(parent)
+{}
+
+void
+MatrixTool::slotSelectSelected()
+{
+ m_parentView->actionCollection()->action("select")->activate();
+}
+
+void
+MatrixTool::slotMoveSelected()
+{
+ m_parentView->actionCollection()->action("move")->activate();
+}
+
+void
+MatrixTool::slotEraseSelected()
+{
+ m_parentView->actionCollection()->action("erase")->activate();
+}
+
+void
+MatrixTool::slotResizeSelected()
+{
+ m_parentView->actionCollection()->action("resize")->activate();
+}
+
+void
+MatrixTool::slotDrawSelected()
+{
+ m_parentView->actionCollection()->action("draw")->activate();
+}
+
+const SnapGrid &
+MatrixTool::getSnapGrid() const
+{
+ return m_mParentView->getSnapGrid();
+}
+
+}
+#include "MatrixTool.moc"
diff --git a/src/gui/editors/matrix/MatrixTool.h b/src/gui/editors/matrix/MatrixTool.h
new file mode 100644
index 0000000..5127f57
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixTool.h
@@ -0,0 +1,74 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MATRIXTOOL_H_
+#define _RG_MATRIXTOOL_H_
+
+#include "gui/general/EditTool.h"
+
+
+class QString;
+
+
+namespace Rosegarden
+{
+
+class MatrixView;
+class SnapGrid;
+
+
+//////////////////////////////////////////////////////////////////////
+
+class MatrixTool : public EditTool
+{
+ Q_OBJECT
+
+public:
+// virtual void ready();
+
+protected slots:
+
+ // For switching between tools on RMB
+ //
+ void slotSelectSelected();
+ void slotMoveSelected();
+ void slotEraseSelected();
+ void slotResizeSelected();
+ void slotDrawSelected();
+
+ const SnapGrid &getSnapGrid() const;
+
+protected:
+ MatrixTool(const QString& menuName, MatrixView*);
+
+ //--------------- Data members ---------------------------------
+
+ MatrixView* m_mParentView;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/MatrixToolBox.cpp b/src/gui/editors/matrix/MatrixToolBox.cpp
new file mode 100644
index 0000000..466cfea
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixToolBox.cpp
@@ -0,0 +1,87 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MatrixToolBox.h"
+
+#include "gui/general/EditToolBox.h"
+#include "gui/general/EditTool.h"
+#include "MatrixView.h"
+#include "MatrixPainter.h"
+#include "MatrixEraser.h"
+#include "MatrixSelector.h"
+#include "MatrixMover.h"
+#include "MatrixResizer.h"
+
+#include <qstring.h>
+#include <kmessagebox.h>
+
+namespace Rosegarden
+{
+
+MatrixToolBox::MatrixToolBox(MatrixView* parent)
+ : EditToolBox(parent),
+ m_mParentView(parent)
+{}
+
+EditTool* MatrixToolBox::createTool(const QString& toolName)
+{
+ MatrixTool* tool = 0;
+
+ QString toolNamelc = toolName.lower();
+
+ if (toolNamelc == MatrixPainter::ToolName)
+
+ tool = new MatrixPainter(m_mParentView);
+
+ else if (toolNamelc == MatrixEraser::ToolName)
+
+ tool = new MatrixEraser(m_mParentView);
+
+ else if (toolNamelc == MatrixSelector::ToolName)
+
+ tool = new MatrixSelector(m_mParentView);
+
+ else if (toolNamelc == MatrixMover::ToolName)
+
+ tool = new MatrixMover(m_mParentView);
+
+ else if (toolNamelc == MatrixResizer::ToolName)
+
+ tool = new MatrixResizer(m_mParentView);
+
+ else {
+ KMessageBox::error(0, QString("MatrixToolBox::createTool : unrecognised toolname %1 (%2)")
+ .arg(toolName).arg(toolNamelc));
+ return 0;
+ }
+
+ m_tools.insert(toolName, tool);
+
+ return tool;
+
+}
+
+}
+#include "MatrixToolBox.moc"
diff --git a/src/gui/editors/matrix/MatrixToolBox.h b/src/gui/editors/matrix/MatrixToolBox.h
new file mode 100644
index 0000000..3bf0818
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixToolBox.h
@@ -0,0 +1,60 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MATRIXTOOLBOX_H_
+#define _RG_MATRIXTOOLBOX_H_
+
+#include "gui/general/EditToolBox.h"
+
+class QString;
+
+
+namespace Rosegarden
+{
+
+class EditTool;
+class MatrixView;
+class MatrixElement;
+class MatrixStaff;
+
+class MatrixToolBox : public EditToolBox
+{
+ Q_OBJECT
+public:
+ MatrixToolBox(MatrixView* parent);
+
+protected:
+
+ virtual EditTool* createTool(const QString& toolName);
+
+ //--------------- Data members ---------------------------------
+
+ MatrixView* m_mParentView;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/MatrixVLayout.cpp b/src/gui/editors/matrix/MatrixVLayout.cpp
new file mode 100644
index 0000000..aadcdf3
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixVLayout.cpp
@@ -0,0 +1,100 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MatrixVLayout.h"
+#include "misc/Debug.h"
+
+#include "base/BaseProperties.h"
+#include "base/LayoutEngine.h"
+#include "base/Staff.h"
+#include "MatrixElement.h"
+#include "MatrixStaff.h"
+
+
+namespace Rosegarden
+{
+
+MatrixVLayout::MatrixVLayout()
+{}
+
+MatrixVLayout::~MatrixVLayout()
+{}
+
+void MatrixVLayout::reset()
+{}
+
+void MatrixVLayout::resetStaff(Staff&, timeT, timeT)
+{}
+
+void MatrixVLayout::scanStaff(Staff& staffBase,
+ timeT startTime, timeT endTime)
+{
+ MatrixStaff& staff = dynamic_cast<MatrixStaff&>(staffBase);
+
+ using BaseProperties::PITCH;
+
+ MatrixElementList *notes = staff.getViewElementList();
+
+ MatrixElementList::iterator from = notes->begin();
+ MatrixElementList::iterator to = notes->end();
+ MatrixElementList::iterator i;
+
+ if (startTime != endTime) {
+ from = notes->findNearestTime(startTime);
+ if (from == notes->end())
+ from = notes->begin();
+ to = notes->findTime(endTime);
+ }
+
+ MATRIX_DEBUG << "MatrixVLayout::scanStaff : id = "
+ << staff.getId() << endl;
+
+
+ for (i = from; i != to; ++i) {
+
+ MatrixElement *el = dynamic_cast<MatrixElement*>((*i));
+
+ if (!el->isNote())
+ continue; // notes only
+
+ long pitch = 60;
+ el->event()->get
+ <Int>(PITCH, pitch);
+
+ int y = staff.getLayoutYForHeight(pitch) - staff.getElementHeight() / 2;
+
+ el->setLayoutY(y);
+ el->setHeight(staff.getElementHeight());
+ }
+
+}
+
+void MatrixVLayout::finishLayout(timeT, timeT)
+{}
+
+const int MatrixVLayout::minMIDIPitch = 0;
+const int MatrixVLayout::maxMIDIPitch = 127;
+
+}
diff --git a/src/gui/editors/matrix/MatrixVLayout.h b/src/gui/editors/matrix/MatrixVLayout.h
new file mode 100644
index 0000000..a33e0d1
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixVLayout.h
@@ -0,0 +1,91 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MATRIXVLAYOUT_H_
+#define _RG_MATRIXVLAYOUT_H_
+
+#include "base/LayoutEngine.h"
+#include "base/Event.h"
+
+
+
+
+namespace Rosegarden
+{
+
+class Staff;
+
+
+class MatrixVLayout : public VerticalLayoutEngine
+{
+public:
+ MatrixVLayout();
+
+ virtual ~MatrixVLayout();
+
+ /**
+ * Resets internal data stores for all staffs
+ */
+ virtual void reset();
+
+ /**
+ * Resets internal data stores for a specific staff
+ */
+ virtual void resetStaff(Staff &staff,
+ timeT = 0,
+ timeT = 0);
+
+ /**
+ * Precomputes layout data for a single staff, updating any
+ * internal data stores associated with that staff and updating
+ * any layout-related properties in the events on the staff's
+ * segment.
+ */
+ virtual void scanStaff(Staff &staff,
+ timeT = 0,
+ timeT = 0);
+
+ /**
+ * Computes any layout data that may depend on the results of
+ * scanning more than one staff. This may mean doing most of
+ * the layout (likely for horizontal layout) or nothing at all
+ * (likely for vertical layout).
+ */
+ virtual void finishLayout(timeT = 0,
+ timeT = 0);
+
+ static const int minMIDIPitch;
+ static const int maxMIDIPitch;
+
+protected:
+ //--------------- Data members ---------------------------------
+
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/MatrixView.cpp b/src/gui/editors/matrix/MatrixView.cpp
new file mode 100644
index 0000000..38abe20
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixView.cpp
@@ -0,0 +1,3076 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MatrixView.h"
+
+#include "base/BaseProperties.h"
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/AudioLevel.h"
+#include "base/Clipboard.h"
+#include "base/Composition.h"
+#include "base/Event.h"
+#include "base/Instrument.h"
+#include "base/LayoutEngine.h"
+#include "base/MidiProgram.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/PropertyName.h"
+#include "base/BasicQuantizer.h"
+#include "base/LegatoQuantizer.h"
+#include "base/RealTime.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/SnapGrid.h"
+#include "base/Staff.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "commands/edit/ChangeVelocityCommand.h"
+#include "commands/edit/ClearTriggersCommand.h"
+#include "commands/edit/CollapseNotesCommand.h"
+#include "commands/edit/CopyCommand.h"
+#include "commands/edit/CutCommand.h"
+#include "commands/edit/EraseCommand.h"
+#include "commands/edit/EventQuantizeCommand.h"
+#include "commands/edit/EventUnquantizeCommand.h"
+#include "commands/edit/PasteEventsCommand.h"
+#include "commands/edit/SelectionPropertyCommand.h"
+#include "commands/edit/SetTriggerCommand.h"
+#include "commands/matrix/MatrixInsertionCommand.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/dialogs/EventFilterDialog.h"
+#include "gui/dialogs/EventParameterDialog.h"
+#include "gui/dialogs/QuantizeDialog.h"
+#include "gui/dialogs/TriggerSegmentDialog.h"
+#include "gui/editors/guitar/Chord.h"
+#include "gui/editors/notation/NotationElement.h"
+#include "gui/editors/notation/NotationStrings.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include "gui/editors/parameters/InstrumentParameterBox.h"
+#include "gui/rulers/StandardRuler.h"
+#include "gui/general/ActiveItem.h"
+#include "gui/general/EditViewBase.h"
+#include "gui/general/EditView.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/MidiPitchLabel.h"
+#include "gui/kdeext/KTmpStatusMsg.h"
+#include "gui/rulers/ChordNameRuler.h"
+#include "gui/rulers/LoopRuler.h"
+#include "gui/rulers/PercussionPitchRuler.h"
+#include "gui/rulers/PitchRuler.h"
+#include "gui/rulers/PropertyBox.h"
+#include "gui/rulers/PropertyViewRuler.h"
+#include "gui/rulers/TempoRuler.h"
+#include "gui/studio/StudioControl.h"
+#include "gui/widgets/QDeferScrollView.h"
+#include "MatrixCanvasView.h"
+#include "MatrixElement.h"
+#include "MatrixEraser.h"
+#include "MatrixHLayout.h"
+#include "MatrixMover.h"
+#include "MatrixPainter.h"
+#include "MatrixResizer.h"
+#include "MatrixSelector.h"
+#include "MatrixStaff.h"
+#include "MatrixToolBox.h"
+#include "MatrixVLayout.h"
+#include "PianoKeyboard.h"
+#include "sound/MappedEvent.h"
+#include "sound/SequencerDataBlock.h"
+#include <klocale.h>
+#include <kstddirs.h>
+#include <kaction.h>
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kdockwidget.h>
+#include <kglobal.h>
+#include <kmessagebox.h>
+#include <kstatusbar.h>
+#include <ktoolbar.h>
+#include <kxmlguiclient.h>
+#include <qcanvas.h>
+#include <qcursor.h>
+#include <qdialog.h>
+#include <qlayout.h>
+#include <qiconset.h>
+#include <qlabel.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qscrollview.h>
+#include <qsize.h>
+#include <qslider.h>
+#include <qstring.h>
+#include <qwidget.h>
+#include <qwmatrix.h>
+
+
+namespace Rosegarden
+{
+
+static double xorigin = 0.0;
+
+
+MatrixView::MatrixView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ QWidget *parent,
+ bool drumMode)
+ : EditView(doc, segments, 3, parent, "matrixview"),
+ m_hlayout(&doc->getComposition()),
+ m_referenceRuler(new ZoomableMatrixHLayoutRulerScale(m_hlayout)),
+ m_vlayout(),
+ m_snapGrid(new SnapGrid(&m_hlayout)),
+ m_lastEndMarkerTime(0),
+ m_hoveredOverAbsoluteTime(0),
+ m_hoveredOverNoteName(0),
+ m_selectionCounter(0),
+ m_insertModeLabel(0),
+ m_haveHoveredOverNote(false),
+ m_previousEvPitch(0),
+ m_dockLeft(0),
+ m_canvasView(0),
+ m_pianoView(0),
+ m_localMapping(0),
+ m_lastNote(0),
+ m_quantizations(BasicQuantizer::getStandardQuantizations()),
+ m_chordNameRuler(0),
+ m_tempoRuler(0),
+ m_playTracking(true),
+ m_dockVisible(true),
+ m_drumMode(drumMode),
+ m_mouseInCanvasView(false)
+{
+ RG_DEBUG << "MatrixView ctor: drumMode " << drumMode << "\n";
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/toolbar");
+ QPixmap matrixPixmap(pixmapDir + "/matrix.xpm");
+
+ m_dockLeft = createDockWidget("params dock", matrixPixmap, 0L,
+ i18n("Instrument Parameters"));
+ m_dockLeft->manualDock(m_mainDockWidget, // dock target
+ KDockWidget::DockLeft, // dock site
+ 20); // relation target/this (in percent)
+
+ connect(m_dockLeft, SIGNAL(iMBeingClosed()),
+ this, SLOT(slotParametersClosed()));
+ connect(m_dockLeft, SIGNAL(hasUndocked()),
+ this, SLOT(slotParametersClosed()));
+ // Apparently, hasUndocked() is emitted when the dock widget's
+ // 'close' button on the dock handle is clicked.
+ connect(m_mainDockWidget, SIGNAL(docking(KDockWidget*, KDockWidget::DockPosition)),
+ this, SLOT(slotParametersDockedBack(KDockWidget*, KDockWidget::DockPosition)));
+
+ Composition &comp = doc->getComposition();
+
+ m_toolBox = new MatrixToolBox(this);
+
+ initStatusBar();
+
+ connect(m_toolBox, SIGNAL(showContextHelp(const QString &)),
+ this, SLOT(slotToolHelpChanged(const QString &)));
+
+ QCanvas *tCanvas = new QCanvas(this);
+
+ m_config->setGroup(MatrixViewConfigGroup);
+ if (m_config->readBoolEntry("backgroundtextures-1.6-plus", true)) {
+ QPixmap background;
+ QString pixmapDir =
+ KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ // We now use a lined background for the non-percussion matrix,
+ // suggested and supplied by Alessandro Preziosi
+ QString backgroundPixmap = isDrumMode() ? "bg-paper-white.xpm" : "bg-matrix-lines.xpm";
+ if (background.load(QString("%1/misc/%2").
+ arg(pixmapDir, backgroundPixmap))) {
+ tCanvas->setBackgroundPixmap(background);
+ }
+ }
+
+ MATRIX_DEBUG << "MatrixView : creating staff\n";
+
+ Track *track =
+ comp.getTrackById(segments[0]->getTrack());
+
+ Instrument *instr = getDocument()->getStudio().
+ getInstrumentById(track->getInstrument());
+
+ int resolution = 8;
+
+ if (isDrumMode() && instr && instr->getKeyMapping()) {
+ resolution = 11;
+ }
+
+ for (unsigned int i = 0; i < segments.size(); ++i) {
+ m_staffs.push_back(new MatrixStaff(tCanvas,
+ segments[i],
+ m_snapGrid,
+ i,
+ resolution,
+ this));
+ // staff has one too many rows to avoid a half-row at the top:
+ m_staffs[i]->setY( -resolution / 2);
+ //!!! if (isDrumMode()) m_staffs[i]->setX(resolution);
+ if (i == 0)
+ m_staffs[i]->setCurrent(true);
+ }
+
+ MATRIX_DEBUG << "MatrixView : creating canvas view\n";
+
+ const MidiKeyMapping *mapping = 0;
+
+ if (instr) {
+ mapping = instr->getKeyMapping();
+ if (mapping) {
+ RG_DEBUG << "MatrixView: Instrument has key mapping: "
+ << mapping->getName() << endl;
+ m_localMapping = new MidiKeyMapping(*mapping);
+ extendKeyMapping();
+ } else {
+ RG_DEBUG << "MatrixView: Instrument has no key mapping\n";
+ }
+ }
+
+ m_pianoView = new QDeferScrollView(getCentralWidget());
+
+ QWidget* vport = m_pianoView->viewport();
+
+ if (isDrumMode() && mapping &&
+ !m_localMapping->getMap().empty()) {
+ m_pitchRuler = new PercussionPitchRuler(vport,
+ m_localMapping,
+ resolution); // line spacing
+ } else {
+ m_pitchRuler = new PianoKeyboard(vport);
+ }
+
+ m_pianoView->setVScrollBarMode(QScrollView::AlwaysOff);
+ m_pianoView->setHScrollBarMode(QScrollView::AlwaysOff);
+ m_pianoView->addChild(m_pitchRuler);
+ m_pianoView->setFixedWidth(m_pianoView->contentsWidth());
+
+ m_grid->addWidget(m_pianoView, CANVASVIEW_ROW, 1);
+
+ m_parameterBox = new InstrumentParameterBox(getDocument(), m_dockLeft);
+ m_dockLeft->setWidget(m_parameterBox);
+
+ RosegardenGUIApp *app = RosegardenGUIApp::self();
+ connect(app,
+ SIGNAL(pluginSelected(InstrumentId, int, int)),
+ m_parameterBox,
+ SLOT(slotPluginSelected(InstrumentId, int, int)));
+ connect(app,
+ SIGNAL(pluginBypassed(InstrumentId, int, bool)),
+ m_parameterBox,
+ SLOT(slotPluginBypassed(InstrumentId, int, bool)));
+ connect(app,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ m_parameterBox,
+ SLOT(slotInstrumentParametersChanged(InstrumentId)));
+ connect(m_parameterBox,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ app,
+ SIGNAL(instrumentParametersChanged(InstrumentId)));
+ connect(m_parameterBox,
+ SIGNAL(selectPlugin(QWidget *, InstrumentId, int)),
+ app,
+ SLOT(slotShowPluginDialog(QWidget *, InstrumentId, int)));
+ connect(m_parameterBox,
+ SIGNAL(showPluginGUI(InstrumentId, int)),
+ app,
+ SLOT(slotShowPluginGUI(InstrumentId, int)));
+ connect(parent, // RosegardenGUIView
+ SIGNAL(checkTrackAssignments()),
+ this,
+ SLOT(slotCheckTrackAssignments()));
+
+ // Assign the instrument
+ //
+ m_parameterBox->useInstrument(instr);
+
+ if (m_drumMode) {
+ connect(m_parameterBox,
+ SIGNAL(instrumentPercussionSetChanged(Instrument *)),
+ this,
+ SLOT(slotPercussionSetChanged(Instrument *)));
+ }
+
+ // Set the snap grid from the stored size in the segment
+ //
+ int snapGridSize = m_staffs[0]->getSegment().getSnapGridSize();
+
+ MATRIX_DEBUG << "MatrixView : Snap Grid Size = " << snapGridSize << endl;
+
+ if (snapGridSize != -1) {
+ m_snapGrid->setSnapTime(snapGridSize);
+ } else {
+ m_config->setGroup(MatrixViewConfigGroup);
+ snapGridSize = m_config->readNumEntry
+ ("Snap Grid Size", SnapGrid::SnapToBeat);
+ m_snapGrid->setSnapTime(snapGridSize);
+ m_staffs[0]->getSegment().setSnapGridSize(snapGridSize);
+ }
+
+ m_canvasView = new MatrixCanvasView(*m_staffs[0],
+ m_snapGrid,
+ m_drumMode,
+ tCanvas,
+ getCentralWidget());
+ setCanvasView(m_canvasView);
+
+ // do this after we have a canvas
+ setupActions();
+ setupAddControlRulerMenu();
+
+ stateChanged("parametersbox_closed", KXMLGUIClient::StateReverse);
+
+ // tool bars
+ initActionsToolbar();
+ initZoomToolbar();
+
+ // Connect vertical scrollbars between matrix and piano
+ //
+ connect(m_canvasView->verticalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(slotVerticalScrollPianoKeyboard(int)));
+
+ connect(m_canvasView->verticalScrollBar(), SIGNAL(sliderMoved(int)),
+ this, SLOT(slotVerticalScrollPianoKeyboard(int)));
+
+ connect(m_canvasView, SIGNAL(zoomIn()), this, SLOT(slotZoomIn()));
+ connect(m_canvasView, SIGNAL(zoomOut()), this, SLOT(slotZoomOut()));
+
+ connect(m_pianoView, SIGNAL(gotWheelEvent(QWheelEvent*)),
+ m_canvasView, SLOT(slotExternalWheelEvent(QWheelEvent*)));
+
+ // ensure the piano keyb keeps the right margins when the user toggles
+ // the canvas view rulers
+ //
+ connect(m_canvasView, SIGNAL(bottomWidgetHeightChanged(int)),
+ this, SLOT(slotCanvasBottomWidgetHeightChanged(int)));
+
+ connect(m_canvasView, SIGNAL(mouseEntered()),
+ this, SLOT(slotMouseEnteredCanvasView()));
+
+ connect(m_canvasView, SIGNAL(mouseLeft()),
+ this, SLOT(slotMouseLeftCanvasView()));
+
+ /*
+ QObject::connect
+ (getCanvasView(), SIGNAL(activeItemPressed(QMouseEvent*, QCanvasItem*)),
+ this, SLOT (activeItemPressed(QMouseEvent*, QCanvasItem*)));
+ */
+
+ QObject::connect
+ (getCanvasView(),
+ SIGNAL(mousePressed(timeT,
+ int, QMouseEvent*, MatrixElement*)),
+ this,
+ SLOT(slotMousePressed(timeT,
+ int, QMouseEvent*, MatrixElement*)));
+
+ QObject::connect
+ (getCanvasView(),
+ SIGNAL(mouseMoved(timeT, int, QMouseEvent*)),
+ this,
+ SLOT(slotMouseMoved(timeT, int, QMouseEvent*)));
+
+ QObject::connect
+ (getCanvasView(),
+ SIGNAL(mouseReleased(timeT, int, QMouseEvent*)),
+ this,
+ SLOT(slotMouseReleased(timeT, int, QMouseEvent*)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(hoveredOverNoteChanged(int, bool, timeT)),
+ this, SLOT(slotHoveredOverNoteChanged(int, bool, timeT)));
+
+ QObject::connect
+ (m_pitchRuler, SIGNAL(hoveredOverKeyChanged(unsigned int)),
+ this, SLOT (slotHoveredOverKeyChanged(unsigned int)));
+
+ QObject::connect
+ (m_pitchRuler, SIGNAL(keyPressed(unsigned int, bool)),
+ this, SLOT (slotKeyPressed(unsigned int, bool)));
+
+ QObject::connect
+ (m_pitchRuler, SIGNAL(keySelected(unsigned int, bool)),
+ this, SLOT (slotKeySelected(unsigned int, bool)));
+
+ QObject::connect
+ (m_pitchRuler, SIGNAL(keyReleased(unsigned int, bool)),
+ this, SLOT (slotKeyReleased(unsigned int, bool)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(hoveredOverAbsoluteTimeChanged(unsigned int)),
+ this, SLOT (slotHoveredOverAbsoluteTimeChanged(unsigned int)));
+
+ QObject::connect
+ (doc, SIGNAL(pointerPositionChanged(timeT)),
+ this, SLOT(slotSetPointerPosition(timeT)));
+
+ MATRIX_DEBUG << "MatrixView : applying layout\n";
+
+ bool layoutApplied = applyLayout();
+ if (!layoutApplied)
+ KMessageBox::sorry(0, i18n("Couldn't apply piano roll layout"));
+ else {
+ MATRIX_DEBUG << "MatrixView : rendering elements\n";
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+
+ m_staffs[i]->positionAllElements();
+ m_staffs[i]->getSegment().getRefreshStatus
+ (m_segmentsRefreshStatusIds[i]).setNeedsRefresh(false);
+ }
+ }
+
+ StandardRuler *topStandardRuler = new StandardRuler(getDocument(),
+ &m_hlayout, int(xorigin), 25,
+ false, getCentralWidget());
+ topStandardRuler->setSnapGrid(m_snapGrid);
+ setTopStandardRuler(topStandardRuler);
+
+ StandardRuler *bottomStandardRuler = new StandardRuler(getDocument(),
+ &m_hlayout, 0, 25,
+ true, getBottomWidget());
+ bottomStandardRuler->setSnapGrid(m_snapGrid);
+ setBottomStandardRuler(bottomStandardRuler);
+
+ topStandardRuler->connectRulerToDocPointer(doc);
+ bottomStandardRuler->connectRulerToDocPointer(doc);
+
+ // Disconnect the default connections for this signal from the
+ // top ruler, and connect our own instead
+
+ QObject::disconnect
+ (topStandardRuler->getLoopRuler(),
+ SIGNAL(setPointerPosition(timeT)), 0, 0);
+
+ QObject::connect
+ (topStandardRuler->getLoopRuler(),
+ SIGNAL(setPointerPosition(timeT)),
+ this, SLOT(slotSetInsertCursorPosition(timeT)));
+
+ QObject::connect
+ (topStandardRuler,
+ SIGNAL(dragPointerToPosition(timeT)),
+ this, SLOT(slotSetInsertCursorPosition(timeT)));
+
+ topStandardRuler->getLoopRuler()->setBackgroundColor
+ (GUIPalette::getColour(GUIPalette::InsertCursorRuler));
+
+ connect(topStandardRuler->getLoopRuler(), SIGNAL(startMouseMove(int)),
+ m_canvasView, SLOT(startAutoScroll(int)));
+ connect(topStandardRuler->getLoopRuler(), SIGNAL(stopMouseMove()),
+ m_canvasView, SLOT(stopAutoScroll()));
+
+ connect(bottomStandardRuler->getLoopRuler(), SIGNAL(startMouseMove(int)),
+ m_canvasView, SLOT(startAutoScroll(int)));
+ connect(bottomStandardRuler->getLoopRuler(), SIGNAL(stopMouseMove()),
+ m_canvasView, SLOT(stopAutoScroll()));
+ connect(m_bottomStandardRuler, SIGNAL(dragPointerToPosition(timeT)),
+ this, SLOT(slotSetPointerPosition(timeT)));
+
+ // Force height for the moment
+ //
+ m_pitchRuler->setFixedHeight(canvas()->height());
+
+
+ updateViewCaption();
+
+ // Add a velocity ruler
+ //
+ //!!! addPropertyViewRuler(BaseProperties::VELOCITY);
+
+ m_chordNameRuler = new ChordNameRuler
+ (m_referenceRuler, doc, segments, 0, 20, getCentralWidget());
+ m_chordNameRuler->setStudio(&getDocument()->getStudio());
+ addRuler(m_chordNameRuler);
+
+ m_tempoRuler = new TempoRuler
+ (m_referenceRuler, doc, this, 0, 24, false, getCentralWidget());
+ static_cast<TempoRuler *>(m_tempoRuler)->connectSignals();
+ addRuler(m_tempoRuler);
+
+ stateChanged("have_selection", KXMLGUIClient::StateReverse);
+ slotTestClipboard();
+
+ timeT start = doc->getComposition().getLoopStart();
+ timeT end = doc->getComposition().getLoopEnd();
+ m_topStandardRuler->getLoopRuler()->slotSetLoopMarker(start, end);
+ m_bottomStandardRuler->getLoopRuler()->slotSetLoopMarker(start, end);
+
+ setCurrentSelection(0, false);
+
+ // Change this if the matrix view ever has its own page
+ // in the config dialog.
+ setConfigDialogPageIndex(0);
+
+ // default zoom
+ m_config->setGroup(MatrixViewConfigGroup);
+ double zoom = m_config->readDoubleNumEntry("Zoom Level",
+ m_hZoomSlider->getCurrentSize());
+ m_hZoomSlider->setSize(zoom);
+ m_referenceRuler->setHScaleFactor(zoom);
+
+ // Scroll view to centre middle-C and warp to pointer position
+ //
+ m_canvasView->scrollBy(0, m_staffs[0]->getCanvasYForHeight(60) / 2);
+
+ slotSetPointerPosition(comp.getPosition());
+
+ // All toolbars should be created before this is called
+ setAutoSaveSettings("MatrixView", true);
+
+ readOptions();
+ setOutOfCtor();
+
+ // Property and Control Rulers
+ //
+ if (getCurrentSegment()->getViewFeatures())
+ slotShowVelocityControlRuler();
+ setupControllerTabs();
+
+ setRewFFwdToAutoRepeat();
+ slotCompositionStateUpdate();
+}
+
+MatrixView::~MatrixView()
+{
+ slotSaveOptions();
+
+ delete m_chordNameRuler;
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ delete m_staffs[i]; // this will erase all "notes" canvas items
+ }
+
+ // This looks silly but the reason is that on destruction of the
+ // MatrixCanvasView, setCanvas() is called (this is in
+ // ~QCanvasView so we can't do anything about it). This calls
+ // QCanvasView::updateContentsSize(), which in turn updates the
+ // view's scrollbars, hence calling QScrollBar::setValue(), and
+ // sending the QSCrollbar::valueChanged() signal. But we have a
+ // slot connected to that signal
+ // (MatrixView::slotVerticalScrollPianoKeyboard), which scrolls
+ // the pianoView. However at this stage the pianoView has already
+ // been deleted, so a likely outcome is a crash.
+ //
+ // A solution is to zero out m_pianoView here, and to check if
+ // it's non null in slotVerticalScrollPianoKeyboard.
+ //
+ m_pianoView = 0;
+
+ delete m_snapGrid;
+
+ if (m_localMapping)
+ delete m_localMapping;
+}
+
+void MatrixView::slotSaveOptions()
+{
+ m_config->setGroup(MatrixViewConfigGroup);
+
+ m_config->writeEntry("Show Chord Name Ruler", getToggleAction("show_chords_ruler")->isChecked());
+ m_config->writeEntry("Show Tempo Ruler", getToggleAction("show_tempo_ruler")->isChecked());
+ m_config->writeEntry("Show Parameters", m_dockVisible);
+ //getToggleAction("m_dockLeft->isVisible());
+
+ m_config->sync();
+}
+
+void MatrixView::readOptions()
+{
+ EditView::readOptions();
+ m_config->setGroup(MatrixViewConfigGroup);
+
+ bool opt = false;
+
+ opt = m_config->readBoolEntry("Show Chord Name Ruler", false);
+ getToggleAction("show_chords_ruler")->setChecked(opt);
+ slotToggleChordsRuler();
+
+ opt = m_config->readBoolEntry("Show Tempo Ruler", true);
+ getToggleAction("show_tempo_ruler")->setChecked(opt);
+ slotToggleTempoRuler();
+
+ opt = m_config->readBoolEntry("Show Parameters", true);
+ if (!opt) {
+ m_dockLeft->undock();
+ m_dockLeft->hide();
+ stateChanged("parametersbox_closed", KXMLGUIClient::StateNoReverse);
+ m_dockVisible = false;
+ }
+
+}
+
+void MatrixView::setupActions()
+{
+ EditViewBase::setupActions("matrix.rc");
+ EditView::setupActions();
+
+ //
+ // Edition tools (eraser, selector...)
+ //
+ KRadioAction* toolAction = 0;
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QIconSet icon(QPixmap(pixmapDir + "/toolbar/select.xpm"));
+
+ toolAction = new KRadioAction(i18n("&Select and Edit"), icon, Key_F2,
+ this, SLOT(slotSelectSelected()),
+ actionCollection(), "select");
+ toolAction->setExclusiveGroup("tools");
+
+ toolAction = new KRadioAction(i18n("&Draw"), "pencil", Key_F3,
+ this, SLOT(slotPaintSelected()),
+ actionCollection(), "draw");
+ toolAction->setExclusiveGroup("tools");
+
+ toolAction = new KRadioAction(i18n("&Erase"), "eraser", Key_F4,
+ this, SLOT(slotEraseSelected()),
+ actionCollection(), "erase");
+ toolAction->setExclusiveGroup("tools");
+
+ toolAction = new KRadioAction(i18n("&Move"), "move", Key_F5,
+ this, SLOT(slotMoveSelected()),
+ actionCollection(), "move");
+ toolAction->setExclusiveGroup("tools");
+
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/resize.xpm");
+ icon = QIconSet(pixmap);
+ toolAction = new KRadioAction(i18n("Resi&ze"), icon, Key_F6,
+ this, SLOT(slotResizeSelected()),
+ actionCollection(), "resize");
+ toolAction->setExclusiveGroup("tools");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("chord")));
+ (new KToggleAction(i18n("C&hord Insert Mode"), icon, Key_H,
+ this, SLOT(slotUpdateInsertModeStatus()),
+ actionCollection(), "chord_mode"))->
+ setChecked(false);
+
+ pixmap.load(pixmapDir + "/toolbar/step_by_step.xpm");
+ icon = QIconSet(pixmap);
+ new KToggleAction(i18n("Ste&p Recording"), icon, 0, this,
+ SLOT(slotToggleStepByStep()), actionCollection(),
+ "toggle_step_by_step");
+
+ pixmap.load(pixmapDir + "/toolbar/quantize.png");
+ icon = QIconSet(pixmap);
+ new KAction(EventQuantizeCommand::getGlobalName(), icon, Key_Equal, this,
+ SLOT(slotTransformsQuantize()), actionCollection(),
+ "quantize");
+
+ new KAction(i18n("Repeat Last Quantize"), Key_Plus, this,
+ SLOT(slotTransformsRepeatQuantize()), actionCollection(),
+ "repeat_quantize");
+
+ new KAction(CollapseNotesCommand::getGlobalName(), Key_Equal + CTRL, this,
+ SLOT(slotTransformsCollapseNotes()), actionCollection(),
+ "collapse_notes");
+
+ new KAction(i18n("&Legato"), Key_Minus, this,
+ SLOT(slotTransformsLegato()), actionCollection(),
+ "legatoize");
+
+ new KAction(ChangeVelocityCommand::getGlobalName(10), 0,
+ Key_Up + SHIFT, this,
+ SLOT(slotVelocityUp()), actionCollection(),
+ "velocity_up");
+
+ new KAction(ChangeVelocityCommand::getGlobalName( -10), 0,
+ Key_Down + SHIFT, this,
+ SLOT(slotVelocityDown()), actionCollection(),
+ "velocity_down");
+
+ new KAction(i18n("Set to Current Velocity"), 0, this,
+ SLOT(slotSetVelocitiesToCurrent()), actionCollection(),
+ "set_to_current_velocity");
+
+ new KAction(i18n("Set Event &Velocities..."), 0, this,
+ SLOT(slotSetVelocities()), actionCollection(),
+ "set_velocities");
+
+ new KAction(i18n("Trigger Se&gment..."), 0, this,
+ SLOT(slotTriggerSegment()), actionCollection(),
+ "trigger_segment");
+
+ new KAction(i18n("Remove Triggers..."), 0, this,
+ SLOT(slotRemoveTriggers()), actionCollection(),
+ "remove_trigger");
+
+ new KAction(i18n("Select &All"), Key_A + CTRL, this,
+ SLOT(slotSelectAll()), actionCollection(),
+ "select_all");
+
+ new KAction(i18n("&Delete"), Key_Delete, this,
+ SLOT(slotEditDelete()), actionCollection(),
+ "delete");
+
+ new KAction(i18n("Cursor &Back"), 0, Key_Left, this,
+ SLOT(slotStepBackward()), actionCollection(),
+ "cursor_back");
+
+ new KAction(i18n("Cursor &Forward"), 0, Key_Right, this,
+ SLOT(slotStepForward()), actionCollection(),
+ "cursor_forward");
+
+ new KAction(i18n("Cursor Ba&ck Bar"), 0, Key_Left + CTRL, this,
+ SLOT(slotJumpBackward()), actionCollection(),
+ "cursor_back_bar");
+
+ new KAction(i18n("Cursor For&ward Bar"), 0, Key_Right + CTRL, this,
+ SLOT(slotJumpForward()), actionCollection(),
+ "cursor_forward_bar");
+
+ new KAction(i18n("Cursor Back and Se&lect"), SHIFT + Key_Left, this,
+ SLOT(slotExtendSelectionBackward()), actionCollection(),
+ "extend_selection_backward");
+
+ new KAction(i18n("Cursor Forward and &Select"), SHIFT + Key_Right, this,
+ SLOT(slotExtendSelectionForward()), actionCollection(),
+ "extend_selection_forward");
+
+ new KAction(i18n("Cursor Back Bar and Select"), SHIFT + CTRL + Key_Left, this,
+ SLOT(slotExtendSelectionBackwardBar()), actionCollection(),
+ "extend_selection_backward_bar");
+
+ new KAction(i18n("Cursor Forward Bar and Select"), SHIFT + CTRL + Key_Right, this,
+ SLOT(slotExtendSelectionForwardBar()), actionCollection(),
+ "extend_selection_forward_bar");
+
+ new KAction(i18n("Cursor to St&art"), 0,
+ /* #1025717: conflicting meanings for ctrl+a - dupe with Select All
+ Key_A + CTRL, */ this,
+ SLOT(slotJumpToStart()), actionCollection(),
+ "cursor_start");
+
+ new KAction(i18n("Cursor to &End"), 0, Key_E + CTRL, this,
+ SLOT(slotJumpToEnd()), actionCollection(),
+ "cursor_end");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-cursor-to-pointer")));
+ new KAction(i18n("Cursor to &Playback Pointer"), icon, 0, this,
+ SLOT(slotJumpCursorToPlayback()), actionCollection(),
+ "cursor_to_playback_pointer");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-play")));
+ KAction *play = new KAction(i18n("&Play"), icon, Key_Enter, this,
+ SIGNAL(play()), actionCollection(), "play");
+ // Alternative shortcut for Play
+ KShortcut playShortcut = play->shortcut();
+ playShortcut.append( KKey(Key_Return + CTRL) );
+ play->setShortcut(playShortcut);
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-stop")));
+ new KAction(i18n("&Stop"), icon, Key_Insert, this,
+ SIGNAL(stop()), actionCollection(), "stop");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-rewind")));
+ new KAction(i18n("Re&wind"), icon, Key_End, this,
+ SIGNAL(rewindPlayback()), actionCollection(),
+ "playback_pointer_back_bar");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-ffwd")));
+ new KAction(i18n("&Fast Forward"), icon, Key_PageDown, this,
+ SIGNAL(fastForwardPlayback()), actionCollection(),
+ "playback_pointer_forward_bar");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-rewind-end")));
+ new KAction(i18n("Rewind to &Beginning"), icon, 0, this,
+ SIGNAL(rewindPlaybackToBeginning()), actionCollection(),
+ "playback_pointer_start");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-ffwd-end")));
+ new KAction(i18n("Fast Forward to &End"), icon, 0, this,
+ SIGNAL(fastForwardPlaybackToEnd()), actionCollection(),
+ "playback_pointer_end");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-pointer-to-cursor")));
+ new KAction(i18n("Playback Pointer to &Cursor"), icon, 0, this,
+ SLOT(slotJumpPlaybackToCursor()), actionCollection(),
+ "playback_pointer_to_cursor");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-solo")));
+ new KToggleAction(i18n("&Solo"), icon, 0, this,
+ SLOT(slotToggleSolo()), actionCollection(),
+ "toggle_solo");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-tracking")));
+ (new KToggleAction(i18n("Scro&ll to Follow Playback"), icon, Key_Pause, this,
+ SLOT(slotToggleTracking()), actionCollection(),
+ "toggle_tracking"))->setChecked(m_playTracking);
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-panic")));
+ new KAction(i18n("Panic"), icon, Key_P + CTRL + ALT, this,
+ SIGNAL(panic()), actionCollection(), "panic");
+
+ new KAction(i18n("Set Loop to Selection"), Key_Semicolon + CTRL, this,
+ SLOT(slotPreviewSelection()), actionCollection(),
+ "preview_selection");
+
+ new KAction(i18n("Clear L&oop"), Key_Colon + CTRL, this,
+ SLOT(slotClearLoop()), actionCollection(),
+ "clear_loop");
+
+ new KAction(i18n("Clear Selection"), Key_Escape, this,
+ SLOT(slotClearSelection()), actionCollection(),
+ "clear_selection");
+
+ // icon = QIconSet(QCanvasPixmap(pixmapDir + "/toolbar/eventfilter.xpm"));
+ new KAction(i18n("&Filter Selection"), "filter", Key_F + CTRL, this,
+ SLOT(slotFilterSelection()), actionCollection(),
+ "filter_selection");
+
+ timeT crotchetDuration = Note(Note::Crotchet).getDuration();
+ m_snapValues.push_back(SnapGrid::NoSnap);
+ m_snapValues.push_back(SnapGrid::SnapToUnit);
+ m_snapValues.push_back(crotchetDuration / 16);
+ m_snapValues.push_back(crotchetDuration / 12);
+ m_snapValues.push_back(crotchetDuration / 8);
+ m_snapValues.push_back(crotchetDuration / 6);
+ m_snapValues.push_back(crotchetDuration / 4);
+ m_snapValues.push_back(crotchetDuration / 3);
+ m_snapValues.push_back(crotchetDuration / 2);
+ m_snapValues.push_back(crotchetDuration);
+ m_snapValues.push_back((crotchetDuration * 3) / 2);
+ m_snapValues.push_back(crotchetDuration * 2);
+ m_snapValues.push_back(SnapGrid::SnapToBeat);
+ m_snapValues.push_back(SnapGrid::SnapToBar);
+
+ for (unsigned int i = 0; i < m_snapValues.size(); i++) {
+
+ timeT d = m_snapValues[i];
+
+ if (d == SnapGrid::NoSnap) {
+ new KAction(i18n("&No Snap"), 0, this,
+ SLOT(slotSetSnapFromAction()),
+ actionCollection(), "snap_none");
+ } else if (d == SnapGrid::SnapToUnit) {
+ } else if (d == SnapGrid::SnapToBeat) {
+ new KAction(i18n("Snap to Bea&t"), Key_1, this,
+ SLOT(slotSetSnapFromAction()),
+ actionCollection(), "snap_beat");
+ } else if (d == SnapGrid::SnapToBar) {
+ new KAction(i18n("Snap to &Bar"), Key_5, this,
+ SLOT(slotSetSnapFromAction()),
+ actionCollection(), "snap_bar");
+ } else {
+
+ timeT err = 0;
+ QString label = NotationStrings::makeNoteMenuLabel(d, true, err);
+ QPixmap pixmap = NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeNoteMenuPixmap(d, err));
+
+ KShortcut cut = 0;
+ if (d == crotchetDuration / 16) cut = Key_0;
+ else if (d == crotchetDuration / 8) cut = Key_3;
+ else if (d == crotchetDuration / 4) cut = Key_6;
+ else if (d == crotchetDuration / 2) cut = Key_8;
+ else if (d == crotchetDuration) cut = Key_4;
+ else if (d == crotchetDuration * 2) cut = Key_2;
+
+ QString actionName = QString("snap_%1").arg(int((crotchetDuration * 4) / d));
+ if (d == (crotchetDuration * 3) / 2) actionName = "snap_3";
+ new KAction(i18n("Snap to %1").arg(label), pixmap, cut, this,
+ SLOT(slotSetSnapFromAction()), actionCollection(),
+ actionName);
+ }
+ }
+
+ //
+ // Settings menu
+ //
+ new KAction(i18n("Show Instrument Parameters"), 0, this,
+ SLOT(slotDockParametersBack()),
+ actionCollection(),
+ "show_inst_parameters");
+
+ new KToggleAction(i18n("Show Ch&ord Name Ruler"), 0, this,
+ SLOT(slotToggleChordsRuler()),
+ actionCollection(), "show_chords_ruler");
+
+ new KToggleAction(i18n("Show &Tempo Ruler"), 0, this,
+ SLOT(slotToggleTempoRuler()),
+ actionCollection(), "show_tempo_ruler");
+
+ createGUI(getRCFileName(), false);
+
+ if (getSegmentsOnlyRestsAndClefs())
+ actionCollection()->action("draw")->activate();
+ else
+ actionCollection()->action("select")->activate();
+}
+
+bool
+MatrixView::isInChordMode()
+{
+ return ((KToggleAction *)actionCollection()->action("chord_mode"))->
+ isChecked();
+}
+
+void MatrixView::slotDockParametersBack()
+{
+ m_dockLeft->dockBack();
+}
+
+void MatrixView::slotParametersClosed()
+{
+ stateChanged("parametersbox_closed");
+ m_dockVisible = false;
+}
+
+void MatrixView::slotParametersDockedBack(KDockWidget* dw, KDockWidget::DockPosition)
+{
+ if (dw == m_dockLeft) {
+ stateChanged("parametersbox_closed", KXMLGUIClient::StateReverse);
+ m_dockVisible = true;
+ }
+}
+
+void MatrixView::slotCheckTrackAssignments()
+{
+ Track *track =
+ m_staffs[0]->getSegment().getComposition()->
+ getTrackById(m_staffs[0]->getSegment().getTrack());
+
+ Instrument *instr = getDocument()->getStudio().
+ getInstrumentById(track->getInstrument());
+
+ m_parameterBox->useInstrument(instr);
+}
+
+void MatrixView::initStatusBar()
+{
+ KStatusBar* sb = statusBar();
+
+ m_hoveredOverAbsoluteTime = new QLabel(sb);
+ m_hoveredOverNoteName = new QLabel(sb);
+
+ m_hoveredOverAbsoluteTime->setMinimumWidth(175);
+ m_hoveredOverNoteName->setMinimumWidth(65);
+
+ sb->addWidget(m_hoveredOverAbsoluteTime);
+ sb->addWidget(m_hoveredOverNoteName);
+
+ m_insertModeLabel = new QLabel(sb);
+ m_insertModeLabel->setMinimumWidth(20);
+ sb->addWidget(m_insertModeLabel);
+
+ sb->insertItem(KTmpStatusMsg::getDefaultMsg(),
+ KTmpStatusMsg::getDefaultId(), 1);
+ sb->setItemAlignment(KTmpStatusMsg::getDefaultId(),
+ AlignLeft | AlignVCenter);
+
+ m_selectionCounter = new QLabel(sb);
+ sb->addWidget(m_selectionCounter);
+}
+
+void MatrixView::slotToolHelpChanged(const QString &s)
+{
+ QString msg = " " + s;
+ if (m_toolContextHelp == msg) return;
+ m_toolContextHelp = msg;
+
+ m_config->setGroup(GeneralOptionsConfigGroup);
+ if (!m_config->readBoolEntry("toolcontexthelp", true)) return;
+
+ if (m_mouseInCanvasView) statusBar()->changeItem(m_toolContextHelp, 1);
+}
+
+void MatrixView::slotMouseEnteredCanvasView()
+{
+ m_config->setGroup(GeneralOptionsConfigGroup);
+ if (!m_config->readBoolEntry("toolcontexthelp", true)) return;
+
+ m_mouseInCanvasView = true;
+ statusBar()->changeItem(m_toolContextHelp, 1);
+}
+
+void MatrixView::slotMouseLeftCanvasView()
+{
+ m_mouseInCanvasView = false;
+ statusBar()->changeItem(KTmpStatusMsg::getDefaultMsg(), 1);
+}
+
+bool MatrixView::applyLayout(int staffNo,
+ timeT startTime,
+ timeT endTime)
+{
+ Profiler profiler("MatrixView::applyLayout", true);
+
+ m_hlayout.reset();
+ m_vlayout.reset();
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+
+ if (staffNo >= 0 && (int)i != staffNo)
+ continue;
+
+ m_hlayout.scanStaff(*m_staffs[i], startTime, endTime);
+ m_vlayout.scanStaff(*m_staffs[i], startTime, endTime);
+ }
+
+ m_hlayout.finishLayout();
+ m_vlayout.finishLayout();
+
+ if (m_staffs[0]->getSegment().getEndMarkerTime() != m_lastEndMarkerTime ||
+ m_lastEndMarkerTime == 0 ||
+ isCompositionModified()) {
+ readjustCanvasSize();
+ m_lastEndMarkerTime = m_staffs[0]->getSegment().getEndMarkerTime();
+ }
+
+ return true;
+}
+
+void MatrixView::refreshSegment(Segment *segment,
+ timeT startTime, timeT endTime)
+{
+ Profiler profiler("MatrixView::refreshSegment", true);
+
+ MATRIX_DEBUG << "MatrixView::refreshSegment(" << startTime
+ << ", " << endTime << ")\n";
+
+ applyLayout( -1, startTime, endTime);
+
+ if (!segment)
+ segment = m_segments[0];
+
+ if (endTime == 0)
+ endTime = segment->getEndTime();
+ else if (startTime == endTime) {
+ startTime = segment->getStartTime();
+ endTime = segment->getEndTime();
+ }
+
+ m_staffs[0]->positionElements(startTime, endTime);
+ repaintRulers();
+}
+
+QSize MatrixView::getViewSize()
+{
+ return canvas()->size();
+}
+
+void MatrixView::setViewSize(QSize s)
+{
+ MATRIX_DEBUG << "MatrixView::setViewSize() w = " << s.width() << endl;
+
+ canvas()->resize(getXbyInverseWorldMatrix(s.width()), s.height());
+ getCanvasView()->resizeContents(s.width(), s.height());
+
+ MATRIX_DEBUG << "MatrixView::setViewSize() contentsWidth = " << getCanvasView()->contentsWidth() << endl;
+}
+
+void MatrixView::repaintRulers()
+{
+ for (unsigned int i = 0; i != m_propertyViewRulers.size(); i++)
+ m_propertyViewRulers[i].first->repaint();
+}
+
+void MatrixView::updateView()
+{
+ canvas()->update();
+}
+
+void MatrixView::setCurrentSelection(EventSelection* s, bool preview,
+ bool redrawNow)
+{
+ //!!! rather too much here shared with notationview -- could much of
+ // this be in editview?
+
+ if (m_currentEventSelection == s) {
+ updateQuantizeCombo();
+ return ;
+ }
+
+ if (m_currentEventSelection) {
+ getStaff(0)->positionElements(m_currentEventSelection->getStartTime(),
+ m_currentEventSelection->getEndTime());
+ }
+
+ EventSelection *oldSelection = m_currentEventSelection;
+ m_currentEventSelection = s;
+
+ timeT startA, endA, startB, endB;
+
+ if (oldSelection) {
+ startA = oldSelection->getStartTime();
+ endA = oldSelection->getEndTime();
+ startB = s ? s->getStartTime() : startA;
+ endB = s ? s->getEndTime() : endA;
+ } else {
+ // we know they can't both be null -- first thing we tested above
+ startA = startB = s->getStartTime();
+ endA = endB = s->getEndTime();
+ }
+
+ // refreshSegment takes start==end to mean refresh everything
+ if (startA == endA)
+ ++endA;
+ if (startB == endB)
+ ++endB;
+
+ bool updateRequired = true;
+
+ if (s) {
+
+ bool foundNewEvent = false;
+
+ for (EventSelection::eventcontainer::iterator i =
+ s->getSegmentEvents().begin();
+ i != s->getSegmentEvents().end(); ++i) {
+
+ if (oldSelection && oldSelection->getSegment() == s->getSegment()
+ && oldSelection->contains(*i))
+ continue;
+
+ foundNewEvent = true;
+
+ if (preview) {
+ long pitch;
+ if ((*i)->get<Int>(BaseProperties::PITCH, pitch)) {
+ long velocity = -1;
+ (void)((*i)->get<Int>(BaseProperties::VELOCITY, velocity));
+ if (!((*i)->has(BaseProperties::TIED_BACKWARD) &&
+ (*i)->get<Bool>(BaseProperties::TIED_BACKWARD)))
+ playNote(s->getSegment(), pitch, velocity);
+ }
+ }
+ }
+
+ if (!foundNewEvent) {
+ if (oldSelection &&
+ oldSelection->getSegment() == s->getSegment() &&
+ oldSelection->getSegmentEvents().size() ==
+ s->getSegmentEvents().size())
+ updateRequired = false;
+ }
+ }
+
+ if (updateRequired) {
+
+ if ((endA >= startB && endB >= startA) &&
+ (!s || !oldSelection ||
+ oldSelection->getSegment() == s->getSegment())) {
+
+ Segment &segment(s ? s->getSegment() :
+ oldSelection->getSegment());
+
+ if (redrawNow) {
+ // recolour the events now
+ getStaff(segment)->positionElements(std::min(startA, startB),
+ std::max(endA, endB));
+ } else {
+ // mark refresh status and then request a repaint
+ segment.getRefreshStatus
+ (m_segmentsRefreshStatusIds
+ [getStaff(segment)->getId()]).
+ push(std::min(startA, startB), std::max(endA, endB));
+ }
+
+ } else {
+ // do two refreshes, one for each -- here we know neither is null
+
+ if (redrawNow) {
+ // recolour the events now
+ getStaff(oldSelection->getSegment())->positionElements(startA,
+ endA);
+
+ getStaff(s->getSegment())->positionElements(startB, endB);
+ } else {
+ // mark refresh status and then request a repaint
+
+ oldSelection->getSegment().getRefreshStatus
+ (m_segmentsRefreshStatusIds
+ [getStaff(oldSelection->getSegment())->getId()]).
+ push(startA, endA);
+
+ s->getSegment().getRefreshStatus
+ (m_segmentsRefreshStatusIds
+ [getStaff(s->getSegment())->getId()]).
+ push(startB, endB);
+ }
+ }
+ }
+
+ delete oldSelection;
+
+ if (s) {
+
+ int eventsSelected = s->getSegmentEvents().size();
+ m_selectionCounter->setText
+ (i18n(" 1 event selected ",
+ " %n events selected ", eventsSelected));
+
+ } else {
+ m_selectionCounter->setText(i18n(" No selection "));
+ }
+
+ m_selectionCounter->update();
+
+ slotSetCurrentVelocityFromSelection();
+
+ // Clear states first, then enter only those ones that apply
+ // (so as to avoid ever clearing one after entering another, in
+ // case the two overlap at all)
+ stateChanged("have_selection", KXMLGUIClient::StateReverse);
+ stateChanged("have_notes_in_selection", KXMLGUIClient::StateReverse);
+ stateChanged("have_rests_in_selection", KXMLGUIClient::StateReverse);
+
+ if (s) {
+ stateChanged("have_selection", KXMLGUIClient::StateNoReverse);
+ if (s->contains(Note::EventType)) {
+ stateChanged("have_notes_in_selection",
+ KXMLGUIClient::StateNoReverse);
+ }
+ if (s->contains(Note::EventRestType)) {
+ stateChanged("have_rests_in_selection",
+ KXMLGUIClient::StateNoReverse);
+ }
+ }
+
+ updateQuantizeCombo();
+
+ if (redrawNow)
+ updateView();
+ else
+ update();
+}
+
+void MatrixView::updateQuantizeCombo()
+{
+ timeT unit = 0;
+
+ if (m_currentEventSelection) {
+ unit =
+ BasicQuantizer::getStandardQuantization
+ (m_currentEventSelection);
+ } else {
+ unit =
+ BasicQuantizer::getStandardQuantization
+ (&(m_staffs[0]->getSegment()));
+ }
+
+ for (unsigned int i = 0; i < m_quantizations.size(); ++i) {
+ if (unit == m_quantizations[i]) {
+ m_quantizeCombo->setCurrentItem(i);
+ return ;
+ }
+ }
+
+ m_quantizeCombo->setCurrentItem(m_quantizeCombo->count() - 1); // "Off"
+}
+
+void MatrixView::slotPaintSelected()
+{
+ EditTool* painter = m_toolBox->getTool(MatrixPainter::ToolName);
+
+ setTool(painter);
+}
+
+void MatrixView::slotEraseSelected()
+{
+ EditTool* eraser = m_toolBox->getTool(MatrixEraser::ToolName);
+
+ setTool(eraser);
+}
+
+void MatrixView::slotSelectSelected()
+{
+ EditTool* selector = m_toolBox->getTool(MatrixSelector::ToolName);
+
+ connect(selector, SIGNAL(gotSelection()),
+ this, SLOT(slotNewSelection()));
+
+ connect(selector, SIGNAL(editTriggerSegment(int)),
+ this, SIGNAL(editTriggerSegment(int)));
+
+ setTool(selector);
+}
+
+void MatrixView::slotMoveSelected()
+{
+ EditTool* mover = m_toolBox->getTool(MatrixMover::ToolName);
+
+ setTool(mover);
+}
+
+void MatrixView::slotResizeSelected()
+{
+ EditTool* resizer = m_toolBox->getTool(MatrixResizer::ToolName);
+
+ setTool(resizer);
+}
+
+void MatrixView::slotTransformsQuantize()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ QuantizeDialog dialog(this);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ KTmpStatusMsg msg(i18n("Quantizing..."), this);
+ addCommandToHistory(new EventQuantizeCommand
+ (*m_currentEventSelection,
+ dialog.getQuantizer()));
+ }
+}
+
+void MatrixView::slotTransformsRepeatQuantize()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ KTmpStatusMsg msg(i18n("Quantizing..."), this);
+ addCommandToHistory(new EventQuantizeCommand
+ (*m_currentEventSelection,
+ "Quantize Dialog Grid", false)); // no i18n (config group name)
+}
+
+void MatrixView::slotTransformsCollapseNotes()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Collapsing notes..."), this);
+
+ addCommandToHistory(new CollapseNotesCommand
+ (*m_currentEventSelection));
+}
+
+void MatrixView::slotTransformsLegato()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ KTmpStatusMsg msg(i18n("Making legato..."), this);
+ addCommandToHistory(new EventQuantizeCommand
+ (*m_currentEventSelection,
+ new LegatoQuantizer(0))); // no quantization
+}
+
+void MatrixView::slotMousePressed(timeT time, int pitch,
+ QMouseEvent* e, MatrixElement* el)
+{
+ MATRIX_DEBUG << "MatrixView::mousePressed at pitch "
+ << pitch << ", time " << time << endl;
+
+ // Don't allow moving/insertion before the beginning of the
+ // segment
+ timeT curSegmentStartTime = getCurrentSegment()->getStartTime();
+ if (curSegmentStartTime > time)
+ time = curSegmentStartTime;
+
+ m_tool->handleMousePress(time, pitch, 0, e, el);
+
+ if (e->button() != RightButton) {
+ getCanvasView()->startAutoScroll();
+ }
+
+ // play a preview
+ //playPreview(pitch);
+}
+
+void MatrixView::slotMouseMoved(timeT time, int pitch, QMouseEvent* e)
+{
+ // Don't allow moving/insertion before the beginning of the
+ // segment
+ timeT curSegmentStartTime = getCurrentSegment()->getStartTime();
+ if (curSegmentStartTime > time)
+ time = curSegmentStartTime;
+
+ if (activeItem()) {
+ activeItem()->handleMouseMove(e);
+ updateView();
+ } else {
+ int follow = m_tool->handleMouseMove(time, pitch, e);
+ getCanvasView()->setScrollDirectionConstraint(follow);
+
+ // if (follow != RosegardenCanvasView::NoFollow) {
+ // getCanvasView()->doAutoScroll();
+ // }
+
+ // play a preview
+ if (pitch != m_previousEvPitch) {
+ //playPreview(pitch);
+ m_previousEvPitch = pitch;
+ }
+ }
+
+}
+
+void MatrixView::slotMouseReleased(timeT time, int pitch, QMouseEvent* e)
+{
+ // Don't allow moving/insertion before the beginning of the
+ // segment
+ timeT curSegmentStartTime = getCurrentSegment()->getStartTime();
+ if (curSegmentStartTime > time)
+ time = curSegmentStartTime;
+
+ if (activeItem()) {
+ activeItem()->handleMouseRelease(e);
+ setActiveItem(0);
+ updateView();
+ }
+
+ // send the real event time now (not adjusted for beginning of bar)
+ m_tool->handleMouseRelease(time, pitch, e);
+ m_previousEvPitch = 0;
+ getCanvasView()->stopAutoScroll();
+}
+
+void
+MatrixView::slotHoveredOverNoteChanged(int evPitch,
+ bool haveEvent,
+ timeT evTime)
+{
+ MidiPitchLabel label(evPitch);
+
+ if (haveEvent) {
+
+ m_haveHoveredOverNote = true;
+
+ int bar, beat, fraction, remainder;
+ getDocument()->getComposition().getMusicalTimeForAbsoluteTime
+ (evTime, bar, beat, fraction, remainder);
+
+ RealTime rt =
+ getDocument()->getComposition().getElapsedRealTime(evTime);
+ long ms = rt.msec();
+
+ QString msg = i18n("Note: %1 (%2.%3s)")
+ .arg(QString("%1-%2-%3-%4")
+ .arg(QString("%1").arg(bar + 1).rightJustify(3, '0'))
+ .arg(QString("%1").arg(beat).rightJustify(2, '0'))
+ .arg(QString("%1").arg(fraction).rightJustify(2, '0'))
+ .arg(QString("%1").arg(remainder).rightJustify(2, '0')))
+ .arg(rt.sec)
+ .arg(QString("%1").arg(ms).rightJustify(3, '0'));
+
+ m_hoveredOverAbsoluteTime->setText(msg);
+ }
+
+ m_haveHoveredOverNote = false;
+
+ m_hoveredOverNoteName->setText(i18n("%1 (%2)")
+ .arg(label.getQString())
+ .arg(evPitch));
+
+ m_pitchRuler->drawHoverNote(evPitch);
+}
+
+void
+MatrixView::slotHoveredOverKeyChanged(unsigned int y)
+{
+ MatrixStaff& staff = *(m_staffs[0]);
+
+ int evPitch = staff.getHeightAtCanvasCoords( -1, y);
+
+ if (evPitch != m_previousEvPitch) {
+ MidiPitchLabel label(evPitch);
+ m_hoveredOverNoteName->setText(QString("%1 (%2)").
+ arg(label.getQString()).arg(evPitch));
+ m_previousEvPitch = evPitch;
+ }
+}
+
+void
+MatrixView::slotHoveredOverAbsoluteTimeChanged(unsigned int time)
+{
+ if (m_haveHoveredOverNote) return;
+
+ timeT t = time;
+
+ int bar, beat, fraction, remainder;
+ getDocument()->getComposition().getMusicalTimeForAbsoluteTime
+ (t, bar, beat, fraction, remainder);
+
+ RealTime rt =
+ getDocument()->getComposition().getElapsedRealTime(t);
+ long ms = rt.msec();
+
+ // At the advice of doc.trolltech.com/3.0/qstring.html#sprintf
+ // we replaced this QString format("%ld (%ld.%03lds)");
+ // to support Unicode
+
+ QString message = i18n("Time: %1 (%2.%3s)")
+ .arg(QString("%1-%2-%3-%4")
+ .arg(QString("%1").arg(bar + 1).rightJustify(3, '0'))
+ .arg(QString("%1").arg(beat).rightJustify(2, '0'))
+ .arg(QString("%1").arg(fraction).rightJustify(2, '0'))
+ .arg(QString("%1").arg(remainder).rightJustify(2, '0')))
+ .arg(rt.sec)
+ .arg(QString("%1").arg(ms).rightJustify(3, '0'));
+
+ m_hoveredOverAbsoluteTime->setText(message);
+}
+
+void
+MatrixView::slotSetPointerPosition(timeT time)
+{
+ slotSetPointerPosition(time, m_playTracking);
+}
+
+void
+MatrixView::slotSetPointerPosition(timeT time, bool scroll)
+{
+ Composition &comp = getDocument()->getComposition();
+ int barNo = comp.getBarNumber(time);
+
+ if (barNo >= m_hlayout.getLastVisibleBarOnStaff(*m_staffs[0])) {
+
+ Segment &seg = m_staffs[0]->getSegment();
+
+ if (seg.isRepeating() && time < seg.getRepeatEndTime()) {
+ time =
+ seg.getStartTime() +
+ ((time - seg.getStartTime()) %
+ (seg.getEndMarkerTime() - seg.getStartTime()));
+ m_staffs[0]->setPointerPosition(m_hlayout, time);
+ } else {
+ m_staffs[0]->hidePointer();
+ scroll = false;
+ }
+ } else if (barNo < m_hlayout.getFirstVisibleBarOnStaff(*m_staffs[0])) {
+ m_staffs[0]->hidePointer();
+ scroll = false;
+ } else {
+ m_staffs[0]->setPointerPosition(m_hlayout, time);
+ }
+
+ if (scroll && !getCanvasView()->isAutoScrolling())
+ getCanvasView()->slotScrollHoriz(static_cast<int>(getXbyWorldMatrix(m_hlayout.getXForTime(time))));
+
+ updateView();
+}
+
+void
+MatrixView::slotSetInsertCursorPosition(timeT time, bool scroll)
+{
+ //!!! For now. Probably unlike slotSetPointerPosition this one
+ // should snap to the nearest event or grid line.
+
+ m_staffs[0]->setInsertCursorPosition(m_hlayout, time);
+
+ if (scroll && !getCanvasView()->isAutoScrolling()) {
+ getCanvasView()->slotScrollHoriz
+ (static_cast<int>(getXbyWorldMatrix(m_hlayout.getXForTime(time))));
+ }
+
+ updateView();
+}
+
+void MatrixView::slotEditCut()
+{
+ MATRIX_DEBUG << "MatrixView::slotEditCut()\n";
+
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Cutting selection to clipboard..."), this);
+
+ addCommandToHistory(new CutCommand(*m_currentEventSelection,
+ getDocument()->getClipboard()));
+}
+
+void MatrixView::slotEditCopy()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Copying selection to clipboard..."), this);
+
+ addCommandToHistory(new CopyCommand(*m_currentEventSelection,
+ getDocument()->getClipboard()));
+
+ emit usedSelection();
+}
+
+void MatrixView::slotEditPaste()
+{
+ if (getDocument()->getClipboard()->isEmpty()) {
+ slotStatusHelpMsg(i18n("Clipboard is empty"));
+ return ;
+ }
+
+ KTmpStatusMsg msg(i18n("Inserting clipboard contents..."), this);
+
+ PasteEventsCommand *command = new PasteEventsCommand
+ (m_staffs[0]->getSegment(), getDocument()->getClipboard(),
+ getInsertionTime(), PasteEventsCommand::MatrixOverlay);
+
+ if (!command->isPossible()) {
+ slotStatusHelpMsg(i18n("Couldn't paste at this point"));
+ } else {
+ addCommandToHistory(command);
+ setCurrentSelection(new EventSelection(command->getPastedEvents()));
+ }
+}
+
+void MatrixView::slotEditDelete()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Deleting selection..."), this);
+
+ addCommandToHistory(new EraseCommand(*m_currentEventSelection));
+
+ // clear and clear
+ setCurrentSelection(0, false);
+}
+
+void MatrixView::slotKeyPressed(unsigned int y, bool repeating)
+{
+ slotHoveredOverKeyChanged(y);
+
+ getCanvasView()->slotScrollVertSmallSteps(y);
+
+ Composition &comp = getDocument()->getComposition();
+ Studio &studio = getDocument()->getStudio();
+
+ MatrixStaff& staff = *(m_staffs[0]);
+ MidiByte evPitch = staff.getHeightAtCanvasCoords( -1, y);
+
+ // Don't do anything if we're part of a run up the keyboard
+ // and the pitch hasn't changed
+ //
+ if (m_lastNote == evPitch && repeating)
+ return ;
+
+ // Save value
+ m_lastNote = evPitch;
+ if (!repeating)
+ m_firstNote = evPitch;
+
+ Track *track = comp.getTrackById(
+ staff.getSegment().getTrack());
+
+ Instrument *ins =
+ studio.getInstrumentById(track->getInstrument());
+
+ // check for null instrument
+ //
+ if (ins == 0)
+ return ;
+
+ MappedEvent mE(ins->getId(),
+ MappedEvent::MidiNote,
+ evPitch + staff.getSegment().getTranspose(),
+ MidiMaxValue,
+ RealTime::zeroTime,
+ RealTime::zeroTime,
+ RealTime::zeroTime);
+ StudioControl::sendMappedEvent(mE);
+
+}
+
+void MatrixView::slotKeySelected(unsigned int y, bool repeating)
+{
+ slotHoveredOverKeyChanged(y);
+
+ getCanvasView()->slotScrollVertSmallSteps(y);
+
+ MatrixStaff& staff = *(m_staffs[0]);
+ Segment &segment(staff.getSegment());
+ MidiByte evPitch = staff.getHeightAtCanvasCoords( -1, y);
+
+ // Don't do anything if we're part of a run up the keyboard
+ // and the pitch hasn't changed
+ //
+ if (m_lastNote == evPitch && repeating)
+ return ;
+
+ // Save value
+ m_lastNote = evPitch;
+ if (!repeating)
+ m_firstNote = evPitch;
+
+ EventSelection *s = new EventSelection(segment);
+
+ for (Segment::iterator i = segment.begin();
+ segment.isBeforeEndMarker(i); ++i) {
+
+ if ((*i)->isa(Note::EventType) &&
+ (*i)->has(BaseProperties::PITCH)) {
+
+ MidiByte p = (*i)->get
+ <Int>
+ (BaseProperties::PITCH);
+ if (p >= std::min(m_firstNote, evPitch) &&
+ p <= std::max(m_firstNote, evPitch)) {
+ s->addEvent(*i);
+ }
+ }
+ }
+
+ if (m_currentEventSelection) {
+ // allow addFromSelection to deal with eliminating duplicates
+ s->addFromSelection(m_currentEventSelection);
+ }
+
+ setCurrentSelection(s, false);
+
+ // now play the note as well
+
+ Composition &comp = getDocument()->getComposition();
+ Studio &studio = getDocument()->getStudio();
+ Track *track = comp.getTrackById(segment.getTrack());
+ Instrument *ins =
+ studio.getInstrumentById(track->getInstrument());
+
+ // check for null instrument
+ //
+ if (ins == 0)
+ return ;
+
+ MappedEvent mE(ins->getId(),
+ MappedEvent::MidiNoteOneShot,
+ evPitch + segment.getTranspose(),
+ MidiMaxValue,
+ RealTime::zeroTime,
+ RealTime(0, 250000000),
+ RealTime::zeroTime);
+ StudioControl::sendMappedEvent(mE);
+}
+
+void MatrixView::slotKeyReleased(unsigned int y, bool repeating)
+{
+ MatrixStaff& staff = *(m_staffs[0]);
+ int evPitch = staff.getHeightAtCanvasCoords(-1, y);
+
+ if (m_lastNote == evPitch && repeating)
+ return;
+
+ Rosegarden::Segment &segment(staff.getSegment());
+
+ // send note off (note on at zero velocity)
+
+ Rosegarden::Composition &comp = getDocument()->getComposition();
+ Rosegarden::Studio &studio = getDocument()->getStudio();
+ Rosegarden::Track *track = comp.getTrackById(segment.getTrack());
+ Rosegarden::Instrument *ins =
+ studio.getInstrumentById(track->getInstrument());
+
+ // check for null instrument
+ //
+ if (ins == 0)
+ return;
+
+ evPitch = evPitch + segment.getTranspose();
+ if (evPitch < 0 || evPitch > 127) return;
+
+ Rosegarden::MappedEvent mE(ins->getId(),
+ Rosegarden::MappedEvent::MidiNote,
+ evPitch,
+ 0,
+ Rosegarden::RealTime::zeroTime,
+ Rosegarden::RealTime::zeroTime,
+ Rosegarden::RealTime::zeroTime);
+ Rosegarden::StudioControl::sendMappedEvent(mE);
+}
+
+void MatrixView::slotVerticalScrollPianoKeyboard(int y)
+{
+ if (m_pianoView) // check that the piano view still exists (see dtor)
+ m_pianoView->setContentsPos(0, y);
+}
+
+void MatrixView::slotInsertNoteFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ Segment &segment = *getCurrentSegment();
+ int pitch = 0;
+
+ Accidental accidental =
+ Accidentals::NoAccidental;
+
+ timeT time(getInsertionTime());
+ ::Rosegarden::Key key = segment.getKeyAtTime(time);
+ Clef clef = segment.getClefAtTime(time);
+
+ try {
+
+ pitch = getPitchFromNoteInsertAction(name, accidental, clef, key);
+
+ } catch (...) {
+
+ KMessageBox::sorry
+ (this, i18n("Unknown note insert action %1").arg(name));
+ return ;
+ }
+
+ KTmpStatusMsg msg(i18n("Inserting note"), this);
+
+ MATRIX_DEBUG << "Inserting note at pitch " << pitch << endl;
+
+ Event modelEvent(Note::EventType, 0, 1);
+ modelEvent.set<Int>(BaseProperties::PITCH, pitch);
+ modelEvent.set<String>(BaseProperties::ACCIDENTAL, accidental);
+ timeT endTime(time + m_snapGrid->getSnapTime(time));
+
+ MatrixInsertionCommand* command =
+ new MatrixInsertionCommand(segment, time, endTime, &modelEvent);
+
+ addCommandToHistory(command);
+
+ if (!isInChordMode()) {
+ slotSetInsertCursorPosition(endTime);
+ }
+}
+
+void MatrixView::closeWindow()
+{
+ delete this;
+}
+
+bool MatrixView::canPreviewAnotherNote()
+{
+ static time_t lastCutOff = 0;
+ static int sinceLastCutOff = 0;
+
+ time_t now = time(0);
+ ++sinceLastCutOff;
+
+ if ((now - lastCutOff) > 0) {
+ sinceLastCutOff = 0;
+ lastCutOff = now;
+ } else {
+ if (sinceLastCutOff >= 20) {
+ // don't permit more than 20 notes per second, to avoid
+ // gungeing up the sound drivers
+ MATRIX_DEBUG << "Rejecting preview (too busy)" << endl;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void MatrixView::playNote(Event *event)
+{
+ // Only play note events
+ //
+ if (!event->isa(Note::EventType))
+ return ;
+
+ Composition &comp = getDocument()->getComposition();
+ Studio &studio = getDocument()->getStudio();
+
+ // Get the Instrument
+ //
+ Track *track = comp.getTrackById(
+ m_staffs[0]->getSegment().getTrack());
+
+ Instrument *ins =
+ studio.getInstrumentById(track->getInstrument());
+
+ if (ins == 0)
+ return ;
+
+ if (!canPreviewAnotherNote())
+ return ;
+
+ // Get a velocity
+ //
+ MidiByte velocity = MidiMaxValue / 4; // be easy on the user's ears
+ long eventVelocity = 0;
+ if (event->get
+ <Int>(BaseProperties::VELOCITY, eventVelocity))
+ velocity = eventVelocity;
+
+ RealTime duration =
+ comp.getElapsedRealTime(event->getDuration());
+
+ // create
+ MappedEvent mE(ins->getId(),
+ MappedEvent::MidiNoteOneShot,
+ (MidiByte)
+ event->get
+ <Int>
+ (BaseProperties::PITCH) +
+ m_staffs[0]->getSegment().getTranspose(),
+ velocity,
+ RealTime::zeroTime,
+ duration,
+ RealTime::zeroTime);
+
+ StudioControl::sendMappedEvent(mE);
+}
+
+void MatrixView::playNote(const Segment &segment, int pitch,
+ int velocity)
+{
+ Composition &comp = getDocument()->getComposition();
+ Studio &studio = getDocument()->getStudio();
+
+ Track *track = comp.getTrackById(segment.getTrack());
+
+ Instrument *ins =
+ studio.getInstrumentById(track->getInstrument());
+
+ // check for null instrument
+ //
+ if (ins == 0)
+ return ;
+
+ if (velocity < 0)
+ velocity = getCurrentVelocity();
+
+ MappedEvent mE(ins->getId(),
+ MappedEvent::MidiNoteOneShot,
+ pitch + segment.getTranspose(),
+ velocity,
+ RealTime::zeroTime,
+ RealTime(0, 250000000),
+ RealTime::zeroTime);
+
+ StudioControl::sendMappedEvent(mE);
+}
+
+MatrixStaff*
+MatrixView::getStaff(const Segment &segment)
+{
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ if (&(m_staffs[i]->getSegment()) == &segment)
+ return m_staffs[i];
+ }
+
+ return 0;
+}
+
+void
+MatrixView::setSingleSelectedEvent(int staffNo, Event *event,
+ bool preview, bool redrawNow)
+{
+ setSingleSelectedEvent(getStaff(staffNo)->getSegment(), event,
+ preview, redrawNow);
+}
+
+void
+MatrixView::setSingleSelectedEvent(Segment &segment,
+ Event *event,
+ bool preview, bool redrawNow)
+{
+ setCurrentSelection(0, false);
+
+ EventSelection *selection = new EventSelection(segment);
+ selection->addEvent(event);
+
+ //!!!
+ // this used to say
+ // setCurrentSelection(selection, true)
+ // since the default arg for preview is false, this changes the
+ // default semantics -- test what circumstance this matters in
+ // and choose an acceptable solution for both matrix & notation
+ setCurrentSelection(selection, preview, redrawNow);
+}
+
+void
+MatrixView::slotNewSelection()
+{
+ MATRIX_DEBUG << "MatrixView::slotNewSelection\n";
+
+ // m_parameterBox->setSelection(m_currentEventSelection);
+}
+
+void
+MatrixView::slotSetSnapFromIndex(int s)
+{
+ slotSetSnap(m_snapValues[s]);
+}
+
+void
+MatrixView::slotSetSnapFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ if (name.left(5) == "snap_") {
+ int snap = name.right(name.length() - 5).toInt();
+ if (snap > 0) {
+ slotSetSnap(Note(Note::Semibreve).getDuration() / snap);
+ } else if (name == "snap_none") {
+ slotSetSnap(SnapGrid::NoSnap);
+ } else if (name == "snap_beat") {
+ slotSetSnap(SnapGrid::SnapToBeat);
+ } else if (name == "snap_bar") {
+ slotSetSnap(SnapGrid::SnapToBar);
+ } else if (name == "snap_unit") {
+ slotSetSnap(SnapGrid::SnapToUnit);
+ } else {
+ MATRIX_DEBUG << "Warning: MatrixView::slotSetSnapFromAction: unrecognised action " << name << endl;
+ }
+ }
+}
+
+void
+MatrixView::slotSetSnap(timeT t)
+{
+ MATRIX_DEBUG << "MatrixView::slotSetSnap: time is " << t << endl;
+ m_snapGrid->setSnapTime(t);
+
+ for (unsigned int i = 0; i < m_snapValues.size(); ++i) {
+ if (m_snapValues[i] == t) {
+ m_snapGridCombo->setCurrentItem(i);
+ break;
+ }
+ }
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i)
+ m_staffs[i]->sizeStaff(m_hlayout);
+
+ m_segments[0]->setSnapGridSize(t);
+
+ m_config->setGroup(MatrixViewConfigGroup);
+ m_config->writeEntry("Snap Grid Size", t);
+
+ updateView();
+}
+
+void
+MatrixView::slotQuantizeSelection(int q)
+{
+ MATRIX_DEBUG << "MatrixView::slotQuantizeSelection\n";
+
+ timeT unit =
+ ((unsigned int)q < m_quantizations.size() ? m_quantizations[q] : 0);
+
+ Quantizer *quant =
+ new BasicQuantizer
+ (unit ? unit :
+ Note(Note::Shortest).getDuration(), false);
+
+ if (unit) {
+ KTmpStatusMsg msg(i18n("Quantizing..."), this);
+ if (m_currentEventSelection &&
+ m_currentEventSelection->getAddedEvents()) {
+ addCommandToHistory(new EventQuantizeCommand
+ (*m_currentEventSelection, quant));
+ } else {
+ Segment &s = m_staffs[0]->getSegment();
+ addCommandToHistory(new EventQuantizeCommand
+ (s, s.getStartTime(), s.getEndMarkerTime(),
+ quant));
+ }
+ } else {
+ KTmpStatusMsg msg(i18n("Unquantizing..."), this);
+ if (m_currentEventSelection &&
+ m_currentEventSelection->getAddedEvents()) {
+ addCommandToHistory(new EventUnquantizeCommand
+ (*m_currentEventSelection, quant));
+ } else {
+ Segment &s = m_staffs[0]->getSegment();
+ addCommandToHistory(new EventUnquantizeCommand
+ (s, s.getStartTime(), s.getEndMarkerTime(),
+ quant));
+ }
+ }
+}
+
+void
+MatrixView::initActionsToolbar()
+{
+ MATRIX_DEBUG << "MatrixView::initActionsToolbar" << endl;
+
+ KToolBar *actionsToolbar = toolBar("Actions Toolbar");
+
+ if (!actionsToolbar) {
+ MATRIX_DEBUG << "MatrixView::initActionsToolbar - "
+ << "tool bar not found" << endl;
+ return ;
+ }
+
+ // The SnapGrid combo and Snap To... menu items
+ //
+ QLabel *sLabel = new QLabel(i18n(" Grid: "), actionsToolbar, "kde toolbar widget");
+ sLabel->setIndent(10);
+
+ QPixmap noMap = NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("menu-no-note"));
+
+ m_snapGridCombo = new KComboBox(actionsToolbar);
+
+ for (unsigned int i = 0; i < m_snapValues.size(); i++) {
+
+ timeT d = m_snapValues[i];
+
+ if (d == SnapGrid::NoSnap) {
+ m_snapGridCombo->insertItem(i18n("None"));
+ } else if (d == SnapGrid::SnapToUnit) {
+ m_snapGridCombo->insertItem(i18n("Unit"));
+ } else if (d == SnapGrid::SnapToBeat) {
+ m_snapGridCombo->insertItem(i18n("Beat"));
+ } else if (d == SnapGrid::SnapToBar) {
+ m_snapGridCombo->insertItem(i18n("Bar"));
+ } else {
+ timeT err = 0;
+ QString label = NotationStrings::makeNoteMenuLabel(d, true, err);
+ QPixmap pixmap = NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeNoteMenuPixmap(d, err));
+ m_snapGridCombo->insertItem((err ? noMap : pixmap), label);
+ }
+
+ if (d == m_snapGrid->getSnapSetting()) {
+ m_snapGridCombo->setCurrentItem(m_snapGridCombo->count() - 1);
+ }
+ }
+
+ connect(m_snapGridCombo, SIGNAL(activated(int)),
+ this, SLOT(slotSetSnapFromIndex(int)));
+
+ // Velocity combo. Not a spin box, because the spin box is too
+ // slow to use unless we make it typeable into, and then it takes
+ // focus away from our more important widgets
+
+ QLabel *vlabel = new QLabel(i18n(" Velocity: "), actionsToolbar, "kde toolbar widget");
+ vlabel->setIndent(10);
+
+ m_velocityCombo = new KComboBox(actionsToolbar);
+ for (int i = 0; i <= 127; ++i) {
+ m_velocityCombo->insertItem(QString("%1").arg(i));
+ }
+ m_velocityCombo->setCurrentItem(100); //!!! associate with segment
+
+ // Quantize combo
+ //
+ QLabel *qLabel = new QLabel(i18n(" Quantize: "), actionsToolbar, "kde toolbar widget");
+ qLabel->setIndent(10);
+
+ m_quantizeCombo = new KComboBox(actionsToolbar);
+
+ for (unsigned int i = 0; i < m_quantizations.size(); ++i) {
+
+ timeT time = m_quantizations[i];
+ timeT error = 0;
+ QString label = NotationStrings::makeNoteMenuLabel(time, true, error);
+ QPixmap pmap = NotePixmapFactory::toQPixmap(NotePixmapFactory::makeNoteMenuPixmap(time, error));
+ m_quantizeCombo->insertItem(error ? noMap : pmap, label);
+ }
+
+ m_quantizeCombo->insertItem(noMap, i18n("Off"));
+
+ connect(m_quantizeCombo, SIGNAL(activated(int)),
+ this, SLOT(slotQuantizeSelection(int)));
+}
+
+void
+MatrixView::initZoomToolbar()
+{
+ MATRIX_DEBUG << "MatrixView::initZoomToolbar" << endl;
+
+ KToolBar *zoomToolbar = toolBar("Zoom Toolbar");
+
+ if (!zoomToolbar) {
+ MATRIX_DEBUG << "MatrixView::initZoomToolbar - "
+ << "tool bar not found" << endl;
+ return ;
+ }
+
+ std::vector<double> zoomSizes; // in units-per-pixel
+
+ //double defaultBarWidth44 = 100.0;
+ //double duration44 = TimeSignature(4,4).getBarDuration();
+
+ static double factors[] = { 0.025, 0.05, 0.1, 0.2, 0.5,
+ 1.0, 1.5, 2.5, 5.0, 10.0, 20.0 };
+ // Zoom labels
+ //
+ for (unsigned int i = 0; i < sizeof(factors) / sizeof(factors[0]); ++i) {
+// zoomSizes.push_back(duration44 / (defaultBarWidth44 * factors[i]));
+
+// zoomSizes.push_back(factors[i] / 2); // GROSS HACK - see in matrixstaff.h - BREAKS MATRIX VIEW, see bug 1000595
+ zoomSizes.push_back(factors[i]);
+ }
+
+ m_hZoomSlider = new ZoomSlider<double>
+ (zoomSizes, -1, QSlider::Horizontal, zoomToolbar, "kde toolbar widget");
+ m_hZoomSlider->setTracking(true);
+ m_hZoomSlider->setFocusPolicy(QWidget::NoFocus);
+
+ m_zoomLabel = new QLabel(zoomToolbar, "kde toolbar widget");
+ m_zoomLabel->setIndent(10);
+ m_zoomLabel->setFixedWidth(80);
+
+ connect(m_hZoomSlider,
+ SIGNAL(valueChanged(int)),
+ SLOT(slotChangeHorizontalZoom(int)));
+
+}
+
+void
+MatrixView::slotChangeHorizontalZoom(int)
+{
+ double zoomValue = m_hZoomSlider->getCurrentSize();
+
+ // m_zoomLabel->setText(i18n("%1%").arg(zoomValue*100.0 * 2)); // GROSS HACK - see in matrixstaff.h - BREAKS MATRIX VIEW, see bug 1000595
+ m_zoomLabel->setText(i18n("%1%").arg(zoomValue*100.0));
+
+ MATRIX_DEBUG << "MatrixView::slotChangeHorizontalZoom() : zoom factor = "
+ << zoomValue << endl;
+
+ m_referenceRuler->setHScaleFactor(zoomValue);
+
+ if (m_tempoRuler)
+ m_tempoRuler->repaint();
+ if (m_chordNameRuler)
+ m_chordNameRuler->repaint();
+
+ // Set zoom matrix
+ //
+ QWMatrix zoomMatrix;
+ zoomMatrix.scale(zoomValue, 1.0);
+ m_canvasView->setWorldMatrix(zoomMatrix);
+
+ // make control rulers zoom too
+ //
+ setControlRulersZoom(zoomMatrix);
+
+ if (m_topStandardRuler)
+ m_topStandardRuler->setHScaleFactor(zoomValue);
+ if (m_bottomStandardRuler)
+ m_bottomStandardRuler->setHScaleFactor(zoomValue);
+
+ for (unsigned int i = 0; i < m_propertyViewRulers.size(); ++i) {
+ m_propertyViewRulers[i].first->setHScaleFactor(zoomValue);
+ m_propertyViewRulers[i].first->repaint();
+ }
+
+ if (m_topStandardRuler)
+ m_topStandardRuler->update();
+ if (m_bottomStandardRuler)
+ m_bottomStandardRuler->update();
+
+ m_config->setGroup(MatrixViewConfigGroup);
+ m_config->writeEntry("Zoom Level", zoomValue);
+
+ // If you do adjust the viewsize then please remember to
+ // either re-center() or remember old scrollbar position
+ // and restore.
+ //
+
+ int newWidth = computePostLayoutWidth();
+
+ // int newWidth = int(getXbyWorldMatrix(getCanvasView()->canvas()->width()));
+
+ // We DO NOT resize the canvas(), only the area it's displaying on
+ //
+ getCanvasView()->resizeContents(newWidth, getViewSize().height());
+
+ // This forces a refresh of the h. scrollbar, even if the canvas width
+ // hasn't changed
+ //
+ getCanvasView()->polish();
+
+ getCanvasView()->slotScrollHoriz
+ (getXbyWorldMatrix(m_staffs[0]->getLayoutXOfInsertCursor()));
+}
+
+void
+MatrixView::slotZoomIn()
+{
+ m_hZoomSlider->increment();
+}
+
+void
+MatrixView::slotZoomOut()
+{
+ m_hZoomSlider->decrement();
+}
+
+void
+MatrixView::scrollToTime(timeT t)
+{
+ double layoutCoord = m_hlayout.getXForTime(t);
+ getCanvasView()->slotScrollHoriz(int(layoutCoord));
+}
+
+int
+MatrixView::getCurrentVelocity() const
+{
+ return m_velocityCombo->currentItem();
+}
+
+void
+MatrixView::slotSetCurrentVelocity(int value)
+{
+ m_velocityCombo->setCurrentItem(value);
+}
+
+
+void
+MatrixView::slotSetCurrentVelocityFromSelection()
+{
+ if (!m_currentEventSelection) return;
+
+ float totalVelocity = 0;
+ int count = 0;
+
+ for (EventSelection::eventcontainer::iterator i =
+ m_currentEventSelection->getSegmentEvents().begin();
+ i != m_currentEventSelection->getSegmentEvents().end(); ++i) {
+
+ if ((*i)->has(BaseProperties::VELOCITY)) {
+ totalVelocity += (*i)->get<Int>(BaseProperties::VELOCITY);
+ ++count;
+ }
+ }
+
+ if (count > 0) {
+ slotSetCurrentVelocity((totalVelocity / count) + 0.5);
+ }
+}
+
+unsigned int
+MatrixView::addPropertyViewRuler(const PropertyName &property)
+{
+ // Try and find this controller if it exists
+ //
+ for (unsigned int i = 0; i != m_propertyViewRulers.size(); i++) {
+ if (m_propertyViewRulers[i].first->getPropertyName() == property)
+ return i;
+ }
+
+ int height = 20;
+
+ PropertyViewRuler *newRuler = new PropertyViewRuler(&m_hlayout,
+ m_segments[0],
+ property,
+ xorigin,
+ height,
+ getCentralWidget());
+
+ addRuler(newRuler);
+
+ PropertyBox *newControl = new PropertyBox(strtoqstr(property),
+ m_parameterBox->width() + m_pitchRuler->width(),
+ height,
+ getCentralWidget());
+
+ addPropertyBox(newControl);
+
+ m_propertyViewRulers.push_back(
+ std::pair<PropertyViewRuler*, PropertyBox*>(newRuler, newControl));
+
+ return m_propertyViewRulers.size() - 1;
+}
+
+bool
+MatrixView::removePropertyViewRuler(unsigned int number)
+{
+ if (number > m_propertyViewRulers.size() - 1)
+ return false;
+
+ std::vector<std::pair<PropertyViewRuler*, PropertyBox*> >::iterator it
+ = m_propertyViewRulers.begin();
+ while (number--)
+ it++;
+
+ delete it->first;
+ delete it->second;
+ m_propertyViewRulers.erase(it);
+
+ return true;
+}
+
+RulerScale*
+MatrixView::getHLayout()
+{
+ return &m_hlayout;
+}
+
+Staff*
+MatrixView::getCurrentStaff()
+{
+ return getStaff(0);
+}
+
+Segment *
+MatrixView::getCurrentSegment()
+{
+ MatrixStaff *staff = getStaff(0);
+ return (staff ? &staff->getSegment() : 0);
+}
+
+timeT
+MatrixView::getInsertionTime()
+{
+ MatrixStaff *staff = m_staffs[0];
+ return staff->getInsertCursorTime(m_hlayout);
+}
+
+void
+MatrixView::slotStepBackward()
+{
+ timeT time(getInsertionTime());
+ slotSetInsertCursorPosition(SnapGrid(&m_hlayout).snapTime
+ (time - 1,
+ SnapGrid::SnapLeft));
+}
+
+void
+MatrixView::slotStepForward()
+{
+ timeT time(getInsertionTime());
+ slotSetInsertCursorPosition(SnapGrid(&m_hlayout).snapTime
+ (time + 1,
+ SnapGrid::SnapRight));
+}
+
+void
+MatrixView::slotJumpCursorToPlayback()
+{
+ slotSetInsertCursorPosition(getDocument()->getComposition().getPosition());
+}
+
+void
+MatrixView::slotJumpPlaybackToCursor()
+{
+ emit jumpPlaybackTo(getInsertionTime());
+}
+
+void
+MatrixView::slotToggleTracking()
+{
+ m_playTracking = !m_playTracking;
+}
+
+void
+MatrixView::slotSelectAll()
+{
+ Segment *segment = m_segments[0];
+ Segment::iterator it = segment->begin();
+ EventSelection *selection = new EventSelection(*segment);
+
+ for (; segment->isBeforeEndMarker(it); it++)
+ if ((*it)->isa(Note::EventType))
+ selection->addEvent(*it);
+
+ setCurrentSelection(selection, false);
+}
+
+void MatrixView::slotPreviewSelection()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ getDocument()->slotSetLoop(m_currentEventSelection->getStartTime(),
+ m_currentEventSelection->getEndTime());
+}
+
+void MatrixView::slotClearLoop()
+{
+ getDocument()->slotSetLoop(0, 0);
+}
+
+void MatrixView::slotClearSelection()
+{
+ // Actually we don't clear the selection immediately: if we're
+ // using some tool other than the select tool, then the first
+ // press switches us back to the select tool.
+
+ MatrixSelector *selector = dynamic_cast<MatrixSelector *>(m_tool);
+
+ if (!selector) {
+ slotSelectSelected();
+ } else {
+ setCurrentSelection(0);
+ }
+}
+
+void MatrixView::slotFilterSelection()
+{
+ RG_DEBUG << "MatrixView::slotFilterSelection" << endl;
+
+ Segment *segment = getCurrentSegment();
+ EventSelection *existingSelection = m_currentEventSelection;
+ if (!segment || !existingSelection)
+ return ;
+
+ EventFilterDialog dialog(this);
+ if (dialog.exec() == QDialog::Accepted) {
+ RG_DEBUG << "slotFilterSelection- accepted" << endl;
+
+ bool haveEvent = false;
+
+ EventSelection *newSelection = new EventSelection(*segment);
+ EventSelection::eventcontainer &ec =
+ existingSelection->getSegmentEvents();
+ for (EventSelection::eventcontainer::iterator i =
+ ec.begin(); i != ec.end(); ++i) {
+ if (dialog.keepEvent(*i)) {
+ haveEvent = true;
+ newSelection->addEvent(*i);
+ }
+ }
+
+ if (haveEvent)
+ setCurrentSelection(newSelection);
+ else
+ setCurrentSelection(0);
+ }
+}
+
+void
+MatrixView::readjustCanvasSize()
+{
+ int maxHeight = 0;
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+
+ MatrixStaff &staff = *m_staffs[i];
+
+ staff.sizeStaff(m_hlayout);
+
+ // if (staff.getTotalWidth() + staff.getX() > maxWidth) {
+ // maxWidth = staff.getTotalWidth() + staff.getX() + 1;
+ // }
+
+ if (staff.getTotalHeight() + staff.getY() > maxHeight) {
+ if (isDrumMode()) {
+ maxHeight = staff.getTotalHeight() + staff.getY() + 5;
+ } else {
+ maxHeight = staff.getTotalHeight() + staff.getY() + 1;
+ }
+ }
+
+ }
+
+ int newWidth = computePostLayoutWidth();
+
+ // now get the EditView to do the biz
+ readjustViewSize(QSize(newWidth, maxHeight), true);
+
+ repaintRulers();
+}
+
+void MatrixView::slotVelocityUp()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Raising velocities..."), this);
+
+ addCommandToHistory
+ (new ChangeVelocityCommand(10, *m_currentEventSelection));
+
+ slotSetCurrentVelocityFromSelection();
+}
+
+void MatrixView::slotVelocityDown()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Lowering velocities..."), this);
+
+ addCommandToHistory
+ (new ChangeVelocityCommand( -10, *m_currentEventSelection));
+
+ slotSetCurrentVelocityFromSelection();
+}
+
+void
+MatrixView::slotSetVelocities()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ EventParameterDialog dialog(this,
+ i18n("Set Event Velocities"),
+ BaseProperties::VELOCITY,
+ getCurrentVelocity());
+
+ if (dialog.exec() == QDialog::Accepted) {
+ KTmpStatusMsg msg(i18n("Setting Velocities..."), this);
+ addCommandToHistory(new SelectionPropertyCommand
+ (m_currentEventSelection,
+ BaseProperties::VELOCITY,
+ dialog.getPattern(),
+ dialog.getValue1(),
+ dialog.getValue2()));
+ }
+}
+
+void
+MatrixView::slotSetVelocitiesToCurrent()
+{
+ if (!m_currentEventSelection) return;
+
+ addCommandToHistory(new SelectionPropertyCommand
+ (m_currentEventSelection,
+ BaseProperties::VELOCITY,
+ FlatPattern,
+ getCurrentVelocity(),
+ getCurrentVelocity()));
+}
+
+void
+MatrixView::slotTriggerSegment()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ TriggerSegmentDialog dialog(this, &getDocument()->getComposition());
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+
+ addCommandToHistory(new SetTriggerCommand(*m_currentEventSelection,
+ dialog.getId(),
+ true,
+ dialog.getRetune(),
+ dialog.getTimeAdjust(),
+ Marks::NoMark,
+ i18n("Trigger Segment")));
+}
+
+void
+MatrixView::slotRemoveTriggers()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ addCommandToHistory(new ClearTriggersCommand(*m_currentEventSelection,
+ i18n("Remove Triggers")));
+}
+
+void
+MatrixView::slotToggleChordsRuler()
+{
+ toggleWidget(m_chordNameRuler, "show_chords_ruler");
+}
+
+void
+MatrixView::slotToggleTempoRuler()
+{
+ toggleWidget(m_tempoRuler, "show_tempo_ruler");
+}
+
+void
+MatrixView::paintEvent(QPaintEvent* e)
+{
+ //!!! There's a lot of code shared between matrix and notation for
+ // dealing with step recording (the insertable note event stuff).
+ // It should probably be factored out into a base class, but I'm
+ // not sure I wouldn't rather wait until the functionality is all
+ // sorted in both matrix and notation so we can be sure how much
+ // of it is actually common.
+
+ EditView::paintEvent(e);
+
+ // now deal with any backlog of insertable notes that appeared
+ // during paint (because it's not safe to modify a segment from
+ // within a sub-event-loop in a processEvents call from a paint)
+ if (!m_pendingInsertableNotes.empty()) {
+ std::vector<std::pair<int, int> > notes = m_pendingInsertableNotes;
+ m_pendingInsertableNotes.clear();
+ for (unsigned int i = 0; i < notes.size(); ++i) {
+ slotInsertableNoteEventReceived(notes[i].first, notes[i].second, true);
+ }
+ }
+}
+
+void
+MatrixView::updateViewCaption()
+{
+ // Set client label
+ //
+ QString view = i18n("Matrix");
+ if (isDrumMode())
+ view = i18n("Percussion");
+
+ if (m_segments.size() == 1) {
+
+ TrackId trackId = m_segments[0]->getTrack();
+ Track *track =
+ m_segments[0]->getComposition()->getTrackById(trackId);
+
+ int trackPosition = -1;
+ if (track)
+ trackPosition = track->getPosition();
+
+ setCaption(i18n("%1 - Segment Track #%2 - %3")
+ .arg(getDocument()->getTitle())
+ .arg(trackPosition + 1)
+ .arg(view));
+
+ } else if (m_segments.size() == getDocument()->getComposition().getNbSegments()) {
+
+ setCaption(i18n("%1 - All Segments - %2")
+ .arg(getDocument()->getTitle())
+ .arg(view));
+
+ } else {
+
+ setCaption(i18n("%1 - 1 Segment - %2",
+ "%1 - %n Segments - %2",
+ m_segments.size())
+ .arg(getDocument()->getTitle())
+ .arg(view));
+ }
+}
+
+int MatrixView::computePostLayoutWidth()
+{
+ Segment *segment = m_segments[0];
+ Composition *composition = segment->getComposition();
+ int endX = int(m_hlayout.getXForTime
+ (composition->getBarEndForTime
+ (segment->getEndMarkerTime())));
+ int startX = int(m_hlayout.getXForTime
+ (composition->getBarStartForTime
+ (segment->getStartTime())));
+
+ int newWidth = int(getXbyWorldMatrix(endX - startX));
+
+ MATRIX_DEBUG << "MatrixView::readjustCanvasSize() : startX = "
+ << startX
+ << " endX = " << endX
+ << " newWidth = " << newWidth
+ << " endmarkertime : " << segment->getEndMarkerTime()
+ << " barEnd for time : " << composition->getBarEndForTime(segment->getEndMarkerTime())
+ << endl;
+
+ newWidth += 12;
+ if (isDrumMode())
+ newWidth += 12;
+
+ return newWidth;
+}
+
+bool MatrixView::getMinMaxPitches(int& minPitch, int& maxPitch)
+{
+ minPitch = MatrixVLayout::maxMIDIPitch + 1;
+ maxPitch = MatrixVLayout::minMIDIPitch - 1;
+
+ std::vector<MatrixStaff*>::iterator sit;
+ for (sit = m_staffs.begin(); sit != m_staffs.end(); ++sit) {
+
+ MatrixElementList *mel = (*sit)->getViewElementList();
+ MatrixElementList::iterator eit;
+ for (eit = mel->begin(); eit != mel->end(); ++eit) {
+
+ NotationElement *el = static_cast<NotationElement*>(*eit);
+ if (el->isNote()) {
+ Event* ev = el->event();
+ int pitch = ev->get
+ <Int>
+ (BaseProperties::PITCH);
+ if (minPitch > pitch)
+ minPitch = pitch;
+ if (maxPitch < pitch)
+ maxPitch = pitch;
+ }
+ }
+ }
+
+ return maxPitch >= minPitch;
+}
+
+void MatrixView::extendKeyMapping()
+{
+ int minStaffPitch, maxStaffPitch;
+ if (getMinMaxPitches(minStaffPitch, maxStaffPitch)) {
+ int minKMPitch = m_localMapping->getPitchForOffset(0);
+ int maxKMPitch = m_localMapping->getPitchForOffset(0)
+ + m_localMapping->getPitchExtent() - 1;
+ if (minStaffPitch < minKMPitch)
+ m_localMapping->getMap()[minStaffPitch] = std::string("");
+ if (maxStaffPitch > maxKMPitch)
+ m_localMapping->getMap()[maxStaffPitch] = std::string("");
+ }
+}
+
+void
+MatrixView::slotInsertableNoteEventReceived(int pitch, int velocity, bool noteOn)
+{
+ // hjj:
+ // The default insertion mode is implemented equivalently in
+ // notationviewslots.cpp:
+ // - proceed if notes do not overlap
+ // - make the chord if notes do overlap, and do not proceed
+
+ static int numberOfNotesOn = 0;
+ static time_t lastInsertionTime = 0;
+ if (!noteOn) {
+ numberOfNotesOn--;
+ return ;
+ }
+
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("toggle_step_by_step"));
+ if (!action) {
+ MATRIX_DEBUG << "WARNING: No toggle_step_by_step action" << endl;
+ return ;
+ }
+ if (!action->isChecked())
+ return ;
+
+ if (m_inPaintEvent) {
+ m_pendingInsertableNotes.push_back(std::pair<int, int>(pitch, velocity));
+ return ;
+ }
+
+ Segment &segment = *getCurrentSegment();
+
+ // If the segment is transposed, we want to take that into
+ // account. But the note has already been played back to the user
+ // at its untransposed pitch, because that's done by the MIDI THRU
+ // code in the sequencer which has no way to know whether a note
+ // was intended for step recording. So rather than adjust the
+ // pitch for playback according to the transpose setting, we have
+ // to adjust the stored pitch in the opposite direction.
+
+ pitch -= segment.getTranspose();
+
+ KTmpStatusMsg msg(i18n("Inserting note"), this);
+
+ MATRIX_DEBUG << "Inserting note at pitch " << pitch << endl;
+
+ Event modelEvent(Note::EventType, 0, 1);
+ modelEvent.set<Int>(BaseProperties::PITCH, pitch);
+ static timeT insertionTime(getInsertionTime());
+ if (insertionTime >= segment.getEndMarkerTime()) {
+ MATRIX_DEBUG << "WARNING: off end of segment" << endl;
+ return ;
+ }
+ time_t now;
+ time (&now);
+ double elapsed = difftime(now, lastInsertionTime);
+ time (&lastInsertionTime);
+
+ if (numberOfNotesOn <= 0 || elapsed > 10.0 ) {
+ numberOfNotesOn = 0;
+ insertionTime = getInsertionTime();
+ }
+ numberOfNotesOn++;
+ timeT endTime(insertionTime + m_snapGrid->getSnapTime(insertionTime));
+
+ if (endTime <= insertionTime) {
+ static bool showingError = false;
+ if (showingError)
+ return ;
+ showingError = true;
+ KMessageBox::sorry(this, i18n("Can't insert note: No grid duration selected"));
+ showingError = false;
+ return ;
+ }
+
+ MatrixInsertionCommand* command =
+ new MatrixInsertionCommand(segment, insertionTime, endTime, &modelEvent);
+
+ addCommandToHistory(command);
+
+ if (!isInChordMode()) {
+ slotSetInsertCursorPosition(endTime);
+ }
+}
+
+void
+MatrixView::slotInsertableNoteOnReceived(int pitch, int velocity)
+{
+ MATRIX_DEBUG << "MatrixView::slotInsertableNoteOnReceived: " << pitch << endl;
+ slotInsertableNoteEventReceived(pitch, velocity, true);
+}
+
+void
+MatrixView::slotInsertableNoteOffReceived(int pitch, int velocity)
+{
+ MATRIX_DEBUG << "MatrixView::slotInsertableNoteOffReceived: " << pitch << endl;
+ slotInsertableNoteEventReceived(pitch, velocity, false);
+}
+
+void
+MatrixView::slotToggleStepByStep()
+{
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("toggle_step_by_step"));
+ if (!action) {
+ MATRIX_DEBUG << "WARNING: No toggle_step_by_step action" << endl;
+ return ;
+ }
+ if (action->isChecked()) { // after toggling, that is
+ emit stepByStepTargetRequested(this);
+ } else {
+ emit stepByStepTargetRequested(0);
+ }
+}
+
+void
+MatrixView::slotUpdateInsertModeStatus()
+{
+ QString message;
+ if (isInChordMode()) {
+ message = i18n(" Chord ");
+ } else {
+ message = "";
+ }
+ m_insertModeLabel->setText(message);
+}
+
+void
+MatrixView::slotStepByStepTargetRequested(QObject *obj)
+{
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("toggle_step_by_step"));
+ if (!action) {
+ MATRIX_DEBUG << "WARNING: No toggle_step_by_step action" << endl;
+ return ;
+ }
+ action->setChecked(obj == this);
+}
+
+void
+MatrixView::slotInstrumentLevelsChanged(InstrumentId id,
+ const LevelInfo &info)
+{
+ if (!m_parameterBox)
+ return ;
+
+ Composition &comp = getDocument()->getComposition();
+
+ Track *track =
+ comp.getTrackById(m_staffs[0]->getSegment().getTrack());
+ if (!track || track->getInstrument() != id)
+ return ;
+
+ Instrument *instr = getDocument()->getStudio().
+ getInstrumentById(track->getInstrument());
+ if (!instr || instr->getType() != Instrument::SoftSynth)
+ return ;
+
+ float dBleft = AudioLevel::fader_to_dB
+ (info.level, 127, AudioLevel::LongFader);
+ float dBright = AudioLevel::fader_to_dB
+ (info.levelRight, 127, AudioLevel::LongFader);
+
+ m_parameterBox->setAudioMeter(dBleft, dBright,
+ AudioLevel::DB_FLOOR,
+ AudioLevel::DB_FLOOR);
+}
+
+void
+MatrixView::slotPercussionSetChanged(Instrument * newInstr)
+{
+ // Must be called only when in drum mode
+ assert(m_drumMode);
+
+ int resolution = 8;
+ if (newInstr && newInstr->getKeyMapping()) {
+ resolution = 11;
+ }
+
+ const MidiKeyMapping *mapping = 0;
+ if (newInstr) {
+ mapping = newInstr->getKeyMapping();
+ }
+
+ // Construct a local new keymapping :
+ if (m_localMapping)
+ delete m_localMapping;
+ if (mapping) {
+ m_localMapping = new MidiKeyMapping(*mapping);
+ extendKeyMapping();
+ } else {
+ m_localMapping = 0;
+ }
+
+ m_staffs[0]->setResolution(resolution);
+
+ delete m_pitchRuler;
+
+ QWidget *vport = m_pianoView->viewport();
+
+ // Create a new pitchruler widget
+ PitchRuler *pitchRuler;
+ if (newInstr && newInstr->getKeyMapping() &&
+ !newInstr->getKeyMapping()->getMap().empty()) {
+ pitchRuler = new PercussionPitchRuler(vport,
+ m_localMapping,
+ resolution); // line spacing
+ } else {
+ pitchRuler = new PianoKeyboard(vport);
+ }
+
+
+ QObject::connect
+ (pitchRuler, SIGNAL(hoveredOverKeyChanged(unsigned int)),
+ this, SLOT (slotHoveredOverKeyChanged(unsigned int)));
+
+ QObject::connect
+ (pitchRuler, SIGNAL(keyPressed(unsigned int, bool)),
+ this, SLOT (slotKeyPressed(unsigned int, bool)));
+
+ QObject::connect
+ (pitchRuler, SIGNAL(keySelected(unsigned int, bool)),
+ this, SLOT (slotKeySelected(unsigned int, bool)));
+
+ QObject::connect
+ (pitchRuler, SIGNAL(keyReleased(unsigned int, bool)),
+ this, SLOT (slotKeyReleased(unsigned int, bool)));
+
+ // Replace the old pitchruler widget
+ m_pitchRuler = pitchRuler;
+ m_pianoView->addChild(m_pitchRuler);
+ m_pitchRuler->show();
+ m_pianoView->setFixedWidth(pitchRuler->sizeHint().width());
+
+ // Update matrix canvas
+ readjustCanvasSize();
+ bool layoutApplied = applyLayout();
+ if (!layoutApplied)
+ KMessageBox::sorry(0, i18n("Couldn't apply piano roll layout"));
+ else {
+ MATRIX_DEBUG << "MatrixView : rendering elements\n";
+ m_staffs[0]->positionAllElements();
+ m_staffs[0]->getSegment().getRefreshStatus
+ (m_segmentsRefreshStatusIds[0]).setNeedsRefresh(false);
+ update();
+ }
+}
+
+void
+MatrixView::slotCanvasBottomWidgetHeightChanged(int newHeight)
+{
+ m_pianoView->setBottomMargin(newHeight +
+ m_canvasView->horizontalScrollBar()->height());
+}
+
+MatrixCanvasView* MatrixView::getCanvasView()
+{
+ return dynamic_cast<MatrixCanvasView *>(m_canvasView);
+}
+
+}
+#include "MatrixView.moc"
diff --git a/src/gui/editors/matrix/MatrixView.h b/src/gui/editors/matrix/MatrixView.h
new file mode 100644
index 0000000..49e0358
--- /dev/null
+++ b/src/gui/editors/matrix/MatrixView.h
@@ -0,0 +1,692 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MATRIXVIEW_H_
+#define _RG_MATRIXVIEW_H_
+
+#include "base/MidiProgram.h"
+#include "base/PropertyName.h"
+#include "base/SnapGrid.h"
+#include "gui/general/EditView.h"
+#include "gui/widgets/ZoomSlider.h"
+#include "MatrixHLayout.h"
+#include "MatrixVLayout.h"
+#include "MatrixCanvasView.h"
+#include <kdockwidget.h>
+#include <qpoint.h>
+#include <qsize.h>
+#include <vector>
+#include "base/Event.h"
+#include "document/ConfigGroups.h"
+
+
+class QWidget;
+class QPaintEvent;
+class QObject;
+class QMouseEvent;
+class QLabel;
+class QCursor;
+class QCanvas;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class Staff;
+class Segment;
+class RulerScale;
+class RosegardenGUIDoc;
+class QDeferScrollView;
+class PropertyViewRuler;
+class PropertyBox;
+class PitchRuler;
+class MidiKeyMapping;
+class MatrixStaff;
+class MatrixElement;
+class InstrumentParameterBox;
+class Instrument;
+class EventSelection;
+class Event;
+class ChordNameRuler;
+class LevelInfo;
+
+
+/**
+ * Matrix ("Piano Roll") View
+ *
+ * Note: we currently display only one staff
+ */
+class MatrixView : public EditView
+{
+ Q_OBJECT
+
+ friend class MatrixSelector;
+
+public:
+ MatrixView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ QWidget *parent, bool drumMode);
+
+ virtual ~MatrixView();
+
+ virtual bool applyLayout(int staffNo = -1,
+ timeT startTime = 0,
+ timeT endTime = 0);
+
+ virtual void refreshSegment(Segment *segment,
+ timeT startTime = 0,
+ timeT endTime = 0);
+
+ QCanvas* canvas() { return getCanvasView()->canvas(); }
+
+ void setCanvasCursor(const QCursor &cursor) {
+ getCanvasView()->viewport()->setCursor(cursor);
+ }
+
+ MatrixStaff* getStaff(int i)
+ {
+ if (i >= 0 && unsigned(i) < m_staffs.size()) return m_staffs[i];
+ else return 0;
+ }
+
+ MatrixStaff *getStaff(const Segment &segment);
+
+ virtual void updateView();
+
+ bool isDrumMode() { return m_drumMode; }
+
+ /**
+ * Discover whether chord-mode insertions are enabled (as opposed
+ * to the default melody-mode)
+ */
+ bool isInChordMode();
+
+ /**
+ * Set the current event selection.
+ *
+ * If preview is true, sound the selection as well.
+ *
+ * If redrawNow is true, recolour the elements on the canvas;
+ * otherwise just line up a refresh for the next paint event.
+ *
+ * (If the selection has changed as part of a modification to a
+ * segment, redrawNow should be unnecessary and undesirable, as a
+ * paint event will occur in the next event loop following the
+ * command invocation anyway.)
+ */
+ virtual void setCurrentSelection(EventSelection* s,
+ bool preview = false,
+ bool redrawNow = false);
+
+ /**
+ * Set the current event selection to a single event
+ */
+ void setSingleSelectedEvent(int staffNo,
+ Event *event,
+ bool preview = false,
+ bool redrawNow = false);
+
+ /**
+ * Set the current event selection to a single event
+ */
+ void setSingleSelectedEvent(Segment &segment,
+ Event *event,
+ bool preview = false,
+ bool redrawNow = false);
+
+
+ /**
+ * Play a Note Event using the keyPressed() signal
+ */
+ void playNote(Event *event);
+
+ /**
+ * Play a preview (same as above but a simpler interface)
+ */
+ void playNote(const Segment &segment, int pitch, int velocity = -1);
+
+ /**
+ * Get the SnapGrid
+ */
+ const SnapGrid &getSnapGrid() const { return *m_snapGrid; }
+
+ /**
+ * Add a ruler that allows control of a single property -
+ * return the number of the added ruler
+ *
+ */
+ unsigned int addPropertyViewRuler(const PropertyName &property);
+
+ /**
+ * Remove a control ruler - return true if it's a valid ruler number
+ */
+ bool removePropertyViewRuler(unsigned int number);
+
+ /**
+ * Adjust an X coord by world matrix
+ */
+ double getXbyWorldMatrix(double value)
+ { return m_canvasView->worldMatrix().m11() * value; }
+
+ double getXbyInverseWorldMatrix(double value)
+ { return m_canvasView->inverseWorldMatrix().m11() * value; }
+
+ QPoint inverseMapPoint(const QPoint& p) { return m_canvasView->inverseMapPoint(p); }
+
+ /*
+ * Repaint the control rulers
+ *
+ */
+ void repaintRulers();
+
+ /*
+ * Readjust the canvas size
+ *
+ */
+ void readjustCanvasSize();
+
+ /*
+ * Scrolls the view such that the given time is centered
+ */
+ void scrollToTime(timeT t);
+
+ /**
+ * Get the local keyMapping (when in drum mode)
+ */
+ MidiKeyMapping *getKeyMapping() { return m_localMapping; }
+
+ /**
+ * Get the velocity currently set in the velocity menu.
+ */
+ int getCurrentVelocity() const;
+
+signals:
+ /**
+ * Emitted when the selection has been cut or copied
+ *
+ * @see MatrixSelector#hideSelection
+ */
+ void usedSelection();
+
+ void play();
+ void stop();
+ void fastForwardPlayback();
+ void rewindPlayback();
+ void fastForwardPlaybackToEnd();
+ void rewindPlaybackToBeginning();
+ void jumpPlaybackTo(timeT);
+ void panic();
+
+ void stepByStepTargetRequested(QObject *);
+
+ void editTriggerSegment(int);
+
+ void editTimeSignature(timeT);
+
+public slots:
+
+ /**
+ * put the indicationed text/object into the clipboard and remove * it
+ * from the document
+ */
+ virtual void slotEditCut();
+
+ /**
+ * put the indicationed text/object into the clipboard
+ */
+ virtual void slotEditCopy();
+
+ /**
+ * paste the clipboard into the document
+ */
+ virtual void slotEditPaste();
+
+ /**
+ * Delete the current selection
+ */
+ void slotEditDelete();
+
+ virtual void slotStepBackward(); // override from EditView
+ virtual void slotStepForward(); // override from EditView
+
+ void slotPreviewSelection();
+ void slotClearLoop();
+ void slotClearSelection();
+
+ /**
+ * Filter selection by event type
+ */
+ void slotFilterSelection(); // dummy - not actually functional yet
+
+ /// edition tools
+ void slotPaintSelected();
+ void slotEraseSelected();
+ void slotSelectSelected();
+ void slotMoveSelected();
+ void slotResizeSelected();
+
+ void slotToggleStepByStep();
+
+ /// status stuff
+ void slotUpdateInsertModeStatus();
+
+ /// transforms
+ void slotTransformsQuantize();
+ void slotTransformsRepeatQuantize();
+ void slotTransformsLegato();
+ void slotVelocityUp();
+ void slotVelocityDown();
+
+ /// settings
+ void slotToggleChordsRuler();
+ void slotToggleTempoRuler();
+
+ /// cursor moves
+ void slotJumpCursorToPlayback();
+ void slotJumpPlaybackToCursor();
+ void slotToggleTracking();
+
+ /// Canvas actions slots
+
+ /**
+ * Called when a mouse press occurred on a matrix element
+ * or somewhere on the staff
+ */
+ void slotMousePressed(timeT time, int pitch,
+ QMouseEvent*, MatrixElement*);
+
+ void slotMouseMoved(timeT time, int pitch, QMouseEvent*);
+ void slotMouseReleased(timeT time, int pitch, QMouseEvent*);
+
+ /**
+ * Called when the mouse cursor moves over a different height on
+ * the staff
+ *
+ * @see MatrixCanvasView#hoveredOverNoteChanged()
+ */
+ void slotHoveredOverNoteChanged(int evPitch, bool haveEvent,
+ timeT evTime);
+
+ /**
+ * Called when the mouse cursor moves over a different key on
+ * the piano keyboard
+ *
+ * @see PianoKeyboard#hoveredOverKeyChanged()
+ */
+ void slotHoveredOverKeyChanged(unsigned int);
+
+ /**
+ * Called when the mouse cursor moves over a note which is at a
+ * different time on the staff
+ *
+ * @see MatrixCanvasView#hoveredOverNoteChange()
+ */
+ void slotHoveredOverAbsoluteTimeChanged(unsigned int);
+
+ /**
+ * Set the time pointer position during playback
+ */
+ void slotSetPointerPosition(timeT time);
+
+ /**
+ * Set the time pointer position during playback
+ */
+ void slotSetPointerPosition(timeT time,
+ bool scroll);
+
+ /**
+ * Set the insertion pointer position (from the bottom LoopRuler)
+ */
+ void slotSetInsertCursorPosition(timeT position, bool scroll);
+
+ virtual void slotSetInsertCursorPosition(timeT position) {
+ slotSetInsertCursorPosition(position, true);
+ }
+
+ /**
+ * Catch the keyboard being pressed
+ */
+ void slotKeyPressed(unsigned int y, bool repeating);
+
+ /**
+ * Catch the keyboard being released
+ */
+ void slotKeyReleased(unsigned int y, bool repeating);
+
+ /**
+ * Catch the keyboard being pressed with selection modifier
+ */
+ void slotKeySelected(unsigned int y, bool repeating);
+
+ /**
+ * Handle scrolling between view and PianoKeyboard
+ */
+ void slotVerticalScrollPianoKeyboard(int y);
+
+ /**
+ * Close
+ */
+ void closeWindow();
+
+ /**
+ * A new selection has been acquired by a tool
+ */
+ void slotNewSelection();
+
+ /**
+ * Set the snaptime of the grid from an item in the snap combo
+ */
+ void slotSetSnapFromIndex(int);
+
+ /**
+ * Set the snaptime of the grid based on the name of the invoking action
+ */
+ void slotSetSnapFromAction();
+
+ /**
+ * Set the snaptime of the grid
+ */
+ void slotSetSnap(timeT);
+
+ /**
+ * Quantize a selection to a given level
+ */
+ void slotQuantizeSelection(int);
+
+ /**
+ * Collapse equal pitch notes
+ */
+ void slotTransformsCollapseNotes();
+
+ /**
+ * Pop-up the velocity modification dialog
+ */
+ void slotSetVelocities();
+
+ /**
+ * Set selected event velocities to whatever's in the velocity widget
+ */
+ void slotSetVelocitiesToCurrent();
+
+ /**
+ * Pop-up the select trigger segment dialog
+ */
+ void slotTriggerSegment();
+
+ /**
+ * Clear triggers from selection
+ */
+ void slotRemoveTriggers();
+
+ /**
+ * Change horizontal zoom
+ */
+ void slotChangeHorizontalZoom(int);
+
+ void slotZoomIn();
+ void slotZoomOut();
+
+ /**
+ * Select all
+ */
+ void slotSelectAll();
+
+ /**
+ * Keyboard insert
+ */
+ void slotInsertNoteFromAction();
+
+ /// Note-on received asynchronously -- consider step-by-step editing
+ void slotInsertableNoteOnReceived(int pitch, int velocity);
+
+ /// Note-off received asynchronously -- consider step-by-step editing
+ void slotInsertableNoteOffReceived(int pitch, int velocity);
+
+ /// Note-on or note-off received asynchronously -- as above
+ void slotInsertableNoteEventReceived(int pitch, int velocity, bool noteOn);
+
+ /// The given QObject has originated a step-by-step-editing request
+ void slotStepByStepTargetRequested(QObject *);
+
+ void slotInstrumentLevelsChanged(InstrumentId,
+ const LevelInfo &);
+
+ /// Set the velocity menu to the given value
+ void slotSetCurrentVelocity(int);
+ void slotSetCurrentVelocityFromSelection();
+
+protected slots:
+ void slotCanvasBottomWidgetHeightChanged(int newHeight);
+
+ /**
+ * A new percussion key mapping has to be displayed
+ */
+ void slotPercussionSetChanged(Instrument *);
+
+ /**
+ * Re-dock the parameters box to its initial position
+ */
+ void slotDockParametersBack();
+
+ /**
+ * The parameters box was closed
+ */
+ void slotParametersClosed();
+
+ /**
+ * The parameters box was docked back
+ */
+ void slotParametersDockedBack(KDockWidget*, KDockWidget::DockPosition);
+
+ /**
+ * The instrument for this track may have changed
+ */
+ void slotCheckTrackAssignments();
+
+ void slotToolHelpChanged(const QString &);
+ void slotMouseEnteredCanvasView();
+ void slotMouseLeftCanvasView();
+
+protected:
+ virtual RulerScale* getHLayout();
+
+ virtual Segment *getCurrentSegment();
+ virtual Staff *getCurrentStaff();
+ virtual timeT getInsertionTime();
+
+ /**
+ * save general Options like all bar positions and status as well
+ * as the geometry and the recent file list to the configuration
+ * file
+ */
+ virtual void slotSaveOptions();
+
+ /**
+ * read general Options again and initialize all variables like the recent file list
+ */
+ virtual void readOptions();
+
+ /**
+ * create menus and toolbars
+ */
+ virtual void setupActions();
+
+ /**
+ * setup status bar
+ */
+ virtual void initStatusBar();
+
+ /**
+ * update the current quantize level from selection or entire segment
+ */
+ virtual void updateQuantizeCombo();
+
+ /**
+ * Return the size of the MatrixCanvasView
+ */
+ virtual QSize getViewSize();
+
+ /**
+ * Set the size of the MatrixCanvasView
+ */
+ virtual void setViewSize(QSize);
+
+ virtual MatrixCanvasView *getCanvasView();
+
+ /**
+ * Init matrix actions toolbar
+ */
+ void initActionsToolbar();
+
+ /**
+ * Zoom toolbar
+ */
+ void initZoomToolbar();
+
+ /**
+ * Test whether we've had too many preview notes recently
+ */
+ bool canPreviewAnotherNote();
+
+ virtual void paintEvent(QPaintEvent* e);
+
+ virtual void updateViewCaption();
+
+ int computePostLayoutWidth();
+
+ /**
+ * Get min and max pitches of notes on matrix.
+ * Return false if no notes.
+ */
+ bool getMinMaxPitches(int& minPitch, int& maxPitch);
+
+ /**
+ * If necessary, extend local keymapping to contain
+ * all notes currently on staff
+ */
+ void extendKeyMapping();
+
+ //--------------- Data members ---------------------------------
+
+ std::vector<MatrixStaff*> m_staffs;
+
+ MatrixHLayout m_hlayout;
+ MatrixVLayout m_vlayout;
+ SnapGrid *m_snapGrid;
+
+ timeT m_lastEndMarkerTime;
+
+ // Status bar elements
+ QLabel* m_hoveredOverAbsoluteTime;
+ QLabel* m_hoveredOverNoteName;
+ QLabel *m_selectionCounter;
+ QLabel *m_insertModeLabel;
+ bool m_haveHoveredOverNote;
+
+ /**
+ * used in slotHoveredOverKeyChanged to track moves over the piano
+ * keyboard
+ */
+ int m_previousEvPitch;
+
+ KDockWidget *m_dockLeft;
+ MatrixCanvasView *m_canvasView;
+ QDeferScrollView *m_pianoView;
+ PitchRuler *m_pitchRuler;
+
+ MidiKeyMapping *m_localMapping;
+
+ // The last note we sent in case we're swooshing up and
+ // down the keyboard and don't want repeat notes sending
+ //
+ MidiByte m_lastNote;
+
+ // The first note we sent in similar case (only used for
+ // doing effective sweep selections
+ //
+ MidiByte m_firstNote;
+
+ PropertyName m_selectedProperty;
+
+ // The parameter box
+ //
+ InstrumentParameterBox *m_parameterBox;
+
+ // Toolbar flora
+ //
+ KComboBox *m_velocityCombo;
+ KComboBox *m_quantizeCombo;
+ KComboBox *m_snapGridCombo;
+ ZoomSlider<double> *m_hZoomSlider;
+ ZoomSlider<double> *m_vZoomSlider;
+ QLabel *m_zoomLabel;
+
+ // Hold our matrix quantization values and snap values
+ //
+ std::vector<timeT> m_quantizations;
+ std::vector<timeT> m_snapValues;
+
+ std::vector<std::pair<PropertyViewRuler*, PropertyBox*> > m_propertyViewRulers;
+
+ ChordNameRuler *m_chordNameRuler;
+ QWidget *m_tempoRuler;
+
+ // ruler used to scale tempo and chord name ruler
+ ZoomableMatrixHLayoutRulerScale* m_referenceRuler;
+
+ std::vector<std::pair<int, int> > m_pendingInsertableNotes;
+
+ bool m_playTracking;
+ bool m_dockVisible;
+ bool m_drumMode;
+
+ bool m_mouseInCanvasView;
+ QString m_toolContextHelp;
+};
+
+// Commented this out - was a MatrixView inner class, but we get a warning
+// that Q_OBJECT can't be used in an inner class - gl
+//
+
+// class NoteSender : public QObject
+// {
+// Q_OBJECT
+
+// public:
+// NoteSender(int i, int p) : m_insid(i), m_pitch(p) { }
+// virtual ~NoteSender();
+
+// public slots:
+// void sendNote();
+
+// private:
+// int m_insid, m_pitch;
+// };
+
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/PianoKeyboard.cpp b/src/gui/editors/matrix/PianoKeyboard.cpp
new file mode 100644
index 0000000..e4641d0
--- /dev/null
+++ b/src/gui/editors/matrix/PianoKeyboard.cpp
@@ -0,0 +1,299 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "PianoKeyboard.h"
+#include "misc/Debug.h"
+
+#include "gui/general/GUIPalette.h"
+#include "gui/general/MidiPitchLabel.h"
+#include "gui/rulers/PitchRuler.h"
+#include "MatrixStaff.h"
+#include "MatrixView.h"
+#include <qcolor.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qfont.h>
+#include <qpainter.h>
+#include <qsize.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+const unsigned int _smallWhiteKeyHeight = 14;
+const unsigned int _whiteKeyHeight = 18;
+
+PianoKeyboard::PianoKeyboard(QWidget *parent, int keys)
+ : PitchRuler(parent),
+ m_keySize(48, 18),
+ m_blackKeySize(24, 8),
+ m_nbKeys(keys),
+ m_mouseDown(false),
+ m_hoverHighlight(new QWidget(this)),
+ m_lastHoverHighlight(0),
+ m_lastKeyPressed(0)
+{
+ m_hoverHighlight->hide();
+ m_hoverHighlight->setPaletteBackgroundColor(GUIPalette::getColour(GUIPalette::MatrixKeyboardFocus));
+
+ setPaletteBackgroundColor(QColor(238, 238, 224));
+
+ computeKeyPos();
+ setMouseTracking(true);
+}
+
+QSize PianoKeyboard::sizeHint() const
+{
+ return QSize(m_keySize.width(),
+ m_keySize.height() * m_nbKeys);
+}
+
+QSize PianoKeyboard::minimumSizeHint() const
+{
+ return m_keySize;
+}
+
+void PianoKeyboard::computeKeyPos()
+{
+ // int y = -9;
+ int y = -4;
+
+ unsigned int posInOctave = 0,
+ keyHeight = _smallWhiteKeyHeight;
+
+ for (unsigned int i = 0; i < m_nbKeys; ++i) {
+ posInOctave = (i + 5) % 7;
+
+ if (y >= 0) {
+ m_whiteKeyPos.push_back(y);
+ m_allKeyPos.push_back(y);
+ }
+
+ if (posInOctave == 2)
+ m_labelKeyPos.push_back(y + (keyHeight * 3 / 4) - 2);
+
+ if (posInOctave == 0 ||
+ posInOctave == 2 ||
+ posInOctave == 6 ||
+ posInOctave == 3) { // draw shorter white key
+
+
+ keyHeight = _smallWhiteKeyHeight;
+
+ if (posInOctave == 2 ||
+ posInOctave == 6)
+ --keyHeight;
+
+ } else {
+
+ keyHeight = _whiteKeyHeight;
+ }
+
+ if (posInOctave != 2 && posInOctave != 6) { // draw black key
+
+ unsigned int bY = y + keyHeight - m_blackKeySize.height() / 2;
+
+ m_blackKeyPos.push_back(bY);
+ m_allKeyPos.push_back(bY);
+
+ }
+
+ y += keyHeight;
+ }
+}
+
+void PianoKeyboard::paintEvent(QPaintEvent*)
+{
+ static QFont *pFont = 0;
+ if (!pFont) {
+ pFont = new QFont();
+ pFont->setPixelSize(9);
+ }
+
+ QPainter paint(this);
+
+ paint.setFont(*pFont);
+
+ for (unsigned int i = 0; i < m_whiteKeyPos.size(); ++i)
+ paint.drawLine(0, m_whiteKeyPos[i],
+ m_keySize.width(), m_whiteKeyPos[i]);
+
+ for (unsigned int i = 0; i < m_labelKeyPos.size(); ++i) {
+
+ int pitch = (m_labelKeyPos.size() - i) * 12;
+
+ // for some reason I don't immediately comprehend,
+ // m_labelKeyPos contains two more octaves than we need
+ pitch -= 24;
+
+ MidiPitchLabel label(pitch);
+ paint.drawText(m_blackKeySize.width(), m_labelKeyPos[i],
+ label.getQString());
+ }
+
+ paint.setBrush(colorGroup().foreground());
+
+ for (unsigned int i = 0; i < m_blackKeyPos.size(); ++i)
+ paint.drawRect(0, m_blackKeyPos[i],
+ m_blackKeySize.width(), m_blackKeySize.height());
+}
+
+void PianoKeyboard::enterEvent(QEvent *)
+{
+ //drawHoverNote(e->y());
+}
+
+void PianoKeyboard::leaveEvent(QEvent*)
+{
+ m_hoverHighlight->hide();
+
+ int pos = mapFromGlobal( cursor().pos() ).x();
+ if ( pos > m_keySize.width() - 5 || pos < 0 ) { // bit of a hack
+ emit keyReleased(m_lastKeyPressed, false);
+ }
+}
+
+void PianoKeyboard::drawHoverNote(int evPitch)
+{
+ if (m_lastHoverHighlight != evPitch) {
+ //MATRIX_DEBUG << "PianoKeyboard::drawHoverNote : note = " << evPitch << endl;
+ m_lastHoverHighlight = evPitch;
+
+ int count = 0;
+ std::vector<unsigned int>::iterator it;
+ for (it = m_allKeyPos.begin(); it != m_allKeyPos.end(); ++it, ++count) {
+ if (126 - evPitch == count) {
+ int width = m_keySize.width() - 8;
+ int yPos = *it + 5;
+
+ // check if this is a black key
+ //
+ std::vector<unsigned int>::iterator bIt;
+ bool isBlack = false;
+ for (bIt = m_blackKeyPos.begin(); bIt != m_blackKeyPos.end(); ++bIt) {
+ if (*bIt == *it) {
+ isBlack = true;
+ break;
+ }
+ }
+
+ // Adjust for black note
+ //
+ if (isBlack) {
+ width = m_blackKeySize.width() - 8;
+ yPos -= 3;
+ } else {
+ // If a white note then ensure that we allow for short/tall ones
+ //
+ std::vector<unsigned int>::iterator wIt = m_whiteKeyPos.begin(), tIt;
+
+ while (wIt != m_whiteKeyPos.end()) {
+ if (*wIt == *it) {
+ tIt = wIt;
+
+ if (++tIt != m_whiteKeyPos.end()) {
+ //MATRIX_DEBUG << "WHITE KEY HEIGHT = " << *tIt - *wIt << endl;
+ if (*tIt - *wIt == _whiteKeyHeight) {
+ yPos += 2;
+ }
+
+ }
+ }
+
+ ++wIt;
+ }
+
+
+ }
+
+ m_hoverHighlight->setFixedSize(width, 4);
+ m_hoverHighlight->move(3, yPos);
+ m_hoverHighlight->show();
+
+ return ;
+ }
+ }
+ }
+
+
+}
+
+void PianoKeyboard::mouseMoveEvent(QMouseEvent* e)
+{
+ // The routine to work out where this should appear doesn't coincide with the note
+ // that we send to the sequencer - hence this is a bit pointless and crap at the moment.
+ // My own fault it's so crap but there you go.
+ //
+ // RWB (20040220)
+ //
+ MatrixView *matrixView = dynamic_cast<MatrixView*>(topLevelWidget());
+ if (matrixView) {
+ MatrixStaff *staff = matrixView->getStaff(0);
+
+ if (staff) {
+ drawHoverNote(staff->getHeightAtCanvasCoords(e->x(), e->y()));
+ }
+ }
+
+ if (e->state() & Qt::LeftButton) {
+ if (m_selecting)
+ emit keySelected(e->y(), true);
+ else
+ emit keyPressed(e->y(), true); // we're swooshing
+
+ emit keyReleased(m_lastKeyPressed, true);
+ m_lastKeyPressed = e->y();
+ } else
+ emit hoveredOverKeyChanged(e->y());
+}
+
+void PianoKeyboard::mousePressEvent(QMouseEvent *e)
+{
+ Qt::ButtonState bs = e->state();
+
+ if (e->button() == LeftButton) {
+ m_mouseDown = true;
+ m_selecting = (bs & Qt::ShiftButton);
+ m_lastKeyPressed = e->y();
+
+ if (m_selecting)
+ emit keySelected(e->y(), false);
+ else
+ emit keyPressed(e->y(), false);
+ }
+}
+
+void PianoKeyboard::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (e->button() == LeftButton) {
+ m_mouseDown = false;
+ m_selecting = false;
+ emit keyReleased(e->y(), false);
+ }
+}
+
+}
+#include "PianoKeyboard.moc"
diff --git a/src/gui/editors/matrix/PianoKeyboard.h b/src/gui/editors/matrix/PianoKeyboard.h
new file mode 100644
index 0000000..e8b06bb
--- /dev/null
+++ b/src/gui/editors/matrix/PianoKeyboard.h
@@ -0,0 +1,133 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PIANOKEYBOARD_H_
+#define _RG_PIANOKEYBOARD_H_
+
+#include "gui/rulers/PitchRuler.h"
+#include <qsize.h>
+#include <vector>
+
+
+class QWidget;
+class QPaintEvent;
+class QMouseEvent;
+class QEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+class PianoKeyboard : public PitchRuler
+{
+ Q_OBJECT
+public:
+ PianoKeyboard(QWidget *parent, int keys = 88);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ /*
+ * We want to be able to call this from the matrix view
+ */
+ void drawHoverNote(int evPitch);
+
+signals:
+
+ /**
+ * A key has been clicked on the keyboard.
+ *
+ * The repeating flag is there to tell the MatrixView not to send
+ * the same note again as we're in the middle of a swoosh.
+ * MatrixView does the y -> Note calculation.
+ */
+ void keyPressed(unsigned int y, bool repeating);
+
+ /**
+ * A key has been clicked with the selection modifier pressed.
+ * The MatrixView will probably interpret this as meaning to
+ * select all notes of that pitch.
+ *
+ * The repeating flag is there to tell the MatrixView not to
+ * clear the selection as we're in the middle of a swoosh.
+ * MatrixView does the y -> Note calculation.
+ */
+ void keySelected(unsigned int y, bool repeating);
+
+ /**
+ * A key has been released on the keyboard.
+ *
+ * The repeating flag is there to tell the MatrixView not to send
+ * the same note again as we're in the middle of a swoosh.
+ * MatrixView does the y -> Note calculation.
+ */
+ void keyReleased(unsigned int y, bool repeating);
+
+ /**
+ * Emitted when the mouse cursor moves to a different key when
+ * not clicking or selecting.
+ * MatrixView does the y -> Note calculation.
+ */
+ void hoveredOverKeyChanged(unsigned int y);
+
+protected:
+
+ virtual void paintEvent(QPaintEvent*);
+
+ virtual void mouseMoveEvent(QMouseEvent*);
+ virtual void mousePressEvent(QMouseEvent*);
+ virtual void mouseReleaseEvent(QMouseEvent*);
+ virtual void enterEvent(QEvent *);
+ virtual void leaveEvent(QEvent *);
+
+ // compute all key positions and store them
+ //
+ void computeKeyPos();
+
+ //--------------- Data members ---------------------------------
+ QSize m_keySize;
+ QSize m_blackKeySize;
+ unsigned int m_nbKeys;
+
+ std::vector<unsigned int> m_whiteKeyPos;
+ std::vector<unsigned int> m_blackKeyPos;
+ std::vector<unsigned int> m_labelKeyPos;
+ std::vector<unsigned int> m_allKeyPos;
+
+ bool m_mouseDown;
+ bool m_selecting;
+
+ // highlight element on the keyboard
+ QWidget *m_hoverHighlight;
+ int m_lastHoverHighlight;
+ int m_lastKeyPressed;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/QCanvasMatrixDiamond.cpp b/src/gui/editors/matrix/QCanvasMatrixDiamond.cpp
new file mode 100644
index 0000000..582b53a
--- /dev/null
+++ b/src/gui/editors/matrix/QCanvasMatrixDiamond.cpp
@@ -0,0 +1,82 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "QCanvasMatrixDiamond.h"
+
+#include "MatrixElement.h"
+#include "QCanvasMatrixRectangle.h"
+#include <qcanvas.h>
+#include <qpainter.h>
+#include <qpointarray.h>
+#include <qpoint.h>
+
+
+namespace Rosegarden
+{
+
+QCanvasMatrixDiamond::QCanvasMatrixDiamond(MatrixElement &n,
+ QCanvas* canvas) :
+ QCanvasMatrixRectangle(n, canvas)
+{}
+
+QCanvasMatrixDiamond::~QCanvasMatrixDiamond()
+{
+ hide();
+}
+
+QPointArray QCanvasMatrixDiamond::areaPoints() const
+{
+ QPointArray pa(4);
+ int pw = (pen().width() + 1) / 2;
+ if ( pw < 1 )
+ pw = 1;
+ if ( pen() == NoPen )
+ pw = 0;
+ pa[0] = QPoint((int)x() - height() / 2 - pw, (int)y() - pw);
+ pa[1] = pa[0] + QPoint(height() + pw * 2, 0);
+ pa[2] = pa[1] + QPoint(0, height() + pw * 2);
+ pa[3] = pa[0] + QPoint(0, height() + pw * 2);
+ return pa;
+}
+
+void QCanvasMatrixDiamond::drawShape(QPainter & p)
+{
+ p.save();
+ p.setWorldXForm(false);
+
+ QPointArray pa(4);
+ int q = height() / 2 + 2;
+ QPoint mapPos = p.worldMatrix().map(QPoint(int(x()), int(y())));
+
+ pa[0] = QPoint(mapPos.x(), mapPos.y() - 3);
+ pa[1] = QPoint(mapPos.x() + q, mapPos.y() - 3 + q);
+ pa[2] = pa[0] + QPoint(0, q * 2);
+ pa[3] = pa[1] - QPoint(q * 2, 0);
+ p.drawConvexPolygon(pa);
+
+ p.restore();
+}
+
+}
diff --git a/src/gui/editors/matrix/QCanvasMatrixDiamond.h b/src/gui/editors/matrix/QCanvasMatrixDiamond.h
new file mode 100644
index 0000000..5163b12
--- /dev/null
+++ b/src/gui/editors/matrix/QCanvasMatrixDiamond.h
@@ -0,0 +1,61 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_QCANVASMATRIXDIAMOND_H_
+#define _RG_QCANVASMATRIXDIAMOND_H_
+
+#include "QCanvasMatrixRectangle.h"
+#include <qpointarray.h>
+
+
+class QPainter;
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+class MatrixElement;
+
+
+/**
+ * A QCanvas diamond shape referencing a MatrixElement
+ */
+class QCanvasMatrixDiamond : public QCanvasMatrixRectangle
+{
+public:
+ QCanvasMatrixDiamond(MatrixElement&, QCanvas *);
+ ~QCanvasMatrixDiamond();
+
+ QPointArray areaPoints() const;
+
+protected:
+ void drawShape(QPainter &);
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/matrix/QCanvasMatrixRectangle.cpp b/src/gui/editors/matrix/QCanvasMatrixRectangle.cpp
new file mode 100644
index 0000000..a27b480
--- /dev/null
+++ b/src/gui/editors/matrix/QCanvasMatrixRectangle.cpp
@@ -0,0 +1,44 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "QCanvasMatrixRectangle.h"
+
+#include "MatrixElement.h"
+#include <qcanvas.h>
+
+
+namespace Rosegarden
+{
+
+QCanvasMatrixRectangle::QCanvasMatrixRectangle(MatrixElement& n,
+ QCanvas* canvas)
+ : QCanvasRectangle(canvas),
+ m_matrixElement(n)
+{}
+
+QCanvasMatrixRectangle::~QCanvasMatrixRectangle()
+{}
+
+}
diff --git a/src/gui/editors/matrix/QCanvasMatrixRectangle.h b/src/gui/editors/matrix/QCanvasMatrixRectangle.h
new file mode 100644
index 0000000..64b6e65
--- /dev/null
+++ b/src/gui/editors/matrix/QCanvasMatrixRectangle.h
@@ -0,0 +1,60 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_QCANVASMATRIXRECTANGLE_H_
+#define _RG_QCANVASMATRIXRECTANGLE_H_
+
+#include <qcanvas.h>
+
+
+namespace Rosegarden
+{
+
+class MatrixElement;
+
+
+/**
+ * A QCanvasRectangle referencing a MatrixElement
+ */
+class QCanvasMatrixRectangle : public QCanvasRectangle
+{
+public:
+ QCanvasMatrixRectangle(MatrixElement&, QCanvas*);
+
+ virtual ~QCanvasMatrixRectangle();
+
+ MatrixElement& getMatrixElement() { return m_matrixElement; }
+
+protected:
+ //--------------- Data members ---------------------------------
+
+ MatrixElement& m_matrixElement;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/ClefInserter.cpp b/src/gui/editors/notation/ClefInserter.cpp
new file mode 100644
index 0000000..f39327e
--- /dev/null
+++ b/src/gui/editors/notation/ClefInserter.cpp
@@ -0,0 +1,132 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "ClefInserter.h"
+
+#include <klocale.h>
+#include "base/Event.h"
+#include "base/NotationTypes.h"
+#include "base/ViewElement.h"
+#include "commands/notation/ClefInsertionCommand.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/LinedStaff.h"
+#include "NotationElement.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NotePixmapFactory.h"
+#include <kaction.h>
+#include <qiconset.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+ClefInserter::ClefInserter(NotationView* view)
+ : NotationTool("ClefInserter", view),
+ m_clef(Clef::Treble)
+{
+ QIconSet icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("select")));
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("crotchet")));
+ new KAction(i18n("Switch to Inserting Notes"), icon, 0, this,
+ SLOT(slotNotesSelected()), actionCollection(),
+ "notes");
+
+ createMenu("clefinserter.rc");
+}
+
+void ClefInserter::slotNotesSelected()
+{
+ m_nParentView->slotLastNoteAction();
+}
+
+void ClefInserter::slotEraseSelected()
+{
+ m_parentView->actionCollection()->action("erase")->activate();
+}
+
+void ClefInserter::slotSelectSelected()
+{
+ m_parentView->actionCollection()->action("select")->activate();
+}
+
+void ClefInserter::ready()
+{
+ m_nParentView->setCanvasCursor(Qt::crossCursor);
+ m_nParentView->setHeightTracking(false);
+}
+
+void ClefInserter::setClef(std::string clefType)
+{
+ m_clef = clefType;
+}
+
+void ClefInserter::handleLeftButtonPress(timeT,
+ int,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement*)
+{
+ if (staffNo < 0)
+ return ;
+ Event *clef = 0, *key = 0;
+
+ LinedStaff *staff = m_nParentView->getLinedStaff(staffNo);
+
+ NotationElementList::iterator closestElement =
+ staff->getClosestElementToCanvasCoords(e->x(), (int)e->y(),
+ clef, key, false, -1);
+
+ if (closestElement == staff->getViewElementList()->end())
+ return ;
+
+ timeT time = (*closestElement)->event()->getAbsoluteTime(); // not getViewAbsoluteTime()
+
+
+ ClefInsertionCommand *command =
+ new ClefInsertionCommand(staff->getSegment(), time, m_clef);
+
+ m_nParentView->addCommandToHistory(command);
+
+ Event *event = command->getLastInsertedEvent();
+ if (event)
+ m_nParentView->setSingleSelectedEvent(staffNo, event);
+}
+
+const QString ClefInserter::ToolName = "clefinserter";
+
+}
+#include "ClefInserter.moc"
diff --git a/src/gui/editors/notation/ClefInserter.h b/src/gui/editors/notation/ClefInserter.h
new file mode 100644
index 0000000..460bfa5
--- /dev/null
+++ b/src/gui/editors/notation/ClefInserter.h
@@ -0,0 +1,83 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_CLEFINSERTER_H_
+#define _RG_CLEFINSERTER_H_
+
+#include "base/NotationTypes.h"
+#include "NotationTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class NotationView;
+
+
+/**
+ * This tool will insert clefs on mouse click events
+ */
+class ClefInserter : public NotationTool
+{
+ Q_OBJECT
+
+ friend class NotationToolBox;
+
+public:
+ void setClef(std::string clefType);
+
+ virtual void ready();
+
+ virtual void handleLeftButtonPress(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement* el);
+ static const QString ToolName;
+
+protected slots:
+ void slotNotesSelected();
+ void slotEraseSelected();
+ void slotSelectSelected();
+
+protected:
+ ClefInserter(NotationView*);
+
+ //--------------- Data members ---------------------------------
+
+ Clef m_clef;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/FontViewFrame.cpp b/src/gui/editors/notation/FontViewFrame.cpp
new file mode 100644
index 0000000..ab0498f
--- /dev/null
+++ b/src/gui/editors/notation/FontViewFrame.cpp
@@ -0,0 +1,252 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "FontViewFrame.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <qfontmetrics.h>
+#include <qframe.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qwidget.h>
+#include <qpainter.h>
+
+#ifdef HAVE_XFT
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#include FT_GLYPH_H
+#include <X11/Xft/Xft.h>
+#endif
+
+namespace Rosegarden
+{
+
+FontViewFrame::FontViewFrame( int pixelSize, QWidget* parent, const char* name ) :
+ QFrame(parent, name),
+ m_fontSize(pixelSize),
+ m_tableFont(0)
+{
+ setBackgroundMode(PaletteBase);
+ setFrameStyle(Panel | Sunken);
+ setMargin(8);
+ setRow(0);
+}
+
+FontViewFrame::~FontViewFrame()
+{
+ // empty
+}
+
+void
+FontViewFrame::setFont(QString font)
+{
+ m_fontName = font;
+ loadFont();
+ update();
+}
+
+void
+FontViewFrame::loadFont()
+{
+#ifdef HAVE_XFT
+ if (m_tableFont) {
+ XftFontClose(x11AppDisplay(), (XftFont *)m_tableFont);
+ }
+ m_tableFont = 0;
+
+ static bool haveDir = false;
+ if (!haveDir) {
+ FcConfigAppFontAddDir(FcConfigGetCurrent(),
+ (const FcChar8 *)"/opt/kde3/share/apps/rosegarden/fonts");
+ haveDir = true;
+ }
+
+ FcPattern *pattern = FcPatternCreate();
+ FcPatternAddString(pattern, FC_FAMILY, (FcChar8 *)m_fontName.latin1());
+ FcPatternAddInteger(pattern, FC_PIXEL_SIZE, m_fontSize);
+
+ FcConfigSubstitute(FcConfigGetCurrent(), pattern, FcMatchPattern);
+
+ FcResult result = FcResultMatch;
+ FcPattern *match = FcFontMatch(FcConfigGetCurrent(), pattern, &result);
+ FcPatternDestroy(pattern);
+
+ if (!match || result != FcResultMatch) {
+ KMessageBox::error(this, i18n("Error: Unable to match font name %1").arg(m_fontName));
+ return ;
+ }
+
+ FcChar8 *matchFamily;
+ FcPatternGetString(match, FC_FAMILY, 0, &matchFamily);
+
+ if (QString((const char *)matchFamily).lower() != m_fontName.lower()) {
+ KMessageBox::sorry(this, i18n("Warning: No good match for font name %1 (best is %2)").
+ arg(m_fontName).arg(QString((const char *)matchFamily)));
+ m_fontName = (const char *)matchFamily;
+ }
+
+ m_tableFont = XftFontOpenPattern(x11AppDisplay(), match);
+
+ if (!m_tableFont) {
+ KMessageBox::error(this, i18n("Error: Unable to open best-match font %1").
+ arg(QString((const char *)matchFamily)));
+ }
+#endif
+}
+
+void FontViewFrame::setGlyphs(bool glyphs)
+{
+ m_glyphs = glyphs;
+ update();
+}
+
+QSize FontViewFrame::sizeHint() const
+{
+ return QSize(16 * m_fontSize * 3 / 2 + margin() + 2 * frameWidth(),
+ 16 * m_fontSize * 3 / 2 + margin() + 2 * frameWidth());
+}
+
+QSize FontViewFrame::cellSize() const
+{
+ QFontMetrics fm = fontMetrics();
+ return QSize( fm.maxWidth(), fm.lineSpacing() + 1 );
+}
+
+void FontViewFrame::paintEvent( QPaintEvent* e )
+{
+#ifdef HAVE_XFT
+ if (!m_tableFont)
+ return ;
+
+ QFrame::paintEvent(e);
+ QPainter p(this);
+
+ int ll = 25;
+ int ml = frameWidth() + margin() + ll + 1;
+ int mt = frameWidth() + margin();
+ QSize cell((width() - 16 - ml) / 17, (height() - 16 - mt) / 17);
+
+ if ( !cell.width() || !cell.height() )
+ return ;
+
+ QColor body(255, 255, 192);
+ QColor negative(255, 192, 192);
+ QColor positive(192, 192, 255);
+ QColor rnegative(255, 128, 128);
+ QColor rpositive(128, 128, 255);
+
+ Drawable drawable = (Drawable)handle();
+ XftDraw *draw = XftDrawCreate(x11AppDisplay(), drawable,
+ (Visual *)x11Visual(), x11Colormap());
+
+ QColor pen(Qt::black);
+ XftColor col;
+ col.color.red = pen.red () | pen.red() << 8;
+ col.color.green = pen.green () | pen.green() << 8;
+ col.color.blue = pen.blue () | pen.blue() << 8;
+ col.color.alpha = 0xffff;
+ col.pixel = pen.pixel();
+
+ for (int j = 0; j <= 16; j++) {
+ for (int i = 0; i <= 16; i++) {
+
+ int x = i * cell.width();
+ int y = j * cell.height();
+
+ x += ml;
+ y += mt; // plus ascent
+
+ if (i == 0) {
+ if (j == 0)
+ continue;
+ p.setFont(kapp->font());
+ QFontMetrics afm(kapp->font());
+ QString s = QString("%1").arg(m_row * 256 + (j - 1) * 16);
+ p.drawText(x - afm.width(s), y, s);
+ p.setPen(QColor(190, 190, 255));
+ p.drawLine(0, y, width(), y);
+ p.setPen(Qt::black);
+ continue;
+ } else if (j == 0) {
+ p.setFont(kapp->font());
+ QString s = QString("%1").arg(i - 1);
+ p.drawText(x, y, s);
+ p.setPen(QColor(190, 190, 255));
+ p.drawLine(x, 0, x, height());
+ p.setPen(Qt::black);
+ continue;
+ }
+
+ p.save();
+
+ if (m_glyphs) {
+ FT_UInt ui = m_row * 256 + (j - 1) * 16 + i - 1;
+ XftDrawGlyphs(draw, &col, (XftFont *)m_tableFont, x, y, &ui, 1);
+ } else {
+ FcChar32 ch = m_row * 256 + (j - 1) * 16 + i - 1;
+ if (XftCharExists(x11AppDisplay(), (XftFont *)m_tableFont, ch)) {
+ XftDrawString32(draw, &col, (XftFont *)m_tableFont, x, y, &ch, 1);
+ }
+ }
+
+ p.restore();
+ }
+ }
+#endif
+}
+
+bool
+FontViewFrame::hasRow(int r) const
+{
+#ifdef HAVE_XFT
+ if (m_glyphs) {
+
+ if (r < 256)
+ return true;
+
+ } else {
+
+ for (int c = 0; c < 256; ++c) {
+ FcChar32 ch = r * 256 + c;
+ if (XftCharExists(x11AppDisplay(), (XftFont *)m_tableFont, ch)) {
+ return true;
+ }
+ }
+ }
+#endif
+ return false;
+}
+
+void FontViewFrame::setRow(int row)
+{
+ m_row = row;
+ update();
+}
+
+}
+#include "FontViewFrame.moc"
diff --git a/src/gui/editors/notation/FontViewFrame.h b/src/gui/editors/notation/FontViewFrame.h
new file mode 100644
index 0000000..8a1a946
--- /dev/null
+++ b/src/gui/editors/notation/FontViewFrame.h
@@ -0,0 +1,77 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_FONTVIEWFRAME_H_
+#define _RG_FONTVIEWFRAME_H_
+
+#include <qframe.h>
+#include <qsize.h>
+#include <qstring.h>
+
+
+class QWidget;
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+class FontViewFrame : public QFrame
+{
+ Q_OBJECT
+
+public:
+ FontViewFrame(int pixelSize, QWidget *parent = 0, const char *name = 0);
+ virtual ~FontViewFrame();
+
+ QSize sizeHint() const;
+ bool hasRow(int row) const;
+
+public slots:
+ void setFont(QString name);
+ void setRow(int);
+ void setGlyphs(bool glyphs);
+
+protected:
+ QSize cellSize() const;
+ void paintEvent( QPaintEvent* );
+ void loadFont();
+
+private:
+ QString m_fontName;
+ int m_fontSize;
+ void *m_tableFont;
+ int m_row;
+ bool m_glyphs;
+};
+
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/GuitarChordInserter.cpp b/src/gui/editors/notation/GuitarChordInserter.cpp
new file mode 100644
index 0000000..2482b87
--- /dev/null
+++ b/src/gui/editors/notation/GuitarChordInserter.cpp
@@ -0,0 +1,185 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "GuitarChordInserter.h"
+
+#include <klocale.h>
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "base/Staff.h"
+#include "base/ViewElement.h"
+#include "commands/notation/EraseEventCommand.h"
+#include "commands/notation/GuitarChordInsertionCommand.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/LinedStaff.h"
+#include "gui/editors/guitar/GuitarChordSelectorDialog.h"
+#include "misc/Debug.h"
+#include "NotationElement.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NotePixmapFactory.h"
+#include <kaction.h>
+#include <qdialog.h>
+#include <qiconset.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+GuitarChordInserter::GuitarChordInserter(NotationView* view)
+ : NotationTool("GuitarChordInserter", view),
+ m_guitarChordSelector(0)
+{
+ QIconSet icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("select")));
+
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("crotchet")));
+
+ new KAction(i18n("Switch to Inserting Notes"), icon, 0, this,
+ SLOT(slotNoteSelected()), actionCollection(),
+ "notes");
+
+ m_guitarChordSelector = new GuitarChordSelectorDialog(m_nParentView);
+ m_guitarChordSelector->init();
+ createMenu("guitarchordinserter.rc");
+}
+
+void GuitarChordInserter::slotGuitarChordSelected()
+{
+ // Switch to last selected Guitar Chord
+ // m_nParentView->slotLastGuitarChordAction();
+}
+
+void GuitarChordInserter::slotEraseSelected()
+{
+ m_parentView->actionCollection()->action("erase")->activate();
+}
+
+void GuitarChordInserter::slotSelectSelected()
+{
+ m_parentView->actionCollection()->action("select")->activate();
+}
+
+void GuitarChordInserter::handleLeftButtonPress(timeT,
+ int,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element)
+{
+ NOTATION_DEBUG << "GuitarChordInserter::handleLeftButtonPress" << endl;
+
+ if (staffNo < 0) {
+ return ;
+ }
+
+ Staff *staff = m_nParentView->getStaff(staffNo);
+
+ if (element && element->event()->isa(Guitar::Chord::EventType)) {
+ handleSelectedGuitarChord (element, staff);
+ } else {
+ createNewGuitarChord (element, staff, e);
+ }
+}
+
+bool GuitarChordInserter::processDialog( Staff* staff,
+ timeT& insertionTime)
+{
+ bool result = false;
+
+ if (m_guitarChordSelector->exec() == QDialog::Accepted) {
+ Guitar::Chord chord = m_guitarChordSelector->getChord();
+
+ GuitarChordInsertionCommand *command =
+ new GuitarChordInsertionCommand
+ (staff->getSegment(), insertionTime, chord);
+
+ m_nParentView->addCommandToHistory(command);
+ result = true;
+ }
+
+ return result;
+}
+
+void GuitarChordInserter::handleSelectedGuitarChord (ViewElement* element, Staff *staff)
+{
+ NOTATION_DEBUG << "GuitarChordInserter::handleSelectedGuitarChord" << endl;
+
+
+ // Get time of where guitar chord is inserted
+ timeT insertionTime = element->event()->getAbsoluteTime(); // not getViewAbsoluteTime()
+
+ // edit an existing guitar chord, if that's what we clicked on
+ try {
+ Guitar::Chord chord(*(element->event()));
+
+ m_guitarChordSelector->setChord(chord);
+
+ if ( processDialog( staff, insertionTime ) ) {
+ // Erase old guitar chord
+ EraseEventCommand *command =
+ new EraseEventCommand(staff->getSegment(),
+ element->event(),
+ false);
+
+ m_nParentView->addCommandToHistory(command);
+ }
+ } catch (Exception e) {}
+}
+
+void GuitarChordInserter::createNewGuitarChord (ViewElement* element, Staff *staff, QMouseEvent* e)
+{
+ NOTATION_DEBUG << "GuitarChordInserter::createNewGuitarChord" << endl;
+ Event *clef = 0, *key = 0;
+
+ LinedStaff *s = dynamic_cast<LinedStaff *>(staff);
+
+ NotationElementList::iterator closestElement =
+ s->getClosestElementToCanvasCoords(e->x(), (int)e->y(),
+ clef, key, false, -1);
+
+ if (closestElement == staff->getViewElementList()->end()) {
+ return ;
+ }
+
+ timeT insertionTime = (*closestElement)->event()->getAbsoluteTime(); // not getViewAbsoluteTime()
+
+ processDialog( staff, insertionTime );
+}
+
+const QString GuitarChordInserter::ToolName = "guitarchordinserter";
+
+}
+#include "GuitarChordInserter.moc"
diff --git a/src/gui/editors/notation/GuitarChordInserter.h b/src/gui/editors/notation/GuitarChordInserter.h
new file mode 100644
index 0000000..3bd5660
--- /dev/null
+++ b/src/gui/editors/notation/GuitarChordInserter.h
@@ -0,0 +1,96 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_GUITAR_CHORD_INSERTER_H_
+#define _RG_GUITAR_CHORD_INSERTER_H_
+
+#include "NotationTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class Staff;
+class NotationView;
+class GuitarChordSelectorDialog;
+
+/**
+ * This tool will insert guitar chord on mouse click events
+*/
+class GuitarChordInserter : public NotationTool
+{
+ Q_OBJECT
+
+ friend class NotationToolBox;
+
+public:
+
+ virtual void handleLeftButtonPress(timeT t,
+ int height,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element);
+
+/*
+ virtual void handleMouseDoubleClick(timeT,
+ int height, int staffNo,
+ QMouseEvent*,
+ ViewElement* el);
+*/
+
+ static const QString ToolName;
+
+protected slots:
+ void slotGuitarChordSelected();
+ void slotEraseSelected();
+ void slotSelectSelected();
+
+protected:
+ GuitarChordSelectorDialog* m_guitarChordSelector;
+
+ GuitarChordInserter(NotationView*);
+
+private:
+ void handleSelectedGuitarChord (ViewElement* element,
+ Staff *staff);
+
+ void createNewGuitarChord (ViewElement* element,
+ Staff *staff,
+ QMouseEvent* e);
+
+ bool processDialog (Staff *staff,
+ timeT& insertionTime);
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/HeadersGroup.cpp b/src/gui/editors/notation/HeadersGroup.cpp
new file mode 100644
index 0000000..c0a2de0
--- /dev/null
+++ b/src/gui/editors/notation/HeadersGroup.cpp
@@ -0,0 +1,160 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ This file is Copyright 2007-2008
+ Yves Guillemot <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include <limits>
+#include <qsize.h>
+#include <qwidget.h>
+#include <qvbox.h>
+#include <qlabel.h>
+
+#include "HeadersGroup.h"
+#include "TrackHeader.h"
+#include "NotationView.h"
+#include "NotePixmapFactory.h"
+
+
+namespace Rosegarden
+{
+
+
+HeadersGroup::
+HeadersGroup(QWidget *parent, NotationView * nv, Composition * comp) :
+ QVBox(parent),
+ m_notationView(nv),
+ m_composition(comp),
+ m_usedHeight(0),
+ m_filler(0),
+ m_lastX(INT_MIN),
+ m_lastWidth(-1)
+{
+}
+
+void
+HeadersGroup::removeAllHeaders()
+{
+ TrackHeaderVector::iterator i;
+ for (i=m_headers.begin(); i!=m_headers.end(); i++) {
+ delete *i;
+ }
+ m_headers.erase(m_headers.begin(), m_headers.end());
+
+ if (m_filler) {
+ delete m_filler;
+ m_filler = 0;
+ }
+ m_usedHeight = 0;
+ m_lastWidth = -1;
+}
+
+void
+HeadersGroup::addHeader(int trackId, int height, int ypos, double xcur)
+{
+ TrackHeader * sh = new TrackHeader(this, trackId, height, ypos);
+ m_headers.push_back(sh);
+ m_usedHeight += height;
+}
+
+void
+HeadersGroup::completeToHeight(int height)
+{
+ if (height > m_usedHeight) {
+ if (!m_filler) m_filler = new QLabel(this);
+ m_filler->setFixedHeight(height - m_usedHeight);
+ }
+}
+
+void
+HeadersGroup::slotUpdateAllHeaders(int x, int y, bool force)
+{
+ // Minimum header width
+ int headerMinWidth = m_notationView->getHeadersTopFrameMinWidth();
+
+ // Maximum header width (may be overriden by clef and key width)
+ int headerMaxWidth = (m_notationView->getCanvasVisibleWidth() * 10) / 100;
+
+ if ((x != m_lastX) || force) {
+ m_lastX = x;
+ TrackHeaderVector::iterator i;
+ int neededWidth = 0;
+
+ // Pass 1 : get the max width needed
+ for (i=m_headers.begin(); i!=m_headers.end(); i++) {
+ int w = (*i)->lookAtStaff(x, headerMaxWidth);
+ if (w > neededWidth) neededWidth = w;
+ }
+
+ if (neededWidth < headerMinWidth) neededWidth = headerMinWidth;
+
+ // Only when m_lastWidth is valid (the first time, m_lastWidth = -1)
+ if (m_lastWidth > 0) {
+ // Don't redraw the headers when change of width is very small
+ const int treshold = 10; // Treshold value should be refined ...
+ int deltaWidth = m_lastWidth - neededWidth;
+ if ((deltaWidth < treshold) && (deltaWidth > -treshold))
+ neededWidth = m_lastWidth;
+ }
+
+ // Pass 2 : redraw the headers when necessary
+ for (i=m_headers.begin(); i!=m_headers.end(); i++) {
+ (*i)->updateHeader(neededWidth);
+ }
+
+ if (neededWidth != m_lastWidth) {
+ setFixedWidth(neededWidth);
+ m_lastWidth = neededWidth;
+
+ // Suppress vertical white stripes on canvas when headers
+ // width changes while scrolling
+ /// TODO : Limit "setChanged()" to the useful part of canvas
+ m_notationView->canvas()->setAllChanged();
+ m_notationView->canvas()->update();
+ }
+ }
+}
+
+
+
+
+void
+HeadersGroup::setCurrent(TrackId trackId)
+{
+ TrackHeaderVector::iterator i;
+ for (i=m_headers.begin(); i!=m_headers.end(); i++)
+ (*i)->setCurrent((*i)->getId() == trackId);
+}
+
+void
+HeadersGroup::resizeEvent(QResizeEvent * ev)
+{
+ // Needed to avoid gray zone at the right of headers
+ // when width is decreasing
+ emit headersResized(ev->size().width());
+}
+
+}
+#include "HeadersGroup.moc"
diff --git a/src/gui/editors/notation/HeadersGroup.h b/src/gui/editors/notation/HeadersGroup.h
new file mode 100644
index 0000000..22d25da
--- /dev/null
+++ b/src/gui/editors/notation/HeadersGroup.h
@@ -0,0 +1,144 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ This file is Copyright 2007-2008
+ Yves Guillemot <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#ifndef _RG_HEADERSGROUP_H_
+#define _RG_HEADERSGROUP_H_
+
+#include "base/Track.h"
+
+#include <vector>
+#include <qsize.h>
+#include <qwidget.h>
+#include <qvbox.h>
+
+
+class QLabel;
+class QResizeEvent;
+
+
+namespace Rosegarden
+{
+
+
+class NotationView;
+class Composition;
+class TrackHeader;
+
+
+class HeadersGroup : public QVBox
+{
+ Q_OBJECT
+public:
+ /**
+ * Create an empty headers group
+ */
+ HeadersGroup(QWidget *parent, NotationView * nv, Composition * comp);
+
+ void removeAllHeaders();
+
+ void addHeader(int trackId, int height, int ypos, double xcur);
+
+ /**
+ * Resize a filler at bottom of group to set the headersGroup height
+ * to the value specified in parameter.
+ * (Used to give to the headers group exactly the same height as the
+ * canvas. Necessary to get synchronous vertical scroll.)
+ */
+ void completeToHeight(int height);
+
+ NotationView * getNotationView()
+ { return m_notationView;
+ }
+
+ Composition * getComposition()
+ { return m_composition;
+ }
+
+ /**
+ * Return the total height of all the headers (without the filler).
+ */
+ int getUsedHeight()
+ { return m_usedHeight;
+ }
+
+ /**
+ * Highlight as "current" the header of the specified track.
+ */
+ void setCurrent(TrackId trackId);
+
+ /**
+ * Highlight as "current" the header of the specified track.
+ */
+ int getWidth()
+ {
+ return m_lastWidth;
+ }
+
+ typedef enum { ShowNever, ShowWhenNeeded, ShowAlways } ShowHeadersModeType;
+
+ // Used to ensure to have one default value and only one.
+ static const ShowHeadersModeType DefaultShowMode = ShowAlways;
+
+ // Useful in configuration dialog.
+ static bool isValidShowMode(int mode)
+ {
+ return ((mode >= ShowNever) && (mode <= ShowAlways));
+ }
+
+public slots :
+ /**
+ * Called when notation canvas moves.
+ * Setting force to true forces the headers to be redrawn even
+ * if x has not changed since the last call.
+ */
+ void slotUpdateAllHeaders(int x, int y, bool force = false);
+
+signals :
+ void headersResized(int newWidth);
+
+private:
+ void resizeEvent(QResizeEvent * ev);
+
+ NotationView * m_notationView;
+ Composition * m_composition;
+
+ typedef std::vector<TrackHeader *> TrackHeaderVector;
+ TrackHeaderVector m_headers;
+
+ int m_usedHeight;
+ QLabel * m_filler;
+ int m_lastX;
+ int m_lastWidth;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationCanvasView.cpp b/src/gui/editors/notation/NotationCanvasView.cpp
new file mode 100644
index 0000000..55e63ac
--- /dev/null
+++ b/src/gui/editors/notation/NotationCanvasView.cpp
@@ -0,0 +1,485 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NotationCanvasView.h"
+#include "misc/Debug.h"
+
+#include "misc/Strings.h"
+#include "gui/general/LinedStaffManager.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "gui/kdeext/QCanvasGroupableItem.h"
+#include "gui/kdeext/QCanvasSimpleSprite.h"
+#include "NotationElement.h"
+#include "NotationProperties.h"
+#include "NotationStaff.h"
+#include <qcanvas.h>
+#include <qcolor.h>
+#include <qpainter.h>
+#include <qpen.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+NotationCanvasView::NotationCanvasView(const LinedStaffManager &staffmgr,
+ QCanvas *viewing, QWidget *parent,
+ const char *name, WFlags f) :
+ RosegardenCanvasView(viewing, parent, name, f),
+ m_linedStaffManager(staffmgr),
+ m_lastYPosNearStaff(0),
+ m_currentStaff(0),
+ m_currentHeight( -1000),
+ m_legerLineOffset(false),
+ m_heightTracking(false)
+{
+ // -- switching mandolin-sonatina first staff to page mode:
+ // default params (I think 16,100): render 1000ms position 1070ms
+ // 64,100: 1000ms 980ms
+ // 8, 100: 1140ms 1140ms
+ // 128, 100: 1060ms 980ms
+ // 256, 100: 1060ms 980ms / 930ms 920ms
+
+ // canvas()->retune(256, 100);
+
+ viewport()->setMouseTracking(true);
+
+ m_heightMarker = new QCanvasItemGroup(viewing);
+
+ m_vert1 = new QCanvasLineGroupable(viewing, m_heightMarker);
+ m_vert1->setPoints(0, 0, 0, 8);
+ m_vert1->setPen(QPen(QColor(64, 64, 64), 1));
+
+ m_vert2 = new QCanvasLineGroupable(viewing, m_heightMarker);
+ m_vert2->setPoints(17, 0, 17, 8);
+ m_vert2->setPen(QPen(QColor(64, 64, 64), 1));
+
+ m_heightMarker->hide();
+}
+
+NotationCanvasView::~NotationCanvasView()
+{
+ // All canvas items are deleted in ~NotationView()
+}
+
+void
+NotationCanvasView::setHeightTracking(bool t)
+{
+ m_heightTracking = t;
+ if (!t) {
+ m_heightMarker->hide();
+ canvas()->update();
+ }
+}
+
+void
+NotationCanvasView::contentsMouseReleaseEvent(QMouseEvent *e)
+{
+ emit mouseReleased(e);
+}
+
+void
+NotationCanvasView::contentsMouseMoveEvent(QMouseEvent *e)
+{
+ NotationStaff *prevStaff = m_currentStaff;
+ int prevHeight = m_currentHeight;
+
+ m_currentStaff = dynamic_cast<NotationStaff *>
+ (m_linedStaffManager.getStaffForCanvasCoords(e->x(), e->y()));
+
+ if (!m_currentStaff) {
+
+ emit hoveredOverNoteChanged(QString::null);
+ if (prevStaff) {
+ m_heightMarker->hide();
+ canvas()->update();
+ }
+
+ } else {
+
+ m_currentHeight = m_currentStaff->getHeightAtCanvasCoords(e->x(), e->y());
+
+ int x = e->x() - 8; // magic based on mouse cursor size
+ bool needUpdate = (m_heightTracking && (m_heightMarker->x() != x));
+ m_heightMarker->setX(x);
+
+ if (prevStaff != m_currentStaff ||
+ prevHeight != m_currentHeight) {
+
+ if (m_heightTracking) {
+ setHeightMarkerHeight(e);
+ m_heightMarker->show();
+ needUpdate = true;
+ }
+
+ emit hoveredOverNoteChanged
+ (strtoqstr
+ (m_currentStaff->getNoteNameAtCanvasCoords(e->x(), e->y())));
+ }
+
+ if (needUpdate)
+ canvas()->update();
+ }
+
+ NotationElement *elt = getElementAtXCoord(e);
+ if (elt) {
+ emit hoveredOverAbsoluteTimeChanged(elt->getViewAbsoluteTime());
+ }
+
+ // if(tracking) ??
+ emit mouseMoved(e);
+}
+
+void NotationCanvasView::contentsMousePressEvent(QMouseEvent *e)
+{
+ NOTATION_DEBUG << "NotationCanvasView::contentsMousePressEvent() - btn : "
+ << e->button() << " - state : " << e->state()
+ << endl;
+
+ QCanvasItemList itemList = canvas()->collisions(e->pos());
+
+ // We don't want to use m_currentStaff/Height, because we want
+ // to make sure the event happens at the point we clicked at
+ // rather than the last point for which contentsMouseMoveEvent
+ // happened to be called
+
+ NotationStaff *staff = dynamic_cast<NotationStaff *>
+ (m_linedStaffManager.getStaffForCanvasCoords(e->x(), e->y()));
+
+ QCanvasItemList::Iterator it;
+ NotationElement *clickedNote = 0;
+ NotationElement *clickedVagueNote = 0;
+ NotationElement *clickedNonNote = 0;
+
+ bool haveClickHeight = false;
+ int clickHeight = 0;
+ if (staff) {
+ clickHeight = staff->getHeightAtCanvasCoords(e->x(), e->y());
+ haveClickHeight = true;
+ }
+
+ for (it = itemList.begin(); it != itemList.end(); ++it) {
+
+ if ((*it)->active()) {
+ emit activeItemPressed(e, *it);
+ return ;
+ }
+
+ QCanvasNotationSprite *sprite =
+ dynamic_cast<QCanvasNotationSprite*>(*it);
+ if (!sprite) {
+ if (dynamic_cast<QCanvasNonElementSprite *>(*it)) {
+ emit nonNotationItemPressed(e, *it);
+ return ;
+ } else if (dynamic_cast<QCanvasText *>(*it)) {
+ emit textItemPressed(e, *it);
+ return ;
+ }
+ continue;
+ }
+
+ NotationElement &el = sprite->getNotationElement();
+
+ // #957364 (Notation: Hard to select upper note in chords of
+ // seconds) -- adjust x-coord for shifted note head
+
+ double cx = el.getCanvasX();
+ int nbw = 10;
+
+ if (staff) {
+ nbw = staff->getNotePixmapFactory(false).getNoteBodyWidth();
+ bool shifted = false;
+
+ if (el.event()->get
+ <Bool>
+ (staff->getProperties().NOTE_HEAD_SHIFTED, shifted) && shifted) {
+ cx += nbw;
+ }
+ }
+
+ if (el.isNote() && haveClickHeight) {
+ long eventHeight = 0;
+ if (el.event()->get
+ <Int>
+ (NotationProperties::HEIGHT_ON_STAFF, eventHeight)) {
+
+ if (eventHeight == clickHeight) {
+
+ if (!clickedNote &&
+ e->x() >= cx &&
+ e->x() <= cx + nbw) {
+ clickedNote = &el;
+ } else if (!clickedVagueNote &&
+ e->x() >= cx - 2 &&
+ e->x() <= cx + nbw + 2) {
+ clickedVagueNote = &el;
+ }
+
+ } else if (eventHeight - 1 == clickHeight ||
+ eventHeight + 1 == clickHeight) {
+ if (!clickedVagueNote)
+ clickedVagueNote = &el;
+ }
+ }
+ } else if (!el.isNote()) {
+ if (!clickedNonNote)
+ clickedNonNote = &el;
+ }
+ }
+
+ int staffNo = -1;
+ if (staff)
+ staffNo = staff->getId();
+
+ if (clickedNote)
+ handleMousePress(clickHeight, staffNo, e, clickedNote);
+ else if (clickedNonNote)
+ handleMousePress(clickHeight, staffNo, e, clickedNonNote);
+ else if (clickedVagueNote)
+ handleMousePress(clickHeight, staffNo, e, clickedVagueNote);
+ else
+ handleMousePress(clickHeight, staffNo, e);
+}
+
+void NotationCanvasView::contentsMouseDoubleClickEvent(QMouseEvent* e)
+{
+ NOTATION_DEBUG << "NotationCanvasView::contentsMouseDoubleClickEvent()\n";
+
+ contentsMousePressEvent(e);
+}
+
+void
+NotationCanvasView::processActiveItems(QMouseEvent* e,
+ QCanvasItemList itemList)
+{
+ QCanvasItem* pressedItem = 0;
+ QCanvasItemList::Iterator it;
+
+ for (it = itemList.begin(); it != itemList.end(); ++it) {
+
+ QCanvasItem *item = *it;
+ if (item->active() && !pressedItem) {
+ NOTATION_DEBUG << "mousepress : got active item\n";
+ pressedItem = item;
+ }
+ }
+
+ if (pressedItem)
+ emit activeItemPressed(e, pressedItem);
+
+}
+
+void
+NotationCanvasView::handleMousePress(int height,
+ int staffNo,
+ QMouseEvent *e,
+ NotationElement *el)
+{
+ NOTATION_DEBUG << "NotationCanvasView::handleMousePress() at height "
+ << height << endl;
+
+ emit itemPressed(height, staffNo, e, el);
+}
+
+bool
+NotationCanvasView::posIsTooFarFromStaff(const QPoint &pos)
+{
+ // return true if pos.y is more than m_staffLineThreshold away from
+ // the last pos for which a collision was detected
+ //
+ return (pos.y() > m_lastYPosNearStaff) ?
+ (pos.y() - m_lastYPosNearStaff) > (int)m_staffLineThreshold :
+ (m_lastYPosNearStaff - pos.y()) > (int)m_staffLineThreshold;
+
+}
+
+int
+NotationCanvasView::getLegerLineCount(int height, bool &offset)
+{
+ //!!! This is far too specifically notation-related to be here, really
+
+ if (height < 0) {
+
+ offset = (( -height % 2) == 1);
+ return height / 2;
+
+ } else if (height > 8) {
+
+ offset = ((height % 2) == 1);
+ return (height - 8) / 2;
+ }
+
+ return 0;
+}
+
+void
+NotationCanvasView::setHeightMarkerHeight(QMouseEvent *e)
+{
+ NotationStaff *staff = dynamic_cast<NotationStaff *>
+ (m_linedStaffManager.getStaffForCanvasCoords(e->x(), e->y()));
+
+ int height = staff->getHeightAtCanvasCoords(e->x(), e->y());
+ int lineY = staff->getCanvasYForHeight(height, e->x(), e->y());
+
+ // NOTATION_DEBUG << "NotationCanvasView::setHeightMarkerHeight: "
+ // << e->y() << " snapped to line -> " << lineY
+ // << " (height " << height << ")" << endl;
+
+ int spacing = staff->getLineSpacing() - 1;
+
+ m_staffLineThreshold = spacing;
+ m_vert1->setPoints(0, -spacing / 2, 0, spacing / 2);
+ m_vert2->setPoints(17, -spacing / 2, 17, spacing / 2); // magic based on mouse cursor size
+ m_heightMarker->setY(lineY);
+
+ bool legerLineOffset = false;
+ int legerLineCount = getLegerLineCount(height, legerLineOffset);
+
+ if (legerLineCount != (int)m_legerLines.size() ||
+ legerLineOffset != m_legerLineOffset) {
+
+ bool above = false;
+ if (legerLineCount < 0) {
+ above = true;
+ legerLineCount = -legerLineCount;
+ }
+
+ int i;
+ for (i = 0; i < (int)m_legerLines.size(); ++i) {
+ delete m_legerLines[i];
+ }
+ m_legerLines.clear();
+
+ for (i = 0; i < legerLineCount; ++i) {
+
+ QCanvasLineGroupable *line =
+ new QCanvasLineGroupable(canvas(), m_heightMarker);
+
+ line->setPen(QPen(QColor(64, 64, 64), 1));
+
+ int y = (int)m_heightMarker->y() +
+ (above ? -1 : 1) * (i * (spacing + 1));
+ int x = (int)m_heightMarker->x() + 1;
+
+ if (legerLineOffset) {
+ if (above)
+ y -= spacing / 2 + 1;
+ else
+ y += spacing / 2;
+ }
+
+ line->setPoints(x, y, x + 15, y); // magic based on mouse cursor size
+ m_legerLines.push_back(line);
+ }
+
+ m_legerLineOffset = legerLineOffset;
+ }
+}
+
+NotationElement *
+NotationCanvasView::getElementAtXCoord(QMouseEvent *e) // any old element
+{
+ QRect threshold(e->pos(), QSize(4, 100)); //!!!
+ threshold.moveCenter(e->pos());
+
+ QCanvasItemList itemList = canvas()->collisions(threshold);
+
+ QCanvasItemList::Iterator it;
+ QCanvasNotationSprite* sprite = 0;
+
+ for (it = itemList.begin(); it != itemList.end(); ++it)
+ {
+
+ QCanvasItem *item = *it;
+
+ if ((sprite = dynamic_cast<QCanvasNotationSprite*>(item))) {
+ return & (sprite->getNotationElement());
+ }
+ }
+
+ return 0;
+}
+
+void
+NotationCanvasView::viewportPaintEvent(QPaintEvent *e)
+{
+ int cx(e->rect().x()),
+ cy(e->rect().y()),
+ cw(e->rect().width()) /*,
+ ch(e->rect().height())*/;
+ // NOTATION_DEBUG << "NotationCanvasView::viewportPaintEvent: (" << cx << ","
+ // << cy << ") size (" << cw << "x" << ch << ")" << endl;
+ QCanvasView::viewportPaintEvent(e);
+
+ cx += contentsX();
+ cy += contentsY();
+ m_lastRender = e->rect();
+ emit renderRequired(std::min(contentsX(), cx),
+ std::max(contentsX() + visibleWidth(), cx + cw));
+}
+
+void
+NotationCanvasView::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
+{
+ /*
+ m_lastRender = QRect(cx, cy, cw, ch);
+ NOTATION_DEBUG << "NotationCanvasView::drawContents: (" << cx << ","
+ << cy << ") size (" << cw << "x" << ch << ")" << endl;
+ */
+ QCanvasView::drawContents(p, cx, cy, cw, ch);
+ /*
+ emit renderRequired(std::min(contentsX(), cx),
+ std::max(contentsX() + visibleWidth(), cx + cw));
+ */
+}
+
+void
+NotationCanvasView::slotRenderComplete()
+{
+ /* QPainter painter(viewport());
+ int cx(m_lastRender.x()),
+ cy(m_lastRender.y()),
+ cw(m_lastRender.width()),
+ ch(m_lastRender.height());
+ NOTATION_DEBUG << "NotationCanvasView::slotRenderComplete: (" << cx << ","
+ << cy << ") size (" << cw << "x" << ch << ")" << endl;
+ QCanvasView::drawContents(&painter, cx, cy, cw, ch);
+ */
+ QPaintEvent ev(m_lastRender);
+ QCanvasView::viewportPaintEvent(&ev);
+}
+
+void
+NotationCanvasView::slotExternalWheelEvent(QWheelEvent* e)
+{
+ wheelEvent(e);
+}
+
+}
+#include "NotationCanvasView.moc"
diff --git a/src/gui/editors/notation/NotationCanvasView.h b/src/gui/editors/notation/NotationCanvasView.h
new file mode 100644
index 0000000..5c88fb0
--- /dev/null
+++ b/src/gui/editors/notation/NotationCanvasView.h
@@ -0,0 +1,218 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTATIONCANVASVIEW_H_
+#define _RG_NOTATIONCANVASVIEW_H_
+
+#include "gui/general/RosegardenCanvasView.h"
+#include <qrect.h>
+#include <vector>
+
+
+class QWidget;
+class QString;
+class QPoint;
+class QPaintEvent;
+class QPainter;
+class QMouseEvent;
+class QCanvasLineGroupable;
+class QCanvasItemGroup;
+class QCanvasItem;
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+class NotationStaff;
+class NotationElement;
+class LinedStaffManager;
+
+
+/**
+ * Central widget for the NotationView window
+ *
+ * This class only takes care of the event handling
+ * (see the various signals).
+ *
+ * It does not deal with any canvas element. All elements are added by
+ * the NotationView.
+ *
+ *@see NotationView
+ */
+
+class NotationCanvasView : public RosegardenCanvasView
+{
+ Q_OBJECT
+
+public:
+ NotationCanvasView(const LinedStaffManager &staffmgr,
+ QCanvas *viewing, QWidget *parent=0,
+ const char *name=0, WFlags f=0);
+
+ ~NotationCanvasView();
+
+ void setHeightTracking(bool t);
+
+signals:
+
+ /**
+ * Emitted when the user clicks on a staff (e.g. mouse button press)
+ * \a pitch is set to the MIDI pitch on which the click occurred
+ * \a staffNo is set to the staff on which the click occurred
+ * \a point is set to the coordinates of the click event
+ * \a el points to the NotationElement which was clicked on, if any
+ */
+ void itemPressed(int pitch, int staffNo,
+ QMouseEvent*,
+ NotationElement* el);
+
+ /**
+ * Emitted when the user clicks on a QCanvasItem which is active
+ *
+ * @see QCanvasItem#setActive
+ */
+ void activeItemPressed(QMouseEvent*,
+ QCanvasItem* item);
+
+ /**
+ * Emitted when the user clicks on a QCanvasItem which is neither
+ * active nor a notation element
+ */
+ void nonNotationItemPressed(QMouseEvent *,
+ QCanvasItem *);
+
+ /**
+ * Emitted when the user clicks on a QCanvasItem which is a
+ * plain QCanvasText
+ */
+ void textItemPressed(QMouseEvent *,
+ QCanvasItem *);
+
+ /**
+ * Emitted when the mouse cursor moves to a different height
+ * on the staff
+ *
+ * \a noteName contains the MIDI name of the corresponding note
+ */
+ void hoveredOverNoteChanged(const QString &noteName);
+
+ /**
+ * Emitted when the mouse cursor moves to a note which is at a
+ * different time
+ *
+ * \a time is set to the absolute time of the note the cursor is
+ * hovering on
+ */
+ void hoveredOverAbsoluteTimeChanged(unsigned int time);
+
+ /**
+ * Emitted when the mouse cursor moves (used by the selection tool)
+ */
+ void mouseMoved(QMouseEvent*);
+
+ /**
+ * Emitted when the mouse button is released
+ */
+ void mouseReleased(QMouseEvent*);
+
+ /**
+ * Emitted when a region is about to be drawn by the canvas view.
+ * Indicates that any on-demand rendering for that region should
+ * be carried out.
+ */
+ void renderRequired(double cx0, double cx1);
+
+public slots:
+ void slotRenderComplete();
+
+ void slotExternalWheelEvent(QWheelEvent* e);
+
+protected:
+
+ virtual void viewportPaintEvent(QPaintEvent *e);
+ virtual void drawContents(QPainter *p, int cx, int cy, int cw, int ch);
+
+ const LinedStaffManager &m_linedStaffManager;
+
+ /**
+ * Callback for a mouse button press event in the canvas
+ */
+ virtual void contentsMousePressEvent(QMouseEvent*);
+
+ /**
+ * Callback for a mouse button release event in the canvas
+ */
+ virtual void contentsMouseReleaseEvent(QMouseEvent*);
+
+ /**
+ * Callback for a mouse move event in the canvas
+ */
+ virtual void contentsMouseMoveEvent(QMouseEvent*);
+
+ /**
+ * Callback for a mouse double click event in the canvas
+ */
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent*);
+
+ void processActiveItems(QMouseEvent*, QCanvasItemList);
+
+ void handleMousePress(int height, int staffNo,
+ QMouseEvent*,
+ NotationElement* pressedNotationElement = 0);
+
+ bool posIsTooFarFromStaff(const QPoint &pos);
+
+ int getLegerLineCount(int height, bool &offset);
+
+ void setHeightMarkerHeight(QMouseEvent *e);
+
+ NotationElement *getElementAtXCoord(QMouseEvent *e);
+
+ //--------------- Data members ---------------------------------
+
+ int m_lastYPosNearStaff;
+
+ unsigned int m_staffLineThreshold;
+
+ NotationStaff *m_currentStaff;
+ int m_currentHeight;
+
+ QCanvasItemGroup *m_heightMarker;
+ QCanvasLineGroupable *m_vert1;
+ QCanvasLineGroupable *m_vert2;
+ std::vector<QCanvasLineGroupable *> m_legerLines;
+ bool m_legerLineOffset;
+
+ bool m_heightTracking;
+
+ QRect m_lastRender;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationChord.cpp b/src/gui/editors/notation/NotationChord.cpp
new file mode 100644
index 0000000..7b0a263
--- /dev/null
+++ b/src/gui/editors/notation/NotationChord.cpp
@@ -0,0 +1,335 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NotationChord.h"
+
+#include "base/Sets.h"
+#include "base/Event.h"
+#include "base/NotationRules.h"
+#include "base/NotationTypes.h"
+#include "base/Quantizer.h"
+#include "NotationProperties.h"
+#include "NoteStyleFactory.h"
+
+namespace Rosegarden
+{
+
+template <>
+Event *
+AbstractSet<NotationElement, NotationElementList>::getAsEvent(const NotationElementList::iterator &i)
+{
+ return (*i)->event();
+}
+
+NotationChord::NotationChord(NotationElementList &c,
+ NotationElementList::iterator i,
+ const Quantizer *quantizer,
+ const NotationProperties &properties,
+ const Clef &clef,
+ const ::Rosegarden::Key &key) :
+ GenericChord < NotationElement,
+ NotationElementList, true > (c, i, quantizer,
+ NotationProperties::STEM_UP),
+ m_properties(properties),
+ m_clef(clef),
+ m_key(key)
+{
+ // nothing else
+}
+
+int
+NotationChord::getHeight(const Iterator &i) const
+{
+ //!!! We use HEIGHT_ON_STAFF in preference to the passed clef/key,
+ //but what if the clef/key changed since HEIGHT_ON_STAFF was
+ //written? Who updates the properties then? Check this.
+
+ long h = 0;
+ if (getAsEvent(i)->get
+ <Int>(NotationProperties::HEIGHT_ON_STAFF, h)) {
+ return h;
+ }
+
+ try {
+ Pitch pitch(*getAsEvent(i));
+ h = pitch.getHeightOnStaff(m_clef, m_key);
+ } catch (...) {
+ // no pitch!
+ }
+
+ // set non-persistent, not setMaybe, as we know the property is absent:
+ getAsEvent(i)->set
+ <Int>(NotationProperties::HEIGHT_ON_STAFF, h, false);
+ return h;
+}
+
+bool
+NotationChord::hasStem() const
+{
+ // true if any of the notes is stemmed
+
+ Iterator i(getInitialNote());
+ for (;;) {
+ long note;
+ if (!getAsEvent(i)->get
+ <Int>(BaseProperties::NOTE_TYPE, note)) return true;
+ if (NoteStyleFactory::getStyleForEvent(getAsEvent(i))->hasStem(note))
+ return true;
+ if (i == getFinalNote())
+ return false;
+ ++i;
+ }
+ return false;
+}
+
+bool
+NotationChord::hasStemUp() const
+{
+ NotationRules rules;
+
+ // believe anything found in any of the notes, if in a persistent
+ // property or a property apparently set by the beaming algorithm
+
+ Iterator i(getInitialNote());
+
+ for (;;) {
+ Event *e = getAsEvent(i);
+ /*!!!
+ if (e->has(m_properties.VIEW_LOCAL_STEM_UP)) {
+ return e->get<Bool>(m_properties.VIEW_LOCAL_STEM_UP);
+ }
+ */
+ if (e->has(NotationProperties::STEM_UP)) {
+ return e->get
+ <Bool>(NotationProperties::STEM_UP);
+ }
+
+ if (e->has(NotationProperties::BEAM_ABOVE)) {
+ if (e->has(NotationProperties::BEAMED) &&
+ e->get
+ <Bool>(NotationProperties::BEAMED)) {
+ return e->get
+ <Bool>(NotationProperties::BEAM_ABOVE);
+ }
+ else {
+ return !e->get
+ <Bool>(NotationProperties::BEAM_ABOVE);
+ }
+ }
+
+ if (i == getFinalNote())
+ break;
+ ++i;
+ }
+
+ return rules.isStemUp(getHighestNoteHeight(),getLowestNoteHeight());
+}
+
+bool
+NotationChord::hasNoteHeadShifted() const
+{
+ int ph = 10000;
+
+ for (unsigned int i = 0; i < size(); ++i) {
+ int h = getHeight((*this)[i]);
+ if (h == ph + 1)
+ return true;
+ ph = h;
+ }
+
+ return false;
+}
+
+bool
+NotationChord::isNoteHeadShifted(const Iterator &itr) const
+{
+ unsigned int i;
+ for (i = 0; i < size(); ++i) {
+ if ((*this)[i] == itr)
+ break;
+ }
+
+ if (i == size()) {
+ std::cerr << "NotationChord::isNoteHeadShifted: Warning: Unable to find note head " << getAsEvent(itr) << std::endl;
+ return false;
+ }
+
+ int h = getHeight((*this)[i]);
+
+ if (hasStemUp()) {
+ if ((i > 0) && (h == getHeight((*this)[i - 1]) + 1)) {
+ return (!isNoteHeadShifted((*this)[i - 1]));
+ }
+ } else {
+ if ((i < size() - 1) && (h == getHeight((*this)[i + 1]) - 1)) {
+ return (!isNoteHeadShifted((*this)[i + 1]));
+ }
+ }
+
+ return false;
+}
+
+void
+NotationChord::applyAccidentalShiftProperties()
+{
+ // Some rules:
+ //
+ // The top accidental always gets the minimum shift (i.e. normally
+ // right next to the note head or stem).
+ //
+ // The bottom accidental gets the next least: the same, if the
+ // interval is more than a sixth, or the next shift out otherwise.
+ //
+ // We then progress up from the bottom accidental upwards.
+ //
+ // These rules aren't really enough, but they might do for now!
+
+ //!!! Uh-oh... we have a catch-22 here. We can't determine the
+ // proper minimum shift until we know which way the stem goes,
+ // because if we have a shifted note head and the stem goes down,
+ // we need to shift one place further than otherwise. But we
+ // don't know for sure which way the stem goes until we've
+ // calculated the beam, and we don't do that until after we've
+ // worked out the x-coordinates based on (among other things) the
+ // accidental shift.
+
+ int minShift = 0;
+ bool extra = false;
+
+ if (!hasStemUp() && hasNoteHeadShifted()) {
+ minShift = 1; // lazy
+ extra = true;
+ }
+
+ int lastShift = minShift;
+ int lastHeight = 0, maxHeight = 999;
+ int lastWidth = 1;
+
+ for (iterator i = end(); i != begin(); ) {
+
+ --i;
+ Event *e = getAsEvent(*i);
+
+ Accidental acc;
+ if (e->get
+ <String>(m_properties.DISPLAY_ACCIDENTAL, acc) &&
+ acc != Accidentals::NoAccidental) {
+ e->setMaybe<Int>(m_properties.ACCIDENTAL_SHIFT, minShift);
+ e->setMaybe<Bool>(m_properties.ACCIDENTAL_EXTRA_SHIFT, extra);
+ maxHeight = lastHeight = getHeight(*i);
+ break;
+ }
+ }
+
+ if (maxHeight == 999) {
+ return ;
+ }
+
+ for (iterator i = begin(); i != end(); ++i) {
+
+ Event *e = getAsEvent(*i);
+ int height = getHeight(*i);
+
+ if (height == maxHeight && e->has(m_properties.ACCIDENTAL_SHIFT)) {
+ // finished -- we've come around to the highest one again
+ break;
+ }
+
+ Accidental acc;
+
+ if (e->get
+ <String>(m_properties.DISPLAY_ACCIDENTAL, acc) &&
+ acc != Accidentals::NoAccidental) {
+
+ int shift = lastShift;
+
+ if (height < lastHeight) { // lastHeight was the first, up top
+ if (lastHeight - height < 6) {
+ shift = lastShift + lastWidth;
+ }
+ } else {
+ if (height - lastHeight >= 6) {
+ if (maxHeight - height >= 6) {
+ shift = minShift;
+ } else {
+ shift = minShift + 1;
+ }
+ } else {
+ shift = lastShift + lastWidth;
+ }
+ }
+
+ e->setMaybe<Int>(m_properties.ACCIDENTAL_SHIFT, shift);
+
+ lastHeight = height;
+ lastShift = shift;
+
+ lastWidth = 1;
+ bool c = false;
+ if (e->get
+ <Bool>(m_properties.DISPLAY_ACCIDENTAL_IS_CAUTIONARY, c)
+ && c) {
+ lastWidth = 2;
+ }
+ }
+ }
+}
+
+int
+NotationChord::getMaxAccidentalShift(bool &extra) const
+{
+ int maxShift = 0;
+
+ for (const_iterator i = begin(); i != end(); ++i) {
+ Event *e = getAsEvent(*i);
+ if (e->has(m_properties.ACCIDENTAL_SHIFT)) {
+ int shift = e->get
+ <Int>(m_properties.ACCIDENTAL_SHIFT);
+ if (shift > maxShift) {
+ maxShift = shift;
+ e->get
+ <Bool>(m_properties.ACCIDENTAL_EXTRA_SHIFT, extra);
+ }
+ }
+ }
+
+ return maxShift;
+}
+
+int
+NotationChord::getAccidentalShift(const Iterator &i, bool &extra) const
+{
+ if (getAsEvent(i)->has(m_properties.ACCIDENTAL_SHIFT)) {
+ int shift = getAsEvent(i)->get
+ <Int>(m_properties.ACCIDENTAL_SHIFT);
+ getAsEvent(i)->get
+ <Bool>(m_properties.ACCIDENTAL_EXTRA_SHIFT, extra);
+ return shift;
+ } else {
+ return 0;
+ }
+}
+
+}
diff --git a/src/gui/editors/notation/NotationChord.h b/src/gui/editors/notation/NotationChord.h
new file mode 100644
index 0000000..7ce12fd
--- /dev/null
+++ b/src/gui/editors/notation/NotationChord.h
@@ -0,0 +1,90 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTATIONCHORD_H_
+#define _RG_NOTATIONCHORD_H_
+
+#include "base/NotationTypes.h"
+#include "base/Sets.h"
+#include "NotationElement.h"
+
+class Iterator;
+
+
+namespace Rosegarden
+{
+
+class Quantizer;
+class NotationProperties;
+
+
+class NotationChord : public GenericChord<NotationElement,
+ NotationElementList,
+ true>
+{
+public:
+ NotationChord(NotationElementList &c,
+ NotationElementList::iterator i,
+ const Quantizer *quantizer,
+ const NotationProperties &properties,
+ const Clef &clef = Clef::DefaultClef,
+ const Key &key = Key::DefaultKey);
+
+ virtual ~NotationChord() { }
+
+ virtual int getHighestNoteHeight() const {
+ return getHeight(getHighestNote());
+ }
+ virtual int getLowestNoteHeight() const {
+ return getHeight(getLowestNote());
+ }
+
+ virtual bool hasStem() const;
+ virtual bool hasStemUp() const;
+
+ virtual bool hasNoteHeadShifted() const;
+ virtual bool isNoteHeadShifted(const NotationElementList::iterator &itr)
+ const;
+
+ void applyAccidentalShiftProperties();
+
+ virtual int getMaxAccidentalShift(bool &extra) const;
+ virtual int getAccidentalShift(const NotationElementList::iterator &itr,
+ bool &extra) const;
+
+protected:
+ const NotationProperties &m_properties;
+ Clef m_clef;
+ Key m_key;
+
+
+ int getHeight(const Iterator&) const;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationElement.cpp b/src/gui/editors/notation/NotationElement.cpp
new file mode 100644
index 0000000..7df1cd5
--- /dev/null
+++ b/src/gui/editors/notation/NotationElement.cpp
@@ -0,0 +1,198 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NotationElement.h"
+#include "misc/Debug.h"
+
+#include "base/BaseProperties.h"
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "base/NotationTypes.h"
+#include "base/ViewElement.h"
+
+#include <qcanvas.h>
+
+namespace Rosegarden
+{
+
+NotationElement::NotationElement(Event *event)
+ : ViewElement(event),
+ m_recentlyRegenerated(false),
+ m_isColliding(false),
+ m_canvasItem(0),
+ m_extraItems(0)
+{
+ // NOTATION_DEBUG << "new NotationElement "
+ // << this << " wrapping " << event << endl;
+}
+
+NotationElement::~NotationElement()
+{
+ removeCanvasItem();
+}
+
+timeT
+NotationElement::getViewAbsoluteTime() const
+{
+ return event()->getNotationAbsoluteTime();
+}
+
+timeT
+NotationElement::getViewDuration() const
+{
+ return event()->getNotationDuration();
+}
+
+double
+NotationElement::getCanvasX()
+{
+ if (m_canvasItem)
+ return m_canvasItem->x();
+ else {
+ std::cerr << "ERROR: No canvas item for this notation element:";
+ event()->dump(std::cerr);
+ throw NoCanvasItem("No canvas item for notation element of type " +
+ event()->getType(), __FILE__, __LINE__);
+ }
+}
+
+double
+NotationElement::getCanvasY()
+{
+ if (m_canvasItem)
+ return m_canvasItem->y();
+ else {
+ std::cerr << "ERROR: No canvas item for this notation element:";
+ event()->dump(std::cerr);
+ throw NoCanvasItem("No canvas item for notation element of type " +
+ event()->getType(), __FILE__, __LINE__);
+ }
+}
+
+bool
+NotationElement::isRest() const
+{
+ return event()->isa(Note::EventRestType);
+}
+
+bool
+NotationElement::isNote() const
+{
+ return event()->isa(Note::EventType);
+}
+
+bool
+NotationElement::isTuplet() const
+{
+ return event()->has(BaseProperties::BEAMED_GROUP_TUPLET_BASE);
+}
+
+bool
+NotationElement::isGrace() const
+{
+ return event()->has(BaseProperties::IS_GRACE_NOTE) &&
+ event()->get
+ <Bool>(BaseProperties::IS_GRACE_NOTE);
+}
+
+void
+NotationElement::setCanvasItem(QCanvasItem *e, double canvasX, double canvasY)
+{
+ removeCanvasItem();
+ m_recentlyRegenerated = true;
+ m_canvasItem = e;
+ e->move(canvasX, canvasY);
+}
+
+void
+NotationElement::addCanvasItem(QCanvasItem *e, double canvasX, double canvasY)
+{
+ if (!m_canvasItem) {
+ std::cerr << "ERROR: Attempt to add extra canvas item to element without main canvas item:";
+ event()->dump(std::cerr);
+ throw NoCanvasItem("No canvas item for notation element of type " +
+ event()->getType(), __FILE__, __LINE__);
+ }
+ if (!m_extraItems) {
+ m_extraItems = new ItemList;
+ }
+ e->move(canvasX, canvasY);
+ m_extraItems->push_back(e);
+}
+
+void
+NotationElement::removeCanvasItem()
+{
+ m_recentlyRegenerated = false;
+
+ delete m_canvasItem;
+ m_canvasItem = 0;
+
+ if (m_extraItems) {
+
+ for (ItemList::iterator i = m_extraItems->begin();
+ i != m_extraItems->end(); ++i)
+ delete *i;
+ m_extraItems->clear();
+
+ delete m_extraItems;
+ m_extraItems = 0;
+ }
+}
+
+void
+NotationElement::reposition(double canvasX, double canvasY)
+{
+ m_recentlyRegenerated = false;
+ if (!m_canvasItem)
+ return ;
+
+ double dx = canvasX - m_canvasItem->x();
+ double dy = canvasY - m_canvasItem->y();
+ m_canvasItem->move(canvasX, canvasY);
+
+ if (m_extraItems) {
+ for (ItemList::iterator i = m_extraItems->begin();
+ i != m_extraItems->end(); ++i) {
+ (*i)->moveBy(dx, dy);
+ }
+ }
+}
+
+bool
+NotationElement::isSelected()
+{
+ return m_canvasItem ? m_canvasItem->selected() : false;
+}
+
+void
+NotationElement::setSelected(bool selected)
+{
+ m_recentlyRegenerated = false;
+ if (m_canvasItem)
+ m_canvasItem->setSelected(selected);
+}
+
+}
diff --git a/src/gui/editors/notation/NotationElement.h b/src/gui/editors/notation/NotationElement.h
new file mode 100644
index 0000000..c756641
--- /dev/null
+++ b/src/gui/editors/notation/NotationElement.h
@@ -0,0 +1,176 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTATIONELEMENT_H_
+#define _RG_NOTATIONELEMENT_H_
+
+#include "base/Exception.h"
+#include "base/ViewElement.h"
+#include <vector>
+#include "base/Event.h"
+
+
+class QCanvasItem;
+class ItemList;
+
+
+namespace Rosegarden
+{
+
+class Event;
+
+
+/**
+ * The Notation H and V layout is performed on a
+ * NotationElementList. Once this is done, each NotationElement is
+ * affected a QCanvasItem which is set at these coords.
+ *
+ * @see NotationView#showElements()
+ */
+
+class NotationElement : public ViewElement
+{
+public:
+ typedef Exception NoCanvasItem;
+
+ NotationElement(Event *event);
+
+ ~NotationElement();
+
+ virtual timeT getViewAbsoluteTime() const;
+ virtual timeT getViewDuration() const;
+
+ void getLayoutAirspace(double &x, double &width) {
+ x = m_airX;
+ width = m_airWidth;
+ }
+
+ void getCanvasAirspace(double &x, double &width) {
+ x = m_airX - getLayoutX() + getCanvasX();
+ width = m_airWidth;
+ }
+
+ /// returns the x pos of the associated canvas item
+ double getCanvasX();
+
+ /// returns the y pos of the associated canvas item
+ double getCanvasY();
+
+ /**
+ * Sets the X coordinate and width of the space "underneath"
+ * this element, i.e. the extents within which a mouse click
+ * or some such might be considered to be interested in this
+ * element as opposed to any other. These are layout coords
+ */
+ void setLayoutAirspace(double x, double width) {
+ m_airX = x; m_airWidth = width;
+ }
+
+ /// Returns true if the wrapped event is a rest
+ bool isRest() const;
+
+ /// Returns true if the wrapped event is a note
+ bool isNote() const;
+
+ /// Returns true if the wrapped event is a tuplet
+ bool isTuplet() const;
+
+ /// Returns true if the wrapped event is a grace note
+ bool isGrace() const;
+
+ /**
+ * Sets the canvas item representing this notation element on screen.
+ *
+ * NOTE: The object takes ownership of its canvas item.
+ */
+ void setCanvasItem(QCanvasItem *e, double canvasX, double canvasY);
+
+ /**
+ * Add an extra canvas item associated with this element, for
+ * example where an element has been split across two or more
+ * staff rows.
+ *
+ * The element will take ownership of these canvas items and
+ * delete them when it deletes the main canvas item.
+ */
+ void addCanvasItem(QCanvasItem *e, double canvasX, double canvasY);
+
+ /**
+ * Remove the main canvas item and any additional ones.
+ */
+ void removeCanvasItem();
+
+ /**
+ * Reset the position of the canvas item (which is assumed to
+ * exist already).
+ */
+ void reposition(double canvasX, double canvasY);
+
+ /**
+ * Return true if setCanvasItem has been called more recently
+ * than reposition. If true, any code that positions this
+ * element will probably not need to regenerate its sprite as
+ * well, even if other indications suggest otherwise.
+ */
+ bool isRecentlyRegenerated() { return m_recentlyRegenerated; }
+
+ bool isSelected();
+ void setSelected(bool selected);
+
+ /**
+ * Return true if the element is a note which lies at the exactly
+ * same place than another note.
+ * Only valid after NotationVLayout::scanStaff() call.
+ * Only a returned true is meaningful (when 2 notes are colliding, the
+ * first element returns false and the second one returns true).
+ */
+ bool isColliding() { return m_isColliding; }
+
+ void setIsColliding(bool value) { m_isColliding = value; }
+
+ /// Returns the associated canvas item
+ QCanvasItem* getCanvasItem() { return m_canvasItem; }
+
+protected:
+ //--------------- Data members ---------------------------------
+
+ double m_airX;
+ double m_airWidth;
+ bool m_recentlyRegenerated;
+ bool m_isColliding;
+
+ QCanvasItem *m_canvasItem;
+
+ typedef std::vector<QCanvasItem *> ItemList;
+ ItemList *m_extraItems;
+};
+
+typedef ViewElementList NotationElementList;
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationEraser.cpp b/src/gui/editors/notation/NotationEraser.cpp
new file mode 100644
index 0000000..4124e44
--- /dev/null
+++ b/src/gui/editors/notation/NotationEraser.cpp
@@ -0,0 +1,115 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NotationEraser.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "document/ConfigGroups.h"
+#include "base/ViewElement.h"
+#include "commands/notation/EraseEventCommand.h"
+#include "gui/general/EditTool.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NotePixmapFactory.h"
+#include <kaction.h>
+#include <kconfig.h>
+#include <qiconset.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+NotationEraser::NotationEraser(NotationView* view)
+ : NotationTool("NotationEraser", view),
+ m_collapseRest(false)
+{
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+ m_collapseRest = config->readBoolEntry("collapse", false);
+
+ new KToggleAction(i18n("Collapse rests after erase"), 0, this,
+ SLOT(slotToggleRestCollapse()), actionCollection(),
+ "toggle_rest_collapse");
+
+ QIconSet icon
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("crotchet")));
+ new KAction(i18n("Switch to Insert Tool"), icon, 0, this,
+ SLOT(slotInsertSelected()), actionCollection(),
+ "insert");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("select")));
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ createMenu("notationeraser.rc");
+}
+
+void NotationEraser::ready()
+{
+ m_nParentView->setCanvasCursor(Qt::pointingHandCursor);
+ m_nParentView->setHeightTracking(false);
+}
+
+void NotationEraser::handleLeftButtonPress(timeT,
+ int,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement* element)
+{
+ if (!element || staffNo < 0)
+ return ;
+
+ EraseEventCommand *command =
+ new EraseEventCommand(m_nParentView->getStaff(staffNo)->getSegment(),
+ element->event(),
+ m_collapseRest);
+
+ m_nParentView->addCommandToHistory(command);
+}
+
+void NotationEraser::slotToggleRestCollapse()
+{
+ m_collapseRest = !m_collapseRest;
+}
+
+void NotationEraser::slotInsertSelected()
+{
+ m_nParentView->slotLastNoteAction();
+}
+
+void NotationEraser::slotSelectSelected()
+{
+ m_parentView->actionCollection()->action("select")->activate();
+}
+
+const QString NotationEraser::ToolName = "notationeraser";
+
+}
+#include "NotationEraser.moc"
diff --git a/src/gui/editors/notation/NotationEraser.h b/src/gui/editors/notation/NotationEraser.h
new file mode 100644
index 0000000..9efdd13
--- /dev/null
+++ b/src/gui/editors/notation/NotationEraser.h
@@ -0,0 +1,81 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTATIONERASER_H_
+#define _RG_NOTATIONERASER_H_
+
+#include "NotationTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class NotationView;
+
+
+/**
+ * This tool will erase a note on mouse click events
+ */
+class NotationEraser : public NotationTool
+{
+ Q_OBJECT
+
+ friend class NotationToolBox;
+
+public:
+
+ virtual void ready();
+
+ virtual void handleLeftButtonPress(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement* el);
+ static const QString ToolName;
+
+public slots:
+ void slotToggleRestCollapse();
+
+ void slotInsertSelected();
+ void slotSelectSelected();
+
+protected:
+ NotationEraser(NotationView*);
+
+ //--------------- Data members ---------------------------------
+
+ bool m_collapseRest;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationGroup.cpp b/src/gui/editors/notation/NotationGroup.cpp
new file mode 100644
index 0000000..78525d9
--- /dev/null
+++ b/src/gui/editors/notation/NotationGroup.cpp
@@ -0,0 +1,979 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NotationGroup.h"
+#include "misc/Debug.h"
+
+#include "base/Equation.h"
+#include "base/Event.h"
+#include "base/NotationRules.h"
+#include "base/NotationTypes.h"
+#include "base/Quantizer.h"
+#include "NotationChord.h"
+#include "NotationElement.h"
+#include "NotationProperties.h"
+#include "NotationStaff.h"
+#include "NoteStyleFactory.h"
+#include "NotePixmapFactory.h"
+
+
+namespace Rosegarden
+{
+
+NotationGroup::NotationGroup(NotationElementList &nel,
+ NELIterator i, const Quantizer *q,
+ std::pair<timeT, timeT> barRange,
+ const NotationProperties &p,
+ const Clef &clef, const Key &key) :
+ AbstractSet<NotationElement, NotationElementList>(nel, i, q),
+ m_barRange(barRange),
+ //!!! What if the clef and/or key change in the course of the group?
+ m_clef(clef),
+ m_key(key),
+ m_weightAbove(0),
+ m_weightBelow(0),
+ m_userSamples(false),
+ m_type(Beamed),
+ m_properties(p)
+{
+ if (!(*i)->event()->get<Int>
+ (BaseProperties::BEAMED_GROUP_ID, m_groupNo)) m_groupNo = -1;
+
+ initialise();
+
+ /*
+ NOTATION_DEBUG << "NotationGroup::NotationGroup: id is " << m_groupNo << endl;
+ i = getInitialElement();
+ while (i != getContainer().end()) {
+ long gid = -1;
+ (*i)->event()->get<Int>(BEAMED_GROUP_ID, gid);
+ NOTATION_DEBUG << "Found element with group id "
+ << gid << endl;
+ if (i == getFinalElement()) break;
+ ++i;
+ }
+ */
+}
+
+NotationGroup::NotationGroup(NotationElementList &nel,
+ const Quantizer *q,
+ const NotationProperties &p,
+ const Clef &clef, const Key &key) :
+ AbstractSet<NotationElement, NotationElementList>(nel, nel.end(), q),
+ m_barRange(0, 0),
+ //!!! What if the clef and/or key change in the course of the group?
+ m_clef(clef),
+ m_key(key),
+ m_weightAbove(0),
+ m_weightBelow(0),
+ m_userSamples(true),
+ m_groupNo( -1),
+ m_type(Beamed),
+ m_properties(p)
+{
+ //...
+}
+
+NotationGroup::~NotationGroup()
+{}
+
+bool NotationGroup::test(const NELIterator &i)
+{
+ // An event is a candidate for being within the bounds of the
+ // set if it's simply within the same bar as the original event.
+ // (Groups may contain other groups, so our bounds may enclose
+ // events that aren't members of the group: we reject those in
+ // sample rather than test, so as to avoid erroneously ending
+ // the group too soon.)
+
+ return ((*i)->getViewAbsoluteTime() >= m_barRange.first &&
+ (*i)->getViewAbsoluteTime() < m_barRange.second);
+}
+
+bool
+NotationGroup::sample(const NELIterator &i, bool goingForwards)
+{
+ if (m_baseIterator == getContainer().end()) {
+ m_baseIterator = i;
+ if (m_userSamples)
+ m_initial = i;
+ }
+ if (m_userSamples)
+ m_final = i;
+
+ std::string t;
+ if (!(*i)->event()->get<String>(BaseProperties::BEAMED_GROUP_TYPE, t)) {
+ NOTATION_DEBUG << "NotationGroup::NotationGroup: Rejecting sample() for non-beamed element" << endl;
+ return false;
+ }
+
+ long n;
+ if (!(*i)->event()->get<Int>(BaseProperties::BEAMED_GROUP_ID, n)) return false;
+ if (m_groupNo == -1) {
+ m_groupNo = n;
+ } else if (n != m_groupNo) {
+ NOTATION_DEBUG << "NotationGroup::NotationGroup: Rejecting sample() for event with group id " << n << " (mine is " << m_groupNo << ")" << endl;
+ return false;
+ }
+
+ if (t == BaseProperties::GROUP_TYPE_BEAMED) {
+ m_type = Beamed;
+ } else if (t == BaseProperties::GROUP_TYPE_TUPLED) {
+ m_type = Tupled;
+ } else if (t == BaseProperties::GROUP_TYPE_GRACE) {
+ std::cerr << "NotationGroup::NotationGroup: WARNING: Obsolete group type Grace found" << std::endl;
+ return false;
+ } else {
+ NOTATION_DEBUG << "NotationGroup::NotationGroup: Warning: Rejecting sample() for unknown GroupType \"" << t << "\"" << endl;
+ return false;
+ }
+
+ NOTATION_DEBUG << "NotationGroup::sample: group id is " << m_groupNo << endl;
+
+ AbstractSet<NotationElement, NotationElementList>::sample
+ (i, goingForwards);
+
+ // If the sum of the distances from the middle line to the notes
+ // above the middle line exceeds the sum of the distances from the
+ // middle line to the notes below, then the beam goes below. We
+ // can calculate the weightings here, as we construct the group.
+
+ if (!static_cast<NotationElement*>(*i)->isNote())
+ return true;
+ if (m_userSamples) {
+ if (m_initialNote == getContainer().end()) m_initialNote = i;
+ m_finalNote = i;
+ }
+
+ // The code that uses the Group should not rely on the presence of
+ // e.g. BEAM_GRADIENT to indicate that a beam should be drawn;
+ // it's possible the gradient might be left over from a previous
+ // calculation and the group might have changed since. Instead it
+ // should test BEAMED, which may be false even if there is a
+ // gradient present.
+ (*i)->event()->setMaybe<Bool>(NotationProperties::BEAMED, false);
+
+ int h = height(i);
+ if (h > 4)
+ m_weightAbove += h - 4;
+ if (h < 4)
+ m_weightBelow += 4 - h;
+
+ return true;
+}
+
+bool
+NotationGroup::contains(const NELIterator &i) const
+{
+ NELIterator j(getInitialElement()),
+ k( getFinalElement());
+
+ for (;;) {
+ if (j == i)
+ return true;
+ if (j == k)
+ return false;
+ ++j;
+ }
+}
+
+int
+NotationGroup::height(const NELIterator &i) const
+{
+ long h = 0;
+ if ((*i)->event()->get<Int>(NotationProperties::HEIGHT_ON_STAFF, h)) {
+ return h;
+ }
+
+ //!!! int pitch = (*i)->event()->get<Int>(PITCH);
+ // NotationDisplayPitch p(pitch, m_clef, m_key);
+ // h = p.getHeightOnStaff();
+
+ try {
+ Pitch pitch(*getAsEvent(i));
+ h = pitch.getHeightOnStaff(m_clef, m_key);
+ } catch (...) {
+ // no pitch!
+ }
+
+ // not setMaybe, as we know the property is absent:
+ (*i)->event()->set<Int>(NotationProperties::HEIGHT_ON_STAFF, h, false);
+ return h;
+}
+
+void
+NotationGroup::applyStemProperties()
+{
+ NotationRules rules;
+
+ NELIterator
+ initialNote(getInitialNote()),
+ finalNote(getFinalNote());
+
+ if (initialNote == getContainer().end() ||
+ initialNote == finalNote) {
+ //!!! This is not true -- if initialNote == finalNote there is
+ // one note in the group, not none. But we still won't have a
+ // beam.
+ NOTATION_DEBUG << "NotationGroup::applyStemProperties: no notes in group"
+ << endl;
+ return; // no notes, no case to answer
+ }
+
+ if (getHighestNote() == getContainer().end()) {
+ std::cerr << "ERROR: NotationGroup::applyStemProperties: no highest note!" << std::endl;
+ abort();
+ }
+
+ if (getLowestNote() == getContainer().end()) {
+ std::cerr << "ERROR: NotationGroup::applyStemProperties: no lowest note!" << std::endl;
+ abort();
+ }
+
+ int up = 0, down = 0;
+
+ for (NELIterator i = initialNote; i != getContainer().end(); ++i) {
+ NotationElement* el = static_cast<NotationElement*>(*i);
+ if (el->isNote()) {
+ if (el->event()->has(NotationProperties::STEM_UP)) {
+ if (el->event()->get<Bool>(NotationProperties::STEM_UP)) ++up;
+ else ++down;
+ }
+ }
+
+ if (i == finalNote) break;
+ }
+
+ NOTATION_DEBUG << "NotationGroup::applyStemProperties: weightAbove "
+ << m_weightAbove << ", weightBelow " << m_weightBelow
+ << ", up " << up << ", down " << down << endl;
+
+ bool aboveNotes = rules.isBeamAbove(height(getHighestNote()),
+ height(getLowestNote()),
+ m_weightAbove,
+ m_weightBelow);
+ if (up != down) {
+ if (up > down) aboveNotes = true;
+ else aboveNotes = false;
+ }
+
+ NOTATION_DEBUG << "NotationGroup::applyStemProperties: hence aboveNotes "
+ << aboveNotes << endl;
+
+ /*!!!
+ if ((*initialNote)->event()->has(STEM_UP) &&
+ (*initialNote)->event()->isPersistent<Bool>(STEM_UP)) {
+ aboveNotes = (*initialNote)->event()->get<Bool>(STEM_UP);
+ }
+
+ if ((*initialNote)->event()->has(NotationProperties::BEAM_ABOVE) &&
+ (*initialNote)->event()->isPersistent<Bool>(NotationProperties::BEAM_ABOVE)) {
+ aboveNotes = (*initialNote)->event()->get<Bool>
+ (NotationProperties::BEAM_ABOVE);
+ }
+ */
+ for (NELIterator i = initialNote; i != getContainer().end(); ++i) {
+
+ NotationElement* el = static_cast<NotationElement*>(*i);
+
+ el->event()->setMaybe<Bool>(NotationProperties::BEAM_ABOVE, aboveNotes);
+
+ if (el->isNote() &&
+ el->event()->has(BaseProperties::NOTE_TYPE) &&
+ el->event()->get<Int>(BaseProperties::NOTE_TYPE) < Note::Crotchet &&
+ el->event()->has(BaseProperties::BEAMED_GROUP_ID) &&
+ el->event()->get<Int>(BaseProperties::BEAMED_GROUP_ID) == m_groupNo) {
+
+ el->event()->setMaybe<Bool>(NotationProperties::BEAMED, true);
+ // el->event()->setMaybe<Bool>(m_properties.VIEW_LOCAL_STEM_UP, aboveNotes);
+
+ } else if (el->isNote()) {
+
+ if (i == initialNote || i == finalNote) {
+ (*i)->event()->setMaybe<Bool>
+ (m_properties.VIEW_LOCAL_STEM_UP, aboveNotes);
+ } else {
+ (*i)->event()->setMaybe<Bool>
+ (m_properties.VIEW_LOCAL_STEM_UP, !aboveNotes);
+ }
+ }
+
+ if (i == finalNote) break;
+ }
+}
+
+bool
+NotationGroup::haveInternalRest()
+const
+{
+ bool inside = false;
+ bool found = false;
+
+ for (NELIterator i = getInitialNote(); i != getContainer().end(); ++i) {
+ NotationElement* el = static_cast<NotationElement*>(*i);
+
+ if (el->isNote() &&
+ el->event()->has(BaseProperties::NOTE_TYPE) &&
+ el->event()->get<Int>(BaseProperties::NOTE_TYPE) < Note::Crotchet &&
+ el->event()->has(BaseProperties::BEAMED_GROUP_ID) &&
+ el->event()->get<Int>(BaseProperties::BEAMED_GROUP_ID) == m_groupNo) {
+ if (found) return true; // a rest is wholly enclosed by beamed notes
+ inside = true;
+ }
+
+ if (el->isRest() && inside) found = true;
+
+ if (i == getFinalNote()) break;
+ }
+
+ return false;
+}
+
+NotationGroup::Beam
+NotationGroup::calculateBeam(NotationStaff &staff)
+{
+ NotationRules rules;
+
+ Beam beam;
+ beam.aboveNotes = true;
+ beam.startY = 0;
+ beam.gradient = 0;
+ beam.necessary = false;
+
+ NELIterator
+ initialNote(getInitialNote()),
+ finalNote(getFinalNote());
+
+ if (initialNote == getContainer().end() ||
+ initialNote == finalNote) {
+ return beam; // no notes, or at most one: no case to answer
+ }
+
+ beam.aboveNotes = rules.isBeamAbove(height(getHighestNote()),
+ height(getLowestNote()),
+ m_weightAbove,
+ m_weightBelow);
+
+ if ((*initialNote)->event()->has(NotationProperties::BEAM_ABOVE)) {
+ beam.aboveNotes = (*initialNote)->event()->get
+ <Bool>
+ (NotationProperties::BEAM_ABOVE);
+ }
+
+ timeT crotchet = Note(Note::Crotchet).getDuration();
+
+ beam.necessary =
+ (*initialNote)->getViewDuration() < crotchet &&
+ (*finalNote)->getViewDuration() < crotchet;
+
+ beam.necessary = beam.necessary &&
+ (((*finalNote)->getViewAbsoluteTime() >
+ (*initialNote)->getViewAbsoluteTime()) ||
+ (((*finalNote)->getViewAbsoluteTime() ==
+ (*initialNote)->getViewAbsoluteTime()) &&
+ ((*finalNote)->event()->getSubOrdering() >
+ (*initialNote)->event()->getSubOrdering())));
+
+ // We continue even if the beam is not necessary, because the
+ // same data is used to generate the tupling line in tupled
+ // groups that do not have beams
+
+ // if (!beam.necessary) return beam;
+
+ NOTATION_DEBUG << "NotationGroup::calculateBeam: beam necessariness: " << beam.necessary << endl;
+
+ NotationChord initialChord(getContainer(), initialNote, &getQuantizer(),
+ m_properties, m_clef, m_key),
+ finalChord(getContainer(), finalNote, &getQuantizer(),
+ m_properties, m_clef, m_key);
+
+ if (initialChord.getInitialElement() == finalChord.getInitialElement()) {
+ return beam;
+ }
+
+ bool isGrace =
+ (*initialNote)->event()->has(BaseProperties::IS_GRACE_NOTE) &&
+ (*initialNote)->event()->get<Bool>(BaseProperties::IS_GRACE_NOTE);
+
+ int initialHeight, finalHeight, extremeHeight;
+ NELIterator extremeNote;
+
+ if (beam.aboveNotes) {
+
+ initialHeight = height(initialChord.getHighestNote());
+ finalHeight = height( finalChord.getHighestNote());
+ extremeHeight = height( getHighestNote());
+ extremeNote = getHighestNote();
+
+ } else {
+ initialHeight = height(initialChord.getLowestNote());
+ finalHeight = height( finalChord.getLowestNote());
+ extremeHeight = height( getLowestNote());
+ extremeNote = getLowestNote();
+ }
+
+ int diff = initialHeight - finalHeight;
+ if (diff < 0) diff = -diff;
+
+ bool linear =
+ (beam.aboveNotes ?
+ (extremeHeight <= std::max(initialHeight, finalHeight)) :
+ (extremeHeight >= std::min(initialHeight, finalHeight)));
+
+ if (!linear) {
+ if (diff > 2)
+ diff = 1;
+ else
+ diff = 0;
+ }
+
+ // Now, we need to judge the height of the beam such that the
+ // nearest note of the whole group, the nearest note of the first
+ // chord and the nearest note of the final chord are all at least
+ // two note-body-heights away from it, and at least one of the
+ // start and end points is at least the usual note stem-length
+ // away from it. This is a straight-line equation y = mx + c,
+ // where we have m and two x,y pairs and need to find c.
+
+ //!!! If we find that making one extreme a sensible distance from
+ //the note head makes the other extreme way too far away from it
+ //in the direction of the gradient, then we should flatten the
+ //gradient. There may be a better heuristic for this.
+
+ int initialX = (int)(*initialNote)->getLayoutX();
+ int finalDX = (int) (*finalNote)->getLayoutX() - initialX;
+ int extremeDX = (int)(*extremeNote)->getLayoutX() - initialX;
+
+ int spacing = staff.getNotePixmapFactory(isGrace).getLineSpacing();
+
+ beam.gradient = 0;
+ if (finalDX > 0) {
+ do {
+ if (diff == 0)
+ break;
+ else if (diff > 3)
+ diff = 3;
+ else
+ --diff;
+ beam.gradient = (diff * spacing * 100) / (finalDX * 2);
+ } while (beam.gradient > 18);
+ } else {
+ beam.gradient = 0;
+ }
+ if (initialHeight < finalHeight)
+ beam.gradient = -beam.gradient;
+
+ int finalY = staff.getLayoutYForHeight(finalHeight);
+ int extremeY = staff.getLayoutYForHeight(extremeHeight);
+
+ int c0 = staff.getLayoutYForHeight(initialHeight), c1, c2;
+ double dgrad = (double)beam.gradient / 100.0;
+
+ Equation::solve(Equation::C, extremeY, dgrad, extremeDX, c1);
+ Equation::solve(Equation::C, finalY, dgrad, finalDX, c2);
+
+ using std::max;
+ using std::min;
+ long shortestNoteType = Note::Quaver;
+ if (!(*getShortestElement())->event()->get
+ <Int>(BaseProperties::NOTE_TYPE,
+ shortestNoteType)) {
+ NOTATION_DEBUG << "NotationGroup::calculateBeam: WARNING: Shortest element has no note-type; should this be possible?" << endl;
+ NOTATION_DEBUG << "(Event dump follows)" << endl;
+ (*getShortestElement())->event()->dump(std::cerr);
+ }
+
+ // minimal stem lengths at start, middle-extreme and end of beam
+ int sl = staff.getNotePixmapFactory(isGrace).getStemLength();
+ int ml = spacing * 2;
+ int el = sl;
+
+ NOTATION_DEBUG << "c0: " << c0 << ", c1: " << c1 << ", c2: " << c2 << endl;
+ NOTATION_DEBUG << "sl: " << sl << ", ml: " << ml << ", el: " << el << endl;
+
+ // If the stems are down, we will need to ensure they end at
+ // heights lower than 0 if there's an internal rest -- likewise
+ // with stems up and an internal rest we need to ensure they end
+ // at higher than 8.
+ // [Avoid doing expensive haveInternalRest() test where possible]
+
+ if (beam.aboveNotes) {
+
+ int topY = staff.getLayoutYForHeight(8);
+
+ if ((c0 - sl > topY) || (c1 - ml > topY) || (c2 - el > topY)) {
+ if (haveInternalRest()) {
+ if (c0 - sl > topY) sl = c0 - topY;
+ if (c1 - ml > topY) ml = c1 - topY;
+ if (c2 - el > topY) el = c2 - topY;
+ NOTATION_DEBUG << "made internal rest adjustment for above notes" << endl;
+ NOTATION_DEBUG << "sl: " << sl << ", ml: " << ml << ", el: " << el << endl;
+ }
+ }
+ } else {
+ int bottomY = staff.getLayoutYForHeight(0);
+
+ if ((c0 + sl < bottomY) || (c1 + ml < bottomY) || (c2 + el < bottomY)) {
+ if (haveInternalRest()) {
+ if (c0 + sl < bottomY) sl = bottomY - c0;
+ if (c1 + ml < bottomY) ml = bottomY - c1;
+ if (c2 + el < bottomY) el = bottomY - c2;
+ NOTATION_DEBUG << "made internal rest adjustment for below notes" << endl;
+ NOTATION_DEBUG << "sl: " << sl << ", ml: " << ml << ", el: " << el << endl;
+ }
+ }
+ }
+
+
+ if (shortestNoteType < Note::Semiquaver) {
+ int off = spacing * (Note::Semiquaver - shortestNoteType);
+ sl += off;
+ el += off;
+ }
+
+ if (shortestNoteType < Note::Quaver) {
+ int off = spacing * (Note::Quaver - shortestNoteType);
+ ml += off;
+ }
+
+
+ int midY = staff.getLayoutYForHeight(4);
+
+ // ensure extended to middle line if necessary, and assign suitable stem length
+ if (beam.aboveNotes) {
+ if (c0 - sl > midY) sl = c0 - midY;
+ if (c1 - ml > midY) ml = c1 - midY;
+ if (c2 - el > midY) el = c2 - midY;
+ if (extremeDX > 1.0 || extremeDX < -1.0) {
+ // beam.gradient = int(100 * double(c2 - c0) / double(extremeDX));
+ }
+ beam.startY = min(min(c0 - sl, c1 - ml), c2 - el);
+ } else {
+ if (c0 + sl < midY) sl = midY - c0;
+ if (c1 + ml < midY) ml = midY - c1;
+ if (c2 + el < midY) el = midY - c2;
+ if (extremeDX > 1.0 || extremeDX < -1.0) {
+ // beam.gradient = int(100 * double(c2 - c0) / double(extremeDX));
+ }
+ beam.startY = max(max(c0 + sl, c1 + ml), c2 + el);
+ }
+ /*
+ NOTATION_DEBUG << "NotationGroup::calculateBeam: beam data:" << endl
+ << "gradient: " << beam.gradient << endl
+ << "(c0 " << c0 << ", c2 " << c2 << ", extremeDX " << extremeDX << ")" << endl
+ << "startY: " << beam.startY << endl
+ << "aboveNotes: " << beam.aboveNotes << endl
+ << "shortestNoteType: " << shortestNoteType << endl
+ << "necessary: " << beam.necessary << endl;
+ */
+ return beam;
+}
+
+void
+NotationGroup::applyBeam(NotationStaff &staff)
+{
+ // NOTATION_DEBUG << "NotationGroup::applyBeam, group no is " << m_groupNo << endl;
+ /*
+ NOTATION_DEBUG << "\nNotationGroup::applyBeam" << endl;
+ NOTATION_DEBUG << "Group id: " << m_groupNo << ", type " << m_type << endl;
+ NOTATION_DEBUG << "Coverage:" << endl;
+ int i = 0;
+ for (NELIterator i = getInitialElement(); i != getContainer().end(); ++i) {
+ (*i)->event()->dump(cerr);
+ if (i == getFinalElement()) break;
+ }
+ {
+ NELIterator i(getInitialNote());
+ NOTATION_DEBUG << "Initial note: " << (i == getContainer().end() ? -1 : (*i)->event()->getAbsoluteTime()) << endl;
+ }
+ {
+ NELIterator i(getFinalNote());
+ NOTATION_DEBUG << "Final note: " << (i == getContainer().end() ? -1 : (*i)->event()->getAbsoluteTime()) << endl;
+ }
+ {
+ NELIterator i(getHighestNote());
+ NOTATION_DEBUG << "Highest note: " << (i == getContainer().end() ? -1 : (*i)->event()->getAbsoluteTime()) << endl;
+ }
+ {
+ NELIterator i(getLowestNote());
+ NOTATION_DEBUG << "Lowest note: " << (i == getContainer().end() ? -1 : (*i)->event()->getAbsoluteTime()) << endl;
+ }
+ */
+ Beam beam(calculateBeam(staff));
+ if (!beam.necessary) {
+ for (NELIterator i = getInitialNote(); i != getContainer().end(); ++i) {
+ (*i)->event()->unset(NotationProperties::BEAMED);
+ (*i)->event()->unset(m_properties.TUPLING_LINE_MY_Y);
+ if (i == getFinalNote())
+ break;
+ }
+ return ;
+ }
+
+ // NOTATION_DEBUG << "NotationGroup::applyBeam: Beam is necessary" << endl;
+
+ NELIterator initialNote(getInitialNote()),
+ finalNote( getFinalNote());
+ int initialX = (int)(*initialNote)->getLayoutX();
+ timeT finalTime = (*finalNote)->getViewAbsoluteTime();
+
+ // For each chord in the group, we nominate the note head furthest
+ // from the beam as the primary note, the one that "owns" the stem
+ // and the section of beam up to the following chord. For this
+ // note, we need to:
+ //
+ // * Set the start height, start x-coord and gradient of the beam
+ // (we can't set the stem length for this note directly, because
+ // we don't know its y-coordinate yet)
+ //
+ // * Set width of this section of beam
+ //
+ // * Set the number of beams required for the following note (one
+ // slight complication here: a beamed group in which the very
+ // first chord is shorter than the following one. Here the first
+ // chord needs to know it's the first, or else it can't draw the
+ // part-beams immediately to its right correctly.)
+ //
+ // For the rest of the notes in the chord, we just need to
+ // indicate that they aren't part of the beam-drawing process and
+ // don't need to draw a stem.
+
+ NELIterator prev = getContainer().end(), prevprev = getContainer().end();
+ double gradient = (double)beam.gradient / 100.0;
+
+ // NOTATION_DEBUG << "NotationGroup::applyBeam starting for group "<< this << endl;
+
+ for (NELIterator i = getInitialNote(); i != getContainer().end(); ++i) {
+ NotationElement* el = static_cast<NotationElement*>(*i);
+
+ // Clear tuplingness for all events in the group, to be
+ // reinstated by any subsequent call to applyTuplingLine. We
+ // do this because applyTuplingLine doesn't clear these
+ // properties from notes that don't need them; it only applies
+ // them to notes that do.
+ el->event()->unset(m_properties.TUPLING_LINE_MY_Y);
+
+ if (el->isNote() &&
+ el->event()->has(BaseProperties::NOTE_TYPE) &&
+ el->event()->get
+ <Int>(BaseProperties::NOTE_TYPE) < Note::Crotchet &&
+ el->event()->has(BaseProperties::BEAMED_GROUP_ID) &&
+ el->event()->get<Int>(BaseProperties::BEAMED_GROUP_ID) == m_groupNo) {
+
+ NotationChord chord(getContainer(), i, &getQuantizer(),
+ m_properties, m_clef, m_key);
+ unsigned int j;
+
+ // NOTATION_DEBUG << "NotationGroup::applyBeam: Found chord" << endl;
+
+ bool hasShifted = chord.hasNoteHeadShifted();
+
+ for (j = 0; j < chord.size(); ++j) {
+ NotationElement *el = static_cast<NotationElement*>(*chord[j]);
+
+ el->event()->setMaybe<Bool>
+ (m_properties.CHORD_PRIMARY_NOTE, false);
+
+ el->event()->setMaybe<Bool>
+ (m_properties.DRAW_FLAG, false);
+
+ el->event()->setMaybe<Bool>
+ (NotationProperties::BEAMED, true);
+
+ el->event()->setMaybe<Bool>
+ (NotationProperties::BEAM_ABOVE, beam.aboveNotes);
+
+ el->event()->setMaybe<Bool>
+ (m_properties.VIEW_LOCAL_STEM_UP, beam.aboveNotes);
+
+ bool shifted = chord.isNoteHeadShifted(chord[j]);
+ el->event()->setMaybe<Bool>
+ (m_properties.NOTE_HEAD_SHIFTED, shifted);
+
+ long dots = 0;
+ (void)el->event()->get
+ <Int>(BaseProperties::NOTE_DOTS, dots);
+
+ el->event()->setMaybe<Bool>
+ (m_properties.NOTE_DOT_SHIFTED, false);
+ if (hasShifted && beam.aboveNotes) {
+ long dots = 0;
+ (void)el->event()->get
+ <Int>(BaseProperties::NOTE_DOTS, dots);
+ if (dots > 0) {
+ el->event()->setMaybe<Bool>
+ (m_properties.NOTE_DOT_SHIFTED, true);
+ }
+ }
+
+ el->event()->setMaybe<Bool>
+ (m_properties.NEEDS_EXTRA_SHIFT_SPACE,
+ chord.hasNoteHeadShifted() && !beam.aboveNotes);
+ }
+
+ if (beam.aboveNotes)
+ j = 0;
+ else
+ j = chord.size() - 1;
+
+ NotationElement *el = static_cast<NotationElement*>(*chord[j]);
+ el->event()->setMaybe<Bool>(NotationProperties::BEAMED, false); // set later
+ el->event()->setMaybe<Bool>(m_properties.DRAW_FLAG, true); // set later
+
+ int x = (int)el->getLayoutX();
+ int myY = (int)(gradient * (x - initialX)) + beam.startY;
+
+ int beamCount =
+ NoteStyleFactory::getStyleForEvent(el->event())->
+ getFlagCount(el->event()->get
+ <Int>(BaseProperties::NOTE_TYPE));
+
+ // If THIS_PART_BEAMS is true, then when drawing the
+ // chord, if it requires more beams than the following
+ // chord then they should be added as partial beams to the
+ // right of the stem.
+
+ // If NEXT_PART_BEAMS is true, then when drawing the
+ // chord, if it requires fewer beams than the following
+ // chord then the difference should be added as partial
+ // beams to the left of the following chord's stem.
+
+ // Procedure for setting these: If we have more beams than
+ // the preceding chord, then the preceding chord should
+ // have NEXT_PART_BEAMS set, until possibly unset again on
+ // the next iteration. If we have at least as many beams
+ // as the preceding chord, then the preceding chord should
+ // have THIS_PART_BEAMS unset and the one before it should
+ // have NEXT_PART_BEAMS unset. The first chord should
+ // have THIS_PART_BEAMS set, until possibly unset again on
+ // the next iteration.
+
+ if (prev != getContainer().end()) {
+
+ NotationElement *prevEl = static_cast<NotationElement*>(*prev);
+ int secWidth = x - (int)prevEl->getLayoutX();
+
+ // prevEl->event()->setMaybe<Int>(BEAM_NEXT_Y, myY);
+
+ prevEl->event()->setMaybe<Int>
+ (m_properties.BEAM_SECTION_WIDTH, secWidth);
+ prevEl->event()->setMaybe<Int>
+ (m_properties.BEAM_NEXT_BEAM_COUNT, beamCount);
+
+ int prevBeamCount =
+ NoteStyleFactory::getStyleForEvent(prevEl->event())->
+ getFlagCount(prevEl->event()->get
+ <Int>(BaseProperties::NOTE_TYPE));
+
+ if ((beamCount > 0) && (prevBeamCount > 0)) {
+ el->event()->setMaybe<Bool>(m_properties.BEAMED, true);
+ el->event()->setMaybe<Bool>(m_properties.DRAW_FLAG, false);
+ prevEl->event()->setMaybe<Bool>(m_properties.BEAMED, true);
+ prevEl->event()->setMaybe<Bool>(m_properties.DRAW_FLAG, false);
+ }
+
+ if (beamCount >= prevBeamCount) {
+ prevEl->event()->setMaybe<Bool>
+ (m_properties.BEAM_THIS_PART_BEAMS, false);
+ if (prevprev != getContainer().end()) {
+ (*prevprev)->event()->setMaybe<Bool>
+ (m_properties.BEAM_NEXT_PART_BEAMS, false);
+ }
+ }
+
+ if (beamCount > prevBeamCount) {
+ prevEl->event()->setMaybe<Bool>
+ (m_properties.BEAM_NEXT_PART_BEAMS, true);
+ }
+
+ } else {
+ el->event()->setMaybe<Bool>(m_properties.BEAM_THIS_PART_BEAMS, true);
+ }
+
+ el->event()->setMaybe<Bool>(m_properties.CHORD_PRIMARY_NOTE, true);
+
+ el->event()->setMaybe<Int>(m_properties.BEAM_MY_Y, myY);
+ el->event()->setMaybe<Int>(m_properties.BEAM_GRADIENT, beam.gradient);
+
+ // until they're set next time around the loop, as (*prev)->...
+ // el->event()->setMaybe<Int>(m_properties.BEAM_NEXT_Y, myY);
+ el->event()->setMaybe<Int>(m_properties.BEAM_SECTION_WIDTH, 0);
+ el->event()->setMaybe<Int>(m_properties.BEAM_NEXT_BEAM_COUNT, 1);
+
+ prevprev = prev;
+ prev = chord[j];
+ i = chord.getFinalElement();
+
+ }
+ else if (el->isNote()) {
+
+ //!!! should we really be setting these here as well as in
+ // applyStemProperties?
+ /*
+ if (i == initialNote || i == finalNote) {
+ (*i)->event()->setMaybe<Bool>(m_properties.VIEW_LOCAL_STEM_UP, beam.aboveNotes);
+ } else {
+ (*i)->event()->setMaybe<Bool>(m_properties.VIEW_LOCAL_STEM_UP, !beam.aboveNotes);
+ }
+ */
+ }
+
+ if (i == finalNote || el->getViewAbsoluteTime() > finalTime) break;
+ }
+}
+
+void
+NotationGroup::applyTuplingLine(NotationStaff &staff)
+{
+ // NOTATION_DEBUG << "NotationGroup::applyTuplingLine, group no is " << m_groupNo << ", group type is " << m_type << endl;
+
+ if (m_type != Tupled)
+ return ;
+
+ // NOTATION_DEBUG << "NotationGroup::applyTuplingLine: line is necessary" << endl;
+
+ Beam beam(calculateBeam(staff));
+
+ NELIterator
+ initialNote(getInitialNote()),
+ finalNote(getFinalNote()),
+ initialElement(getInitialElement()),
+ finalElement(getFinalElement());
+
+ NELIterator initialNoteOrRest(initialElement);
+ NotationElement* initialNoteOrRestEl = static_cast<NotationElement*>(*initialNoteOrRest);
+
+ while (initialNoteOrRest != finalElement &&
+ !(initialNoteOrRestEl->isNote() ||
+ initialNoteOrRestEl->isRest())) {
+ ++initialNoteOrRest;
+ initialNoteOrRestEl = static_cast<NotationElement*>(*initialNoteOrRest);
+ }
+
+ if (!initialNoteOrRestEl->isRest()) {
+ initialNoteOrRest = initialNote;
+ initialNoteOrRestEl = static_cast<NotationElement*>(*initialNoteOrRest);
+ }
+
+ if (initialNoteOrRest == staff.getViewElementList()->end()) return;
+
+ bool isGrace = false;
+ if (initialNote != staff.getViewElementList()->end()) {
+ isGrace =
+ (*initialNote)->event()->has(BaseProperties::IS_GRACE_NOTE) &&
+ (*initialNote)->event()->get<Bool>(BaseProperties::IS_GRACE_NOTE);
+ }
+
+ // NOTATION_DEBUG << "NotationGroup::applyTuplingLine: first element is " << (initialNoteOrRestEl->isNote() ? "Note" : "Non-Note") << ", last is " << (static_cast<NotationElement*>(*finalElement)->isNote() ? "Note" : "Non-Note") << endl;
+
+ int initialX = (int)(*initialNoteOrRest)->getLayoutX();
+ int finalX = (int)(*finalElement)->getLayoutX();
+
+ if (initialNote == staff.getViewElementList()->end() &&
+ finalNote == staff.getViewElementList()->end()) {
+
+ Event *e = (*initialNoteOrRest)->event();
+ e->setMaybe<Int>(m_properties.TUPLING_LINE_MY_Y,
+ staff.getLayoutYForHeight(12));
+ e->setMaybe<Int>(m_properties.TUPLING_LINE_WIDTH, finalX - initialX);
+ e->setMaybe<Int>(m_properties.TUPLING_LINE_GRADIENT, 0);
+
+ } else {
+
+ // only notes have height
+ int initialY = staff.getLayoutYForHeight(height(initialNote));
+ int finalY = staff.getLayoutYForHeight(height( finalNote));
+
+ // if we have a beam and both end-points of it are notes,
+ // place the tupling number over it (that is, make the tupling
+ // line follow the beam and say so); otherwise make the line
+ // follow the gradient a beam would have, but on the other
+ // side of the notes
+ bool followBeam =
+ (beam.necessary &&
+ (*initialNoteOrRest)->event()->isa(Note::EventType) &&
+ (finalNote == finalElement));
+
+ int startY = (followBeam ? beam.startY :
+ initialY - (beam.startY - initialY));
+
+ int endY = startY + (int)((finalX - initialX) *
+ ((double)beam.gradient / 100.0));
+
+ // NOTATION_DEBUG << "applyTuplingLine: beam.startY is " << beam.startY << ", initialY is " << initialY << " so my startY is " << startY << ", endY " << endY << ", beam.gradient " << beam.gradient << endl;
+
+ int nh = staff.getNotePixmapFactory(isGrace).getNoteBodyHeight();
+
+ if (followBeam) { // adjust to move text slightly away from beam
+
+ int maxEndBeamCount = 1;
+ long bc;
+ if ((*initialNoteOrRest)->event()->get<Int>
+ (m_properties.BEAM_NEXT_BEAM_COUNT, bc)) {
+ if (bc > maxEndBeamCount)
+ maxEndBeamCount = bc;
+ }
+ if ((*finalNote)->event()->get<Int>
+ (m_properties.BEAM_NEXT_BEAM_COUNT, bc)) {
+ if (bc > maxEndBeamCount)
+ maxEndBeamCount = bc;
+ }
+
+ int extraBeamSpace = maxEndBeamCount * nh + nh / 2;
+
+ if (beam.aboveNotes) {
+ startY -= extraBeamSpace;
+ endY -= extraBeamSpace;
+ finalX += nh;
+ } else {
+ startY += extraBeamSpace;
+ endY += extraBeamSpace;
+ finalX -= nh;
+ }
+
+ } else { // adjust to place close to note heads
+
+ if (startY < initialY) {
+ if (initialY - startY > nh * 3)
+ startY = initialY - nh * 3;
+ if ( finalY - endY < nh * 2)
+ startY -= nh * 2 - (finalY - endY);
+ } else {
+ if (startY - initialY > nh * 3)
+ startY = initialY + nh * 3;
+ if ( endY - finalY < nh * 2)
+ startY += nh * 2 - (endY - finalY);
+ }
+ }
+
+ Event *e = (*initialNoteOrRest)->event();
+ e->setMaybe<Int>(m_properties.TUPLING_LINE_MY_Y, startY);
+ e->setMaybe<Int>(m_properties.TUPLING_LINE_WIDTH, finalX - initialX);
+ e->setMaybe<Int>(m_properties.TUPLING_LINE_GRADIENT, beam.gradient);
+ e->setMaybe<Bool>(m_properties.TUPLING_LINE_FOLLOWS_BEAM, followBeam);
+ }
+}
+
+}
diff --git a/src/gui/editors/notation/NotationGroup.h b/src/gui/editors/notation/NotationGroup.h
new file mode 100644
index 0000000..c7b1134
--- /dev/null
+++ b/src/gui/editors/notation/NotationGroup.h
@@ -0,0 +1,133 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTATIONGROUP_H_
+#define _RG_NOTATIONGROUP_H_
+
+#include "base/Sets.h"
+#include <utility>
+#include "base/Event.h"
+#include "NotationElement.h"
+
+
+
+
+namespace Rosegarden
+{
+
+class Quantizer;
+class NotationStaff;
+class NotationProperties;
+class Key;
+class Clef;
+
+
+/// Several sorts of "Beamed Group"
+
+class NotationGroup : public AbstractSet<NotationElement,
+ NotationElementList>
+{
+public:
+ typedef NotationElementList::iterator NELIterator;
+
+ enum Type { Beamed, Tupled };
+
+ /// Group contents will be sampled from elements surrounding elementInGroup
+ NotationGroup(NotationElementList &nel, NELIterator elementInGroup,
+ const Quantizer *,
+ std::pair<timeT, timeT> barRange,
+ const NotationProperties &properties,
+ const Clef &clef, const Key &key);
+
+ /// Caller intends to call sample() for each item in the group, _in order_
+ NotationGroup(NotationElementList &nel,
+ const Quantizer *,
+ const NotationProperties &properties,
+ const Clef &clef, const Key &key);
+
+ virtual ~NotationGroup();
+
+ Type getGroupType() const { return m_type; }
+
+ /**
+ * Writes the BEAMED, BEAM_ABOVE, and STEM_UP properties into the
+ * notes in the group, as appropriate. Does not require layout x
+ * coordinates to have been set.
+ */
+ void applyStemProperties();
+
+ /**
+ * Writes beam data into each note in the group. Notes' layout x
+ * coordinates must already have been set. Does not require
+ * applyStemProperties to have already been called.
+ */
+ void applyBeam(NotationStaff &);
+
+ /**
+ * Writes tupling line data into each note in the group. Notes'
+ * layout x coordinates must already have been set. Does nothing
+ * if this is not a tupled group.
+ */
+ void applyTuplingLine(NotationStaff &);
+
+ virtual bool contains(const NELIterator &) const;
+
+ virtual bool sample(const NELIterator &i, bool goingForwards);
+
+protected:
+ virtual bool test(const NELIterator &i);
+
+private:
+ struct Beam
+ { // if a beam has a line equation y = mx + c,
+ int gradient; // -- then this is m*100 (i.e. a percentage)
+ int startY; // -- and this is c
+ bool aboveNotes;
+ bool necessary;
+ };
+
+ Beam calculateBeam(NotationStaff &);
+
+ int height(const NELIterator&) const;
+
+ bool haveInternalRest() const;
+
+ //--------------- Data members ---------------------------------
+
+ std::pair<timeT, timeT> m_barRange;
+ const Clef &m_clef;
+ const Key &m_key;
+ int m_weightAbove, m_weightBelow;
+ bool m_userSamples;
+ long m_groupNo;
+ Type m_type;
+
+ const NotationProperties &m_properties;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationHLayout.cpp b/src/gui/editors/notation/NotationHLayout.cpp
new file mode 100644
index 0000000..1b13765
--- /dev/null
+++ b/src/gui/editors/notation/NotationHLayout.cpp
@@ -0,0 +1,2110 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NotationHLayout.h"
+#include "misc/Debug.h"
+#include <kapplication.h>
+
+#include "base/Composition.h"
+#include "base/LayoutEngine.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/NotationQuantizer.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/SegmentNotationHelper.h"
+#include "base/Staff.h"
+#include "base/ViewElement.h"
+#include "gui/editors/guitar/Chord.h"
+#include "gui/general/ProgressReporter.h"
+#include "gui/widgets/ProgressDialog.h"
+#include "NotationChord.h"
+#include "NotationElement.h"
+#include "NotationGroup.h"
+#include "NotationProperties.h"
+#include "NotationStaff.h"
+#include "NotePixmapFactory.h"
+#include <kconfig.h>
+#include <qobject.h>
+#include <cmath>
+
+namespace Rosegarden
+{
+
+using namespace BaseProperties;
+
+
+NotationHLayout::NotationHLayout(Composition *c, NotePixmapFactory *npf,
+ const NotationProperties &properties,
+ QObject* parent, const char* name) :
+ ProgressReporter(parent, name),
+ HorizontalLayoutEngine(c),
+ m_totalWidth(0.),
+ m_pageMode(false),
+ m_pageWidth(0.),
+ m_spacing(100),
+ m_proportion(60),
+ m_keySigCancelMode(1),
+ m_npf(npf),
+ m_notationQuantizer(c->getNotationQuantizer()),
+ m_properties(properties),
+ m_timePerProgressIncrement(0),
+ m_staffCount(0)
+{
+ // NOTATION_DEBUG << "NotationHLayout::NotationHLayout()" << endl;
+
+ KConfig *config = kapp->config();
+ config->setGroup("Notation Options");
+ m_keySigCancelMode = config->readNumEntry("keysigcancelmode", 1);
+}
+
+NotationHLayout::~NotationHLayout()
+{
+ // empty
+}
+
+std::vector<int>
+NotationHLayout::getAvailableSpacings()
+{
+ if (m_availableSpacings.size() == 0) {
+ m_availableSpacings.push_back(30);
+ m_availableSpacings.push_back(60);
+ m_availableSpacings.push_back(85);
+ m_availableSpacings.push_back(100);
+ m_availableSpacings.push_back(130);
+ m_availableSpacings.push_back(170);
+ m_availableSpacings.push_back(220);
+ }
+ return m_availableSpacings;
+}
+
+std::vector<int>
+NotationHLayout::getAvailableProportions()
+{
+ if (m_availableProportions.size() == 0) {
+ m_availableProportions.push_back(0);
+ m_availableProportions.push_back(20);
+ m_availableProportions.push_back(40);
+ m_availableProportions.push_back(60);
+ m_availableProportions.push_back(80);
+ m_availableProportions.push_back(100);
+ }
+ return m_availableProportions;
+}
+
+NotationHLayout::BarDataList &
+
+NotationHLayout::getBarData(Staff &staff)
+{
+ BarDataMap::iterator i = m_barData.find(&staff);
+ if (i == m_barData.end()) {
+ m_barData[&staff] = BarDataList();
+ }
+
+ return m_barData[&staff];
+}
+
+const NotationHLayout::BarDataList &
+
+NotationHLayout::getBarData(Staff &staff) const
+{
+ return ((NotationHLayout *)this)->getBarData(staff);
+}
+
+NotationElementList::iterator
+NotationHLayout::getStartOfQuantizedSlice(NotationElementList *notes,
+ timeT t)
+const
+{
+ NotationElementList::iterator i = notes->findTime(t);
+ NotationElementList::iterator j(i);
+
+ while (true) {
+ if (i == notes->begin())
+ return i;
+ --j;
+ if ((*j)->getViewAbsoluteTime() < t)
+ return i;
+ i = j;
+ }
+}
+
+NotePixmapFactory *
+NotationHLayout::getNotePixmapFactory(Staff &staff)
+{
+ NotationStaff *ns = dynamic_cast<NotationStaff *>(&staff);
+ if (ns) return &ns->getNotePixmapFactory(false);
+ else return 0;
+}
+
+NotePixmapFactory *
+NotationHLayout::getGraceNotePixmapFactory(Staff &staff)
+{
+ NotationStaff *ns = dynamic_cast<NotationStaff *>(&staff);
+ if (ns) return &ns->getNotePixmapFactory(true);
+ else return 0;
+}
+
+void
+NotationHLayout::scanStaff(Staff &staff, timeT startTime, timeT endTime)
+{
+ throwIfCancelled();
+ Profiler profiler("NotationHLayout::scanStaff");
+
+ Segment &segment(staff.getSegment());
+ bool isFullScan = (startTime == endTime);
+ int startBarOfStaff = getComposition()->getBarNumber(segment.getStartTime());
+
+ if (isFullScan) {
+ clearBarList(staff);
+ startTime = segment.getStartTime();
+ endTime = segment.getEndMarkerTime();
+ } else {
+ startTime = getComposition()->getBarStartForTime(startTime);
+ endTime = getComposition()->getBarEndForTime(endTime);
+ }
+
+ NotationElementList *notes = staff.getViewElementList();
+ BarDataList &barList(getBarData(staff));
+
+ NotePixmapFactory *npf = getNotePixmapFactory(staff);
+
+ int startBarNo = getComposition()->getBarNumber(startTime);
+ int endBarNo = getComposition()->getBarNumber(endTime);
+ /*
+ if (endBarNo > startBarNo &&
+ getComposition()->getBarStart(endBarNo) == segment.getEndMarkerTime()) {
+ --endBarNo;
+ }
+ */
+ std::string name =
+ segment.getComposition()->
+ getTrackById(segment.getTrack())->getLabel();
+ m_staffNameWidths[&staff] =
+ npf->getNoteBodyWidth() * 2 +
+ npf->getTextWidth(Text(name, Text::StaffName));
+
+ NOTATION_DEBUG << "NotationHLayout::scanStaff: full scan " << isFullScan << ", times " << startTime << "->" << endTime << ", bars " << startBarNo << "->" << endBarNo << ", staff name \"" << segment.getLabel() << "\", width " << m_staffNameWidths[&staff] << endl;
+
+ SegmentNotationHelper helper(segment);
+ if (isFullScan) {
+ helper.setNotationProperties();
+ } else {
+ helper.setNotationProperties(startTime, endTime);
+ }
+
+ ::Rosegarden::Key key = segment.getKeyAtTime(startTime);
+ Clef clef = segment.getClefAtTime(startTime);
+ TimeSignature timeSignature =
+ segment.getComposition()->getTimeSignatureAt(startTime);
+ bool barCorrect = true;
+
+ int ottavaShift = 0;
+ timeT ottavaEnd = segment.getEndMarkerTime();
+
+ if (isFullScan) {
+
+ NOTATION_DEBUG << "full scan: setting haveOttava false" << endl;
+
+ m_haveOttavaSomewhere[&staff] = false;
+
+ } else if (m_haveOttavaSomewhere[&staff]) {
+
+ NOTATION_DEBUG << "not full scan but ottava is listed" << endl;
+
+ Segment::iterator i = segment.findTime(startTime);
+ while (1) {
+ if ((*i)->isa(Indication::EventType)) {
+ try {
+ Indication indication(**i);
+ if (indication.isOttavaType()) {
+ ottavaShift = indication.getOttavaShift();
+ ottavaEnd = (*i)->getAbsoluteTime() +
+ indication.getIndicationDuration();
+ break;
+ }
+ } catch (...) { }
+ }
+ if (i == segment.begin())
+ break;
+ --i;
+ }
+ }
+
+ NOTATION_DEBUG << "ottava shift at start:" << ottavaShift << ", ottavaEnd " << ottavaEnd << endl;
+
+ KConfig *config = kapp->config();
+ config->setGroup("Notation Options");
+
+ int accOctaveMode = config->readNumEntry("accidentaloctavemode", 1);
+ AccidentalTable::OctaveType octaveType =
+ (accOctaveMode == 0 ? AccidentalTable::OctavesIndependent :
+ accOctaveMode == 1 ? AccidentalTable::OctavesCautionary :
+ AccidentalTable::OctavesEquivalent);
+
+ int accBarMode = config->readNumEntry("accidentalbarmode", 0);
+ AccidentalTable::BarResetType barResetType =
+ (accBarMode == 0 ? AccidentalTable::BarResetNone :
+ accBarMode == 1 ? AccidentalTable::BarResetCautionary :
+ AccidentalTable::BarResetExplicit);
+
+ bool showInvisibles = config->readBoolEntry("showinvisibles", true);
+
+ if (barResetType != AccidentalTable::BarResetNone) {
+ //!!! very crude and expensive way of making sure we see the
+ // accidentals from previous bar:
+ if (startBarNo > segment.getComposition()->getBarNumber(segment.getStartTime())) {
+ --startBarNo;
+ }
+ }
+
+ AccidentalTable accTable(key, clef, octaveType, barResetType);
+
+ for (int barNo = startBarNo; barNo <= endBarNo; ++barNo) {
+
+ std::pair<timeT, timeT> barTimes =
+ getComposition()->getBarRange(barNo);
+
+ if (barTimes.first >= segment.getEndMarkerTime()) {
+ // clear data if we have any old stuff
+ BarDataList::iterator i(barList.find(barNo));
+ if (i != barList.end()) {
+ barList.erase(i);
+ }
+ continue; // so as to erase any further bars next time around
+ }
+
+ NotationElementList::iterator from =
+ getStartOfQuantizedSlice(notes, barTimes.first);
+
+ NOTATION_DEBUG << "getStartOfQuantizedSlice returned " <<
+ (from != notes->end() ? (*from)->getViewAbsoluteTime() : -1)
+ << " from " << barTimes.first << endl;
+
+ NotationElementList::iterator to =
+ getStartOfQuantizedSlice(notes, barTimes.second);
+
+ if (barTimes.second >= segment.getEndMarkerTime()) {
+ to = notes->end();
+ }
+
+ bool newTimeSig = false;
+ timeSignature = getComposition()->getTimeSignatureInBar
+ (barNo, newTimeSig);
+ NOTATION_DEBUG << "bar " << barNo << ", startBarOfStaff " << startBarOfStaff
+ << ", newTimeSig " << newTimeSig << endl;
+
+ float fixedWidth = 0.0;
+ if (newTimeSig && !timeSignature.isHidden()) {
+ fixedWidth += npf->getNoteBodyWidth() +
+ npf->getTimeSigWidth(timeSignature);
+ }
+
+ setBarBasicData(staff, barNo, from, barCorrect, timeSignature, newTimeSig);
+ BarDataList::iterator bdli(barList.find(barNo));
+ bdli->second.layoutData.needsLayout = true;
+
+ ChunkList &chunks = bdli->second.chunks;
+ chunks.clear();
+
+ float lyricWidth = 0;
+ int graceCount = 0;
+
+ typedef std::set
+ <long> GroupIdSet;
+ GroupIdSet groupIds;
+
+ NOTATION_DEBUG << "NotationHLayout::scanStaff: bar " << barNo << ", from " << barTimes.first << ", to " << barTimes.second << " (end " << segment.getEndMarkerTime() << "); from is at " << (from == notes->end() ? -1 : (*from)->getViewAbsoluteTime()) << ", to is at " << (to == notes->end() ? -1 : (*to)->getViewAbsoluteTime()) << endl;
+
+ timeT actualBarEnd = barTimes.first;
+
+ accTable.newBar();
+
+ for (NotationElementList::iterator itr = from; itr != to; ++itr) {
+
+ NotationElement *el = static_cast<NotationElement*>((*itr));
+ NOTATION_DEBUG << "element is a " << el->event()->getType() << endl;
+
+ if (ottavaShift != 0) {
+ if (el->event()->getAbsoluteTime() >= ottavaEnd) {
+ NOTATION_DEBUG << "reached end of ottava" << endl;
+ ottavaShift = 0;
+ }
+ }
+
+ bool invisible = false;
+ if (el->event()->get<Bool>(INVISIBLE, invisible) && invisible) {
+ if (!showInvisibles)
+ continue;
+ }
+
+ if (el->event()->has(BEAMED_GROUP_ID)) {
+ NOTATION_DEBUG << "element is beamed" << endl;
+ long groupId = el->event()->get<Int>(BEAMED_GROUP_ID);
+ if (groupIds.find(groupId) == groupIds.end()) {
+ NOTATION_DEBUG << "it's a new beamed group, applying stem properties" << endl;
+ NotationGroup group(*staff.getViewElementList(),
+ itr,
+ m_notationQuantizer,
+ barTimes,
+ m_properties,
+ clef, key);
+ group.applyStemProperties();
+ groupIds.insert(groupId);
+ }
+ }
+
+ if (el->event()->isa(Clef::EventType)) {
+
+ // NOTATION_DEBUG << "Found clef" << endl;
+ chunks.push_back(Chunk(el->event()->getSubOrdering(),
+ getLayoutWidth(*el, npf, key)));
+
+ clef = Clef(*el->event());
+ accTable.newClef(clef);
+
+ } else if (el->event()->isa(::Rosegarden::Key::EventType)) {
+
+ // NOTATION_DEBUG << "Found key" << endl;
+ chunks.push_back(Chunk(el->event()->getSubOrdering(),
+ getLayoutWidth(*el, npf, key)));
+
+ key = ::Rosegarden::Key(*el->event());
+
+ accTable = AccidentalTable
+ (key, clef, octaveType, barResetType);
+
+ } else if (el->event()->isa(Text::EventType)) {
+
+ // the only text events of interest are lyrics, which
+ // contribute to a fixed area following the next chord
+
+ if (el->event()->has(Text::TextTypePropertyName) &&
+ el->event()->get<String>(Text::TextTypePropertyName) ==
+ Text::Lyric) {
+ lyricWidth = std::max
+ (lyricWidth, float(npf->getTextWidth(Text(*el->event()))));
+ NOTATION_DEBUG << "Setting lyric width to " << lyricWidth
+ << " for text " << el->event()->get<String>(Text::TextPropertyName) << endl;
+ }
+ chunks.push_back(Chunk(el->event()->getSubOrdering(), 0));
+
+ } else if (el->isNote()) {
+
+ NotePixmapFactory *cnpf = npf;
+ if (el->isGrace()) cnpf = getGraceNotePixmapFactory(staff);
+
+ scanChord(notes, itr, clef, key, accTable,
+ lyricWidth, chunks, cnpf, ottavaShift, to);
+
+ } else if (el->isRest()) {
+
+ chunks.push_back(Chunk(el->getViewDuration(),
+ el->event()->getSubOrdering(),
+ 0,
+ getLayoutWidth(*el, npf, key)));
+
+ } else if (el->event()->isa(Indication::EventType)) {
+
+ // NOTATION_DEBUG << "Found indication" << endl;
+
+ chunks.push_back(Chunk(el->event()->getSubOrdering(), 0));
+
+ try {
+ Indication indication(*el->event());
+ if (indication.isOttavaType()) {
+ ottavaShift = indication.getOttavaShift();
+ ottavaEnd = el->event()->getAbsoluteTime() +
+ indication.getIndicationDuration();
+ m_haveOttavaSomewhere[&staff] = true;
+ }
+ } catch (...) {
+ NOTATION_DEBUG << "Bad indication!" << endl;
+ }
+
+ } else {
+
+// NOTATION_DEBUG << "Found something I don't know about (type is " << el->event()->getType() << ")" << endl;
+ chunks.push_back(Chunk(el->event()->getSubOrdering(),
+ getLayoutWidth(*el, npf, key)));
+ }
+
+ actualBarEnd = el->getViewAbsoluteTime() + el->getViewDuration();
+ }
+
+ if (actualBarEnd == barTimes.first)
+ actualBarEnd = barTimes.second;
+ barCorrect = (actualBarEnd == barTimes.second);
+
+ setBarSizeData(staff, barNo, fixedWidth,
+ actualBarEnd - barTimes.first);
+
+ if ((endTime > startTime) && (barNo % 20 == 0)) {
+ emit setProgress((barTimes.second - startTime) * 95 /
+ (endTime - startTime));
+ ProgressDialog::processEvents();
+ }
+
+ throwIfCancelled();
+ }
+ /*
+ BarDataList::iterator ei(barList.end());
+ while (ei != barList.begin() && (--ei)->first > endBarNo) {
+ barList.erase(ei);
+ ei = barList.end();
+ }
+ */
+}
+
+void
+NotationHLayout::clearBarList(Staff &staff)
+{
+ BarDataList &bdl = m_barData[&staff];
+ bdl.clear();
+}
+
+void
+NotationHLayout::setBarBasicData(Staff &staff,
+ int barNo,
+ NotationElementList::iterator start,
+ bool correct,
+ TimeSignature timeSig,
+ bool newTimeSig)
+{
+ // NOTATION_DEBUG << "setBarBasicData for " << barNo << endl;
+
+ BarDataList &bdl(m_barData[&staff]);
+
+ BarDataList::iterator i(bdl.find(barNo));
+ if (i == bdl.end()) {
+ NotationElementList::iterator endi = staff.getViewElementList()->end();
+ bdl.insert(BarDataPair(barNo, BarData(endi, true,
+ TimeSignature(), false)));
+ i = bdl.find(barNo);
+ }
+
+ i->second.basicData.start = start;
+ i->second.basicData.correct = correct;
+ i->second.basicData.timeSignature = timeSig;
+ i->second.basicData.newTimeSig = newTimeSig;
+}
+
+void
+NotationHLayout::setBarSizeData(Staff &staff,
+ int barNo,
+ float fixedWidth,
+ timeT actualDuration)
+{
+ // NOTATION_DEBUG << "setBarSizeData for " << barNo << endl;
+
+ BarDataList &bdl(m_barData[&staff]);
+
+ BarDataList::iterator i(bdl.find(barNo));
+ if (i == bdl.end()) {
+ NotationElementList::iterator endi = staff.getViewElementList()->end();
+ bdl.insert(BarDataPair(barNo, BarData(endi, true,
+ TimeSignature(), false)));
+ i = bdl.find(barNo);
+ }
+
+ i->second.sizeData.actualDuration = actualDuration;
+ i->second.sizeData.idealWidth = 0.0;
+ i->second.sizeData.reconciledWidth = 0.0;
+ i->second.sizeData.clefKeyWidth = 0;
+ i->second.sizeData.fixedWidth = fixedWidth;
+}
+
+void
+NotationHLayout::scanChord(NotationElementList *notes,
+ NotationElementList::iterator &itr,
+ const Clef &clef,
+ const ::Rosegarden::Key &key,
+ AccidentalTable &accTable,
+ float &lyricWidth,
+ ChunkList &chunks,
+ NotePixmapFactory *npf,
+ int ottavaShift,
+ NotationElementList::iterator &to)
+{
+ NotationChord chord(*notes, itr, m_notationQuantizer, m_properties);
+ Accidental someAccidental = Accidentals::NoAccidental;
+ bool someCautionary = false;
+ bool barEndsInChord = false;
+ bool grace = false;
+
+// std::cerr << "NotationHLayout::scanChord: "
+// << chord.size() << "-voice chord at "
+// << (*itr)->event()->getAbsoluteTime()
+// << " unquantized, "
+// << (*itr)->getViewAbsoluteTime()
+// << " quantized" << std::endl;
+
+// NOTATION_DEBUG << "Contents:" << endl;
+
+ /*
+ for (NotationElementList::iterator i = chord.getInitialElement();
+ i != notes->end(); ++i) {
+ (*i)->event()->dump(std::cerr);
+ if (i == chord.getFinalElement()) break;
+ }
+ */
+ // We don't need to get the chord's notes in pitch order here,
+ // but we do need to ensure we see any random non-note events
+ // that may crop up in the middle of it.
+
+ for (NotationElementList::iterator i = chord.getInitialElement();
+ i != notes->end(); ++i) {
+
+ NotationElement *el = static_cast<NotationElement*>(*i);
+ if (el->isRest()) {
+ el->event()->setMaybe<Bool>(m_properties.REST_TOO_SHORT, true);
+ if (i == chord.getFinalElement())
+ break;
+ continue;
+ }
+
+ if (el->isGrace()) {
+ grace = true;
+ }
+
+ long pitch = 64;
+ if (!el->event()->get<Int>(PITCH, pitch)) {
+ NOTATION_DEBUG <<
+ "WARNING: NotationHLayout::scanChord: couldn't get pitch for element, using default pitch of " << pitch << endl;
+ }
+
+ Accidental explicitAccidental = Accidentals::NoAccidental;
+ (void)el->event()->get<String>(ACCIDENTAL, explicitAccidental);
+
+ Pitch p(pitch, explicitAccidental);
+ int h = p.getHeightOnStaff(clef, key);
+ Accidental acc = p.getDisplayAccidental(key);
+
+ h -= 7 * ottavaShift;
+
+ el->event()->setMaybe<Int>(NotationProperties::OTTAVA_SHIFT, ottavaShift);
+ el->event()->setMaybe<Int>(NotationProperties::HEIGHT_ON_STAFF, h);
+ el->event()->setMaybe<String>(m_properties.CALCULATED_ACCIDENTAL, acc);
+
+ // update display acc for note according to the accTable
+ // (accidentals in force when the last chord ended) and tell
+ // accTable about accidentals from this note.
+
+ bool cautionary = false;
+ if (el->event()->has(m_properties.USE_CAUTIONARY_ACCIDENTAL)) {
+ cautionary = el->event()->get<Bool>(m_properties.USE_CAUTIONARY_ACCIDENTAL);
+ }
+ Accidental dacc = accTable.processDisplayAccidental(acc, h, cautionary);
+ el->event()->setMaybe<String>(m_properties.DISPLAY_ACCIDENTAL, dacc);
+ el->event()->setMaybe<Bool>(m_properties.DISPLAY_ACCIDENTAL_IS_CAUTIONARY,
+ cautionary);
+ if (cautionary) {
+ someCautionary = true;
+ }
+
+ if (someAccidental == Accidentals::NoAccidental)
+ someAccidental = dacc;
+
+ if (i == to)
+ barEndsInChord = true;
+
+ if (i == chord.getFinalElement())
+ break;
+ }
+
+ // tell accTable the chord has ended, so to bring its accidentals
+ // into force for future chords
+ accTable.update();
+
+ chord.applyAccidentalShiftProperties();
+
+ float extraWidth = 0;
+
+ if (someAccidental != Accidentals::NoAccidental) {
+ bool extraShift = false;
+ int shift = chord.getMaxAccidentalShift(extraShift);
+ int e = npf->getAccidentalWidth(someAccidental, shift, extraShift);
+ if (someAccidental != Accidentals::Sharp) {
+ e = std::max(e, npf->getAccidentalWidth(Accidentals::Sharp, shift, extraShift));
+ }
+ if (someCautionary) {
+ e += npf->getNoteBodyWidth();
+ }
+ extraWidth += e;
+ }
+
+ float layoutExtra = 0;
+ if (chord.hasNoteHeadShifted()) {
+ if (chord.hasStemUp()) {
+ layoutExtra += npf->getNoteBodyWidth();
+ } else {
+ extraWidth = std::max(extraWidth, float(npf->getNoteBodyWidth()));
+ }
+ }
+/*!!!
+ if (grace) {
+ std::cerr << "Grace note: subordering " << chord.getSubOrdering() << std::endl;
+ chunks.push_back(Chunk(-10 + graceCount,
+ extraWidth + npf->getNoteBodyWidth()));
+ if (graceCount < 9) ++graceCount;
+ return;
+ } else {
+ std::cerr << "Non-grace note (grace count was " << graceCount << ")" << std::endl;
+ graceCount = 0;
+ }
+*/
+ NotationElementList::iterator myLongest = chord.getLongestElement();
+ if (myLongest == notes->end()) {
+ NOTATION_DEBUG << "WARNING: NotationHLayout::scanChord: No longest element in chord!" << endl;
+ }
+
+ timeT d = (*myLongest)->getViewDuration();
+
+ NOTATION_DEBUG << "Lyric width is " << lyricWidth << endl;
+
+ if (grace) {
+ chunks.push_back(Chunk(d, chord.getSubOrdering(),
+ extraWidth + layoutExtra
+ + getLayoutWidth(**myLongest, npf, key)
+ - npf->getNoteBodyWidth(), // tighten up
+ 0));
+ } else {
+ chunks.push_back(Chunk(d, 0, extraWidth,
+ std::max(layoutExtra +
+ getLayoutWidth(**myLongest, npf, key),
+ lyricWidth)));
+ lyricWidth = 0;
+ }
+
+ itr = chord.getFinalElement();
+ if (barEndsInChord) {
+ to = itr;
+ ++to;
+ }
+}
+
+struct ChunkLocation {
+ timeT time;
+ short subordering;
+ ChunkLocation(timeT t, short s) : time(t), subordering(s) { }
+};
+
+bool operator<(const ChunkLocation &l0, const ChunkLocation &l1) {
+ return ((l0.time < l1.time) ||
+ ((l0.time == l1.time) && (l0.subordering < l1.subordering)));
+}
+
+
+
+void
+NotationHLayout::preSquishBar(int barNo)
+{
+ typedef std::vector<Chunk *> ChunkRefList;
+ typedef std::map<ChunkLocation, ChunkRefList> ColumnMap;
+ static ColumnMap columns;
+ bool haveSomething = false;
+
+ columns.clear();
+
+ for (BarDataMap::iterator mi = m_barData.begin();
+ mi != m_barData.end(); ++mi) {
+
+ BarDataList &bdl = mi->second;
+ BarDataList::iterator bdli = bdl.find(barNo);
+
+ if (bdli != bdl.end()) {
+
+ haveSomething = true;
+ ChunkList &cl(bdli->second.chunks);
+ timeT aggregateTime = 0;
+
+ for (ChunkList::iterator cli = cl.begin(); cli != cl.end(); ++cli) {
+
+ // Subordering is typically zero for notes, positive
+ // for rests and negative for other stuff. We want to
+ // handle notes and rests together, but not the others.
+
+ int subordering = cli->subordering;
+ if (subordering > 0)
+ subordering = 0;
+
+ columns[ChunkLocation(aggregateTime, subordering)].push_back(&(*cli));
+
+ aggregateTime += cli->duration;
+ }
+ }
+ }
+
+ if (!haveSomething)
+ return ;
+
+ // now modify chunks in-place
+
+ // What we want to do here is idle along the whole set of chunk
+ // lists, inspecting all the chunks that occur at each moment in
+ // turn and choosing a "rate" from the "slowest" of these
+ // (i.e. most space per time)
+
+ float x = 0.0;
+ timeT prevTime = 0;
+ double prevRate = 0.0;
+ float maxStretchy = 0.0;
+
+ NOTATION_DEBUG << "NotationHLayout::preSquishBar(" << barNo << "): have "
+ << columns.size() << " columns" << endl;
+
+ for (ColumnMap::iterator i = columns.begin(); i != columns.end(); ++i) {
+
+ timeT time = i->first.time;
+ ChunkRefList &list = i->second;
+
+ NOTATION_DEBUG << "NotationHLayout::preSquishBar: "
+ << "column at " << time << " : " << i->first.subordering << endl;
+
+
+ double minRate = -1.0;
+ float totalFixed = 0.0;
+ maxStretchy = 0.0;
+
+ for (ChunkRefList::iterator j = list.begin(); j != list.end(); ++j) {
+ if ((*j)->stretchy > 0.0) {
+ double rate = (*j)->duration / (*j)->stretchy; // time per px
+ NOTATION_DEBUG << "NotationHLayout::preSquishBar: rate " << rate << endl;
+ if (minRate < 0.0 || rate < minRate)
+ minRate = rate;
+ } else {
+ NOTATION_DEBUG << "NotationHLayout::preSquishBar: not stretchy" << endl;
+ }
+
+ maxStretchy = std::max(maxStretchy, (*j)->stretchy);
+ totalFixed = std::max(totalFixed, (*j)->fixed);
+ }
+
+ NOTATION_DEBUG << "NotationHLayout::preSquishBar: minRate " << minRate << ", maxStretchy " << maxStretchy << ", totalFixed " << totalFixed << endl;
+
+ // we have the rate from this point, but we want to assign
+ // these elements an x coord based on the rate and distance
+ // from the previous point, plus fixed space for this point
+ // if it's a note (otherwise fixed space goes afterwards)
+
+ if (i->first.subordering == 0)
+ x += totalFixed;
+ if (prevRate > 0.0)
+ x += (time - prevTime) / prevRate;
+
+ for (ChunkRefList::iterator j = list.begin(); j != list.end(); ++j) {
+ NOTATION_DEBUG << "Setting x for time " << time << " to " << x << " in chunk at " << *j << endl;
+ (*j)->x = x;
+ }
+
+ if (i->first.subordering != 0)
+ x += totalFixed;
+
+ prevTime = time;
+ prevRate = minRate;
+ }
+
+ x += maxStretchy;
+
+ for (BarDataMap::iterator mi = m_barData.begin();
+ mi != m_barData.end(); ++mi) {
+
+ BarDataList &bdl = mi->second;
+ BarDataList::iterator bdli = bdl.find(barNo);
+ if (bdli != bdl.end()) {
+
+ bdli->second.sizeData.idealWidth =
+ bdli->second.sizeData.fixedWidth + x;
+
+ if (!bdli->second.basicData.timeSignature.hasHiddenBars()) {
+ bdli->second.sizeData.idealWidth += getBarMargin();
+ } else if (bdli->second.basicData.newTimeSig) {
+ bdli->second.sizeData.idealWidth += getPostBarMargin();
+ }
+
+ bdli->second.sizeData.reconciledWidth =
+ bdli->second.sizeData.idealWidth;
+
+ bdli->second.layoutData.needsLayout = true;
+ }
+ }
+}
+
+Staff *
+NotationHLayout::getStaffWithWidestBar(int barNo)
+{
+ float maxWidth = -1;
+ Staff *widest = 0;
+
+ for (BarDataMap::iterator mi = m_barData.begin();
+ mi != m_barData.end(); ++mi) {
+
+ BarDataList &list = mi->second;
+ BarDataList::iterator li = list.find(barNo);
+ if (li != list.end()) {
+
+ NOTATION_DEBUG << "getStaffWithWidestBar: idealWidth is " << li->second.sizeData.idealWidth << endl;
+
+ if (li->second.sizeData.idealWidth == 0.0) {
+ NOTATION_DEBUG << "getStaffWithWidestBar(" << barNo << "): found idealWidth of zero, presquishing" << endl;
+ preSquishBar(barNo);
+ }
+
+ if (li->second.sizeData.idealWidth > maxWidth) {
+ maxWidth = li->second.sizeData.idealWidth;
+ widest = mi->first;
+ }
+ }
+ }
+
+ return widest;
+}
+
+int
+NotationHLayout::getMaxRepeatedClefAndKeyWidth(int barNo)
+{
+ int max = 0;
+
+ timeT barStart = 0;
+
+ for (BarDataMap::iterator mi = m_barData.begin();
+ mi != m_barData.end(); ++mi) {
+
+ Staff *staff = mi->first;
+ if (mi == m_barData.begin()) {
+ barStart = staff->getSegment().getComposition()->getBarStart(barNo);
+ }
+
+ timeT t;
+ int w = 0;
+
+ Clef clef = staff->getSegment().getClefAtTime(barStart, t);
+ if (t < barStart)
+ w += m_npf->getClefWidth(clef);
+
+ ::Rosegarden::Key key = staff->getSegment().getKeyAtTime(barStart, t);
+ if (t < barStart)
+ w += m_npf->getKeyWidth(key);
+
+ if (w > max)
+ max = w;
+ }
+
+ NOTATION_DEBUG << "getMaxRepeatedClefAndKeyWidth(" << barNo << "): " << max
+ << endl;
+
+ if (max > 0)
+ return max + getFixedItemSpacing() * 2;
+ else
+ return 0;
+}
+
+void
+NotationHLayout::reconcileBarsLinear()
+{
+ Profiler profiler("NotationHLayout::reconcileBarsLinear");
+
+ // Ensure that concurrent bars on all staffs have the same width,
+ // which for now we make the maximum width required for this bar
+ // on any staff. These days getStaffWithWidestBar actually does
+ // most of the work in its call to preSquishBar, but this function
+ // still sets the bar line positions etc.
+
+ int barNo = getFirstVisibleBar();
+
+ m_totalWidth = 0.0;
+ for (StaffIntMap::iterator i = m_staffNameWidths.begin();
+ i != m_staffNameWidths.end(); ++i) {
+ if (i->second > m_totalWidth)
+ m_totalWidth = double(i->second);
+ }
+
+ for (;;) {
+
+ Staff *widest = getStaffWithWidestBar(barNo);
+
+ if (!widest) {
+ // have we reached the end of the piece?
+ if (barNo >= getLastVisibleBar()) { // yes
+ break;
+ } else {
+ m_totalWidth += m_spacing / 3;
+ NOTATION_DEBUG << "Setting bar position for degenerate bar "
+ << barNo << " to " << m_totalWidth << endl;
+
+ m_barPositions[barNo] = m_totalWidth;
+ ++barNo;
+ continue;
+ }
+ }
+
+ float maxWidth = m_barData[widest].find(barNo)->second.sizeData.idealWidth;
+ if (m_pageWidth > 0.1 && maxWidth > m_pageWidth) {
+ maxWidth = m_pageWidth;
+ }
+
+ NOTATION_DEBUG << "Setting bar position for bar " << barNo
+ << " to " << m_totalWidth << endl;
+
+ m_barPositions[barNo] = m_totalWidth;
+ m_totalWidth += maxWidth;
+
+ // Now apply width to this bar on all staffs
+
+ for (BarDataMap::iterator i = m_barData.begin();
+ i != m_barData.end(); ++i) {
+
+ BarDataList &list = i->second;
+ BarDataList::iterator bdli = list.find(barNo);
+ if (bdli != list.end()) {
+
+ BarData::SizeData &bd(bdli->second.sizeData);
+
+ NOTATION_DEBUG << "Changing width from " << bd.reconciledWidth << " to " << maxWidth << endl;
+
+ double diff = maxWidth - bd.reconciledWidth;
+ if (diff < -0.1 || diff > 0.1) {
+ NOTATION_DEBUG << "(So needsLayout becomes true)" << endl;
+ bdli->second.layoutData.needsLayout = true;
+ }
+ bd.reconciledWidth = maxWidth;
+ }
+ }
+
+ ++barNo;
+ }
+
+ NOTATION_DEBUG << "Setting bar position for bar " << barNo
+ << " to " << m_totalWidth << endl;
+
+ m_barPositions[barNo] = m_totalWidth;
+}
+
+void
+NotationHLayout::reconcileBarsPage()
+{
+ Profiler profiler("NotationHLayout::reconcileBarsPage");
+
+ int barNo = getFirstVisibleBar();
+ int barNoThisRow = 0;
+
+ // pair of the recommended number of bars with those bars'
+ // original total width, for each row
+ std::vector<std::pair<int, double> > rowData;
+
+ double stretchFactor = 10.0;
+ double maxStaffNameWidth = 0.0;
+
+ for (StaffIntMap::iterator i = m_staffNameWidths.begin();
+ i != m_staffNameWidths.end(); ++i) {
+ if (i->second > maxStaffNameWidth) {
+ maxStaffNameWidth = double(i->second);
+ }
+ }
+
+ double pageWidthSoFar = maxStaffNameWidth;
+ m_totalWidth = maxStaffNameWidth + getPreBarMargin();
+
+ NOTATION_DEBUG << "NotationHLayout::reconcileBarsPage: pageWidthSoFar is " << pageWidthSoFar << endl;
+
+ for (;;) {
+
+ Staff *widest = getStaffWithWidestBar(barNo);
+ double maxWidth = m_spacing / 3;
+
+ if (!widest) {
+ // have we reached the end of the piece?
+ if (barNo >= getLastVisibleBar())
+ break; // yes
+ } else {
+ maxWidth =
+ m_barData[widest].find(barNo)->second.sizeData.idealWidth;
+ }
+
+ // Work on the assumption that this bar is the last in the
+ // row. How would that make things look?
+
+ double nextPageWidth = pageWidthSoFar + maxWidth;
+ double nextStretchFactor = m_pageWidth / nextPageWidth;
+
+ NOTATION_DEBUG << "barNo is " << barNo << ", maxWidth " << maxWidth << ", nextPageWidth " << nextPageWidth << ", nextStretchFactor " << nextStretchFactor << ", m_pageWidth " << m_pageWidth << endl;
+
+ // We have to have at least one bar per row
+
+ bool tooFar = false;
+
+ if (barNoThisRow >= 1) {
+
+ // If this stretch factor is "worse" than the previous
+ // one, we've come too far and have too many bars
+
+ if (fabs(1.0 - nextStretchFactor) > fabs(1.0 - stretchFactor)) {
+ tooFar = true;
+ }
+
+ // If the next stretch factor is less than 1 and would
+ // make this bar on any of the staffs narrower than it can
+ // afford to be, then we've got too many bars
+ //!!! rework this -- we have no concept of "too narrow"
+ // any more but we can declare we don't want it any
+ // narrower than e.g. 90% or something based on the spacing
+ /*!!!
+ if (!tooFar && (nextStretchFactor < 1.0)) {
+
+ for (BarDataMap::iterator i = m_barData.begin();
+ i != m_barData.end(); ++i) {
+
+ BarDataList &list = i->second;
+ BarDataList::iterator bdli = list.find(barNo);
+ if (bdli != list.end()) {
+ BarData::SizeData &bd(bdli->second.sizeData);
+ if ((nextStretchFactor * bd.idealWidth) <
+ (double)(bd.fixedWidth + bd.baseWidth)) {
+ tooFar = true;
+ break;
+ }
+ }
+ }
+ }
+ */
+ }
+
+ if (tooFar) {
+ rowData.push_back(std::pair<int, double>(barNoThisRow,
+ pageWidthSoFar));
+ barNoThisRow = 1;
+
+ // When we start a new row, we always need to allow for the
+ // repeated clef and key at the start of it.
+ int maxClefKeyWidth = getMaxRepeatedClefAndKeyWidth(barNo);
+
+ for (BarDataMap::iterator i = m_barData.begin();
+ i != m_barData.end(); ++i) {
+
+ BarDataList &list = i->second;
+ BarDataList::iterator bdli = list.find(barNo);
+
+ if (bdli != list.end()) {
+ bdli->second.sizeData.clefKeyWidth = maxClefKeyWidth;
+ }
+ }
+
+ pageWidthSoFar = maxWidth + maxClefKeyWidth;
+ stretchFactor = m_pageWidth / pageWidthSoFar;
+ } else {
+ ++barNoThisRow;
+ pageWidthSoFar = nextPageWidth;
+ stretchFactor = nextStretchFactor;
+ }
+
+ ++barNo;
+ }
+
+ if (barNoThisRow > 0) {
+ rowData.push_back(std::pair<int, double>(barNoThisRow,
+ pageWidthSoFar));
+ }
+
+ // Now we need to actually apply the widths
+
+ barNo = getFirstVisibleBar();
+
+ for (unsigned int row = 0; row < rowData.size(); ++row) {
+
+ barNoThisRow = barNo;
+ int finalBarThisRow = barNo + rowData[row].first - 1;
+
+ pageWidthSoFar = (row > 0 ? 0 : maxStaffNameWidth + getPreBarMargin());
+ stretchFactor = m_pageWidth / rowData[row].second;
+
+ for (; barNoThisRow <= finalBarThisRow; ++barNoThisRow, ++barNo) {
+
+ bool finalRow = (row == rowData.size() - 1);
+
+ Staff *widest = getStaffWithWidestBar(barNo);
+ if (finalRow && (stretchFactor > 1.0))
+ stretchFactor = 1.0;
+ double maxWidth = 0.0;
+
+ if (!widest) {
+ // have we reached the end of the piece? (shouldn't happen)
+ if (barNo >= getLastVisibleBar())
+ break; // yes
+ else
+ maxWidth = stretchFactor * (m_spacing / 3);
+ } else {
+ BarData &bd = m_barData[widest].find(barNo)->second;
+ maxWidth = (stretchFactor * bd.sizeData.idealWidth) +
+ bd.sizeData.clefKeyWidth;
+ NOTATION_DEBUG << "setting maxWidth to " << (stretchFactor * bd.sizeData.idealWidth) << " + " << bd.sizeData.clefKeyWidth << " = " << maxWidth << endl;
+ }
+
+ if (barNoThisRow == finalBarThisRow) {
+ if (!finalRow ||
+ (maxWidth > (m_pageWidth - pageWidthSoFar))) {
+ maxWidth = m_pageWidth - pageWidthSoFar;
+ NOTATION_DEBUG << "reset maxWidth to " << m_pageWidth << " - " << pageWidthSoFar << " = " << maxWidth << endl;
+ }
+ }
+
+ m_barPositions[barNo] = m_totalWidth;
+ m_totalWidth += maxWidth;
+
+ for (BarDataMap::iterator i = m_barData.begin();
+ i != m_barData.end(); ++i) {
+
+ BarDataList &list = i->second;
+ BarDataList::iterator bdli = list.find(barNo);
+ if (bdli != list.end()) {
+ BarData::SizeData &bd(bdli->second.sizeData);
+ double diff = maxWidth - bd.reconciledWidth;
+ if (diff < -0.1 || diff > 0.1) {
+ bdli->second.layoutData.needsLayout = true;
+ }
+ bd.reconciledWidth = maxWidth;
+ }
+ }
+
+ pageWidthSoFar += maxWidth;
+ }
+ }
+
+ m_barPositions[barNo] = m_totalWidth;
+}
+
+void
+NotationHLayout::finishLayout(timeT startTime, timeT endTime)
+{
+ Profiler profiler("NotationHLayout::finishLayout");
+ m_barPositions.clear();
+
+ bool isFullLayout = (startTime == endTime);
+ if (m_pageMode && (m_pageWidth > 0.1))
+ reconcileBarsPage();
+ else
+ reconcileBarsLinear();
+
+ int staffNo = 0;
+
+ for (BarDataMap::iterator i(m_barData.begin());
+ i != m_barData.end(); ++i) {
+
+ emit setProgress(100 * staffNo / m_barData.size());
+ ProgressDialog::processEvents();
+
+ throwIfCancelled();
+
+ timeT timeCovered = endTime - startTime;
+
+ if (isFullLayout) {
+ NotationElementList *notes = i->first->getViewElementList();
+ if (notes->begin() != notes->end()) {
+ NotationElementList::iterator j(notes->end());
+ timeCovered =
+ (*--j)->getViewAbsoluteTime() -
+ (*notes->begin())->getViewAbsoluteTime();
+ }
+ }
+
+ m_timePerProgressIncrement = timeCovered / (100 / m_barData.size());
+
+ layout(i, startTime, endTime);
+ ++staffNo;
+ }
+}
+
+void
+NotationHLayout::layout(BarDataMap::iterator i, timeT startTime, timeT endTime)
+{
+ Profiler profiler("NotationHLayout::layout");
+
+ Staff &staff = *(i->first);
+ NotationElementList *notes = staff.getViewElementList();
+ BarDataList &barList(getBarData(staff));
+ NotationStaff &notationStaff = dynamic_cast<NotationStaff &>(staff);
+
+ bool isFullLayout = (startTime == endTime);
+
+ // these two are for partial layouts:
+ // bool haveSimpleOffset = false;
+ // double simpleOffset = 0;
+
+ NOTATION_DEBUG << "NotationHLayout::layout: full layout " << isFullLayout << ", times " << startTime << "->" << endTime << endl;
+
+ double x = 0, barX = 0;
+ TieMap tieMap;
+
+ timeT lastIncrement =
+ (isFullLayout && (notes->begin() != notes->end())) ?
+ (*notes->begin())->getViewAbsoluteTime() : startTime;
+
+ ::Rosegarden::Key key = notationStaff.getSegment().getKeyAtTime(lastIncrement);
+ Clef clef = notationStaff.getSegment().getClefAtTime(lastIncrement);
+ TimeSignature timeSignature;
+
+ int startBar = getComposition()->getBarNumber(startTime);
+
+ KConfig *config = kapp->config();
+ config->setGroup("Notation Options");
+ bool showInvisibles = config->readBoolEntry("showinvisibles", true);
+
+ for (BarPositionList::iterator bpi = m_barPositions.begin();
+ bpi != m_barPositions.end(); ++bpi) {
+
+ int barNo = bpi->first;
+ if (!isFullLayout && barNo < startBar)
+ continue;
+
+ NOTATION_DEBUG << "NotationHLayout::looking for bar "
+ << bpi->first << endl;
+ BarDataList::iterator bdi = barList.find(barNo);
+ if (bdi == barList.end())
+ continue;
+ barX = bpi->second;
+
+ NotationElementList::iterator from = bdi->second.basicData.start;
+ NotationElementList::iterator to;
+
+ NOTATION_DEBUG << "NotationHLayout::layout(): starting bar " << barNo << ", x = " << barX << ", width = " << bdi->second.sizeData.idealWidth << ", time = " << (from == notes->end() ? -1 : (*from)->getViewAbsoluteTime()) << endl;
+
+ BarDataList::iterator nbdi(bdi);
+ if (++nbdi == barList.end()) {
+ to = notes->end();
+ } else {
+ to = nbdi->second.basicData.start;
+ }
+
+ if (from == notes->end()) {
+ NOTATION_DEBUG << "Start is end" << endl;
+ }
+ if (from == to) {
+ NOTATION_DEBUG << "Start is to" << endl;
+ }
+
+ if (!bdi->second.layoutData.needsLayout) {
+
+ double offset = barX - bdi->second.layoutData.x;
+
+ NOTATION_DEBUG << "NotationHLayout::layout(): bar " << barNo << " has needsLayout false and offset of " << offset << endl;
+
+ if (offset > -0.1 && offset < 0.1) {
+ NOTATION_DEBUG << "NotationHLayout::layout(): no offset, ignoring" << endl;
+ continue;
+ }
+
+ bdi->second.layoutData.x += offset;
+
+ if (bdi->second.basicData.newTimeSig)
+ bdi->second.layoutData.timeSigX += (int)offset;
+
+ for (NotationElementList::iterator it = from;
+ it != to && it != notes->end(); ++it) {
+
+ NotationElement* nel = static_cast<NotationElement*>(*it);
+ NOTATION_DEBUG << "NotationHLayout::layout(): shifting element's x to " << ((*it)->getLayoutX() + offset) << " (was " << (*it)->getLayoutX() << ")" << endl;
+ nel->setLayoutX((*it)->getLayoutX() + offset);
+ double airX, airWidth;
+ nel->getLayoutAirspace(airX, airWidth);
+ nel->setLayoutAirspace(airX + offset, airWidth);
+ }
+
+ continue;
+ }
+
+ bdi->second.layoutData.x = barX;
+ // x = barX + getPostBarMargin();
+
+ bool timeSigToPlace = false;
+ if (bdi->second.basicData.newTimeSig) {
+ timeSignature = bdi->second.basicData.timeSignature;
+ timeSigToPlace = !bdi->second.basicData.timeSignature.isHidden();
+ }
+ if (timeSigToPlace) {
+ NOTATION_DEBUG << "NotationHLayout::layout(): there's a time sig in this bar" << endl;
+ }
+
+ bool repeatClefAndKey = false;
+ if (bdi->second.sizeData.clefKeyWidth > 0) {
+ repeatClefAndKey = true;
+ }
+ if (repeatClefAndKey) {
+ NOTATION_DEBUG << "NotationHLayout::layout(): need to repeat clef & key in this bar" << endl;
+ }
+
+ double barInset = notationStaff.getBarInset(barNo, repeatClefAndKey);
+
+ NotationElement *lastDynamicText = 0;
+ int fretboardCount = 0;
+ int count = 0;
+
+ double offset = 0.0;
+ double reconciledWidth = bdi->second.sizeData.reconciledWidth;
+
+ if (repeatClefAndKey) {
+ offset = bdi->second.sizeData.clefKeyWidth;
+ reconciledWidth -= offset;
+ }
+
+ if (bdi->second.basicData.newTimeSig ||
+ !bdi->second.basicData.timeSignature.hasHiddenBars()) {
+ offset += getPostBarMargin();
+ }
+
+ ChunkList &chunks = bdi->second.chunks;
+ ChunkList::iterator chunkitr = chunks.begin();
+ double reconcileRatio = 1.0;
+ if (bdi->second.sizeData.idealWidth > 0.0) {
+ reconcileRatio = reconciledWidth / bdi->second.sizeData.idealWidth;
+ }
+
+ NOTATION_DEBUG << "have " << chunks.size() << " chunks, reconciledWidth " << bdi->second.sizeData.reconciledWidth << ", idealWidth " << bdi->second.sizeData.idealWidth << ", ratio " << reconcileRatio << endl;
+
+ double delta = 0;
+ float sigx = 0.f;
+
+ for (NotationElementList::iterator it = from; it != to; ++it) {
+
+ NotationElement *el = static_cast<NotationElement*>(*it);
+ delta = 0;
+ float fixed = 0;
+
+ if (el->event()->isa(Note::EventType)) {
+ long pitch = 0;
+ el->event()->get<Int>(PITCH, pitch);
+ NOTATION_DEBUG << "element is a " << el->event()->getType() << " (pitch " << pitch << ")" << endl;
+ } else {
+ NOTATION_DEBUG << "element is a " << el->event()->getType() << endl;
+ }
+
+ bool invisible = false;
+ if (el->event()->get<Bool>(INVISIBLE, invisible) && invisible) {
+ if (!showInvisibles)
+ continue;
+ }
+
+// float sigx = 0;
+
+ if (chunkitr != chunks.end()) {
+ NOTATION_DEBUG << "new chunk: addr " << &(*chunkitr) << " duration=" << (*chunkitr).duration << " subordering=" << (*chunkitr).subordering << " fixed=" << (*chunkitr).fixed << " stretchy=" << (*chunkitr).stretchy << " x=" << (*chunkitr).x << endl;
+ x = barX + offset + reconcileRatio * (*chunkitr).x;
+ fixed = (*chunkitr).fixed;
+// sigx = barX + offset - fixed;
+// sigx = x - fixed;
+ NOTATION_DEBUG << "adjusted x is " << x << ", fixed is " << fixed << endl;
+
+ if (timeSigToPlace) {
+ if (el->event()->isa(Clef::EventType) ||
+ el->event()->isa(Rosegarden::Key::EventType)) {
+ sigx = x + (*chunkitr).fixed + (*chunkitr).stretchy;
+ }
+ }
+
+ ChunkList::iterator chunkscooter(chunkitr);
+ if (++chunkscooter != chunks.end()) {
+ delta = (*chunkscooter).x - (*chunkitr).x;
+ } else {
+ delta = reconciledWidth -
+ bdi->second.sizeData.fixedWidth - (*chunkitr).x;
+ }
+ delta *= reconcileRatio;
+
+ ++chunkitr;
+ } else {
+ x = barX + reconciledWidth - getPreBarMargin();
+// sigx = x;
+ delta = 0;
+ }
+
+ if (timeSigToPlace &&
+ !el->event()->isa(Clef::EventType) &&
+ !el->event()->isa(::Rosegarden::Key::EventType)) {
+
+ if (sigx == 0.f) {
+ sigx = barX + offset;
+ }
+
+// NOTATION_DEBUG << "Placing timesig at " << (x - fixed) << endl;
+// bdi->second.layoutData.timeSigX = (int)(x - fixed);
+ NOTATION_DEBUG << "Placing timesig at " << sigx << " (would previously have been " << int(x-fixed) << "?)" << endl;
+ bdi->second.layoutData.timeSigX = (int)sigx;
+ double shift = getFixedItemSpacing() +
+ m_npf->getTimeSigWidth(timeSignature);
+ offset += shift;
+ x += shift;
+ NOTATION_DEBUG << "and moving next elt to " << x << endl;
+ timeSigToPlace = false;
+ }
+
+ if (barInset >= 1.0) {
+ if (el->event()->isa(Clef::EventType) ||
+ el->event()->isa(::Rosegarden::Key::EventType)) {
+ NOTATION_DEBUG << "Pulling clef/key back by " << getPreBarMargin() << endl;
+ x -= getPostBarMargin() * 2 / 3;
+ } else {
+ barInset = 0.0;
+ }
+ }
+
+ NOTATION_DEBUG << "NotationHLayout::layout(): setting element's x to " << x << " (was " << el->getLayoutX() << ")" << endl;
+
+ double displacedX = 0.0;
+ long dxRaw = 0;
+ el->event()->get<Int>(DISPLACED_X, dxRaw);
+ displacedX = double(dxRaw * m_npf->getNoteBodyWidth()) / 1000.0;
+
+ el->setLayoutX(x + displacedX);
+ el->setLayoutAirspace(x, int(delta));
+
+ // #704958 (multiple tuplet spanners created when entering
+ // triplet chord) -- only do this here for non-notes,
+ // notes get it from positionChord
+ if (!el->isNote()) {
+ sampleGroupElement(staff, clef, key, it);
+ }
+
+ if (el->isNote()) {
+
+ // This modifies "it" and "tieMap"
+ positionChord(staff, it, clef, key, tieMap, to);
+
+ } else if (el->isRest()) {
+
+ // nothing to do
+
+ } else if (el->event()->isa(Clef::EventType)) {
+
+ clef = Clef(*el->event());
+
+ } else if (el->event()->isa(::Rosegarden::Key::EventType)) {
+
+ key = ::Rosegarden::Key(*el->event());
+
+ } else if (el->event()->isa(Text::EventType)) {
+
+ // if it's a dynamic, make a note of it in case a
+ // hairpin immediately follows it
+
+ if (el->event()->has(Text::TextTypePropertyName) &&
+ el->event()->get<String>(Text::TextTypePropertyName) ==
+ Text::Dynamic) {
+ lastDynamicText = el;
+ }
+
+ } else if (el->event()->isa(Indication::EventType)) {
+
+ std::string type;
+ double ix = x;
+
+ // Check for a dynamic text at the same time as the
+ // indication and if found, move the indication to the
+ // right to make room. We know the text should have
+ // preceded the indication in the staff because it has
+ // a smaller subordering
+
+ if (el->event()->get<String>
+ (Indication::IndicationTypePropertyName, type) &&
+ (type == Indication::Crescendo ||
+ type == Indication::Decrescendo) &&
+ lastDynamicText &&
+ lastDynamicText->getViewAbsoluteTime() ==
+ el->getViewAbsoluteTime()) {
+
+ ix = x + m_npf->getTextWidth
+ (Text(*lastDynamicText->event())) +
+ m_npf->getNoteBodyWidth() / 4;
+ }
+
+ el->setLayoutX(ix + displacedX);
+ el->setLayoutAirspace(ix, delta - (ix - x));
+
+ } else if (el->event()->isa(Guitar::Chord::EventType)) {
+
+ int guitarChordWidth = m_npf->getLineSpacing() * 6;
+ el->setLayoutX(x - (guitarChordWidth / 2)
+ + fretboardCount * (guitarChordWidth +
+ m_npf->getNoteBodyWidth()/2)
+ + displacedX);
+ ++fretboardCount;
+
+ } else {
+
+ // nothing else
+ }
+
+ if (m_timePerProgressIncrement > 0 && (++count == 100)) {
+ count = 0;
+ timeT sinceIncrement = el->getViewAbsoluteTime() - lastIncrement;
+ if (sinceIncrement > m_timePerProgressIncrement) {
+ emit incrementProgress
+ (sinceIncrement / m_timePerProgressIncrement);
+ lastIncrement +=
+ (sinceIncrement / m_timePerProgressIncrement)
+ * m_timePerProgressIncrement;
+ throwIfCancelled();
+ }
+ }
+ }
+
+ if (timeSigToPlace) {
+ // no other events in this bar, so we never managed to place it
+ x = barX + offset;
+ NOTATION_DEBUG << "Placing timesig reluctantly at " << x << endl;
+ bdi->second.layoutData.timeSigX = (int)(x);
+ timeSigToPlace = false;
+ }
+
+ for (NotationGroupMap::iterator mi = m_groupsExtant.begin();
+ mi != m_groupsExtant.end(); ++mi) {
+ mi->second->applyBeam(notationStaff);
+ mi->second->applyTuplingLine(notationStaff);
+ delete mi->second;
+ }
+ m_groupsExtant.clear();
+
+ bdi->second.layoutData.needsLayout = false;
+ }
+}
+
+void
+NotationHLayout::sampleGroupElement(Staff &staff,
+ const Clef &clef,
+ const ::Rosegarden::Key &key,
+ const NotationElementList::iterator &itr)
+{
+ NotationElement *el = static_cast<NotationElement *>(*itr);
+
+ if (el->event()->has(BEAMED_GROUP_ID)) {
+
+ //!!! Gosh. We need some clever logic to establish whether
+ // one group is happening while another has not yet ended --
+ // perhaps we decide one has ended if we see another, and then
+ // re-open the case of the first if we meet another note that
+ // claims to be in it. Then we need to hint to both of the
+ // groups that they should choose appropriate stem directions
+ // -- we could just use HEIGHT_ON_STAFF of their first notes
+ // to determine this, as if that doesn't work, nothing will
+
+ long groupId = el->event()->get<Int>(BEAMED_GROUP_ID);
+ NOTATION_DEBUG << "group id: " << groupId << endl;
+ if (m_groupsExtant.find(groupId) == m_groupsExtant.end()) {
+ NOTATION_DEBUG << "(new group)" << endl;
+ m_groupsExtant[groupId] =
+ new NotationGroup(*staff.getViewElementList(),
+ m_notationQuantizer,
+ m_properties, clef, key);
+ }
+ m_groupsExtant[groupId]->sample(itr, true);
+ }
+}
+
+timeT
+NotationHLayout::getSpacingDuration(Staff &staff,
+ const NotationElementList::iterator &i)
+{
+ SegmentNotationHelper helper(staff.getSegment());
+ timeT t((*i)->getViewAbsoluteTime());
+ timeT d((*i)->getViewDuration());
+
+ if (d > 0 && (*i)->event()->getDuration() == 0) return d; // grace note
+
+ NotationElementList::iterator j(i), e(staff.getViewElementList()->end());
+ while (j != e && ((*j)->getViewAbsoluteTime() == t ||
+ (*j)->getViewDuration() == 0)) {
+ ++j;
+ }
+ if (j == e) {
+ return d;
+ } else {
+ return (*j)->getViewAbsoluteTime() - (*i)->getViewAbsoluteTime();
+ }
+}
+
+timeT
+NotationHLayout::getSpacingDuration(Staff &staff,
+ const NotationChord &chord)
+{
+ SegmentNotationHelper helper(staff.getSegment());
+
+ NotationElementList::iterator i = chord.getShortestElement();
+ timeT d((*i)->getViewDuration());
+
+ if (d > 0 && (*i)->event()->getDuration() == 0) return d; // grace note
+
+ NotationElementList::iterator j(i), e(staff.getViewElementList()->end());
+ while (j != e && (chord.contains(j) || (*j)->getViewDuration() == 0))
+ ++j;
+
+ if (j != e) {
+ d = (*j)->getViewAbsoluteTime() - (*i)->getViewAbsoluteTime();
+ }
+
+ return d;
+}
+
+void
+NotationHLayout::positionChord(Staff &staff,
+ NotationElementList::iterator &itr,
+ const Clef &clef, const ::Rosegarden::Key &key,
+ TieMap &tieMap,
+ NotationElementList::iterator &to)
+{
+ NotationChord chord(*staff.getViewElementList(), itr, m_notationQuantizer,
+ m_properties, clef, key);
+ double baseX, delta;
+ (static_cast<NotationElement *>(*itr))->getLayoutAirspace(baseX, delta);
+
+ bool barEndsInChord = false;
+
+ NOTATION_DEBUG << "NotationHLayout::positionChord: x = " << baseX << endl;
+
+ // #938545 (Broken notation: Duplicated note can float outside
+ // stave) -- We need to iterate over all elements in the chord
+ // range here, not just the ordered set of notes actually in the
+ // chord. They all have the same x-coord, so there's no
+ // particular complication here.
+
+ for (NotationElementList::iterator citr = chord.getInitialElement();
+ citr != staff.getViewElementList()->end(); ++citr) {
+
+ if (citr == to)
+ barEndsInChord = true;
+
+ // #704958 (multiple tuplet spanners created when entering
+ // triplet chord) -- layout() updates the beamed group data
+ // for non-notes, but we have to do it for notes so as to
+ // ensure every note in the chord is accounted for
+ sampleGroupElement(staff, clef, key, citr);
+
+ NotationElement *elt = static_cast<NotationElement*>(*citr);
+
+ double displacedX = 0.0;
+ long dxRaw = 0;
+ elt->event()->get<Int>(DISPLACED_X, dxRaw);
+ displacedX = double(dxRaw * m_npf->getNoteBodyWidth()) / 1000.0;
+
+ elt->setLayoutX(baseX + displacedX);
+ elt->setLayoutAirspace(baseX, delta);
+
+ NOTATION_DEBUG << "NotationHLayout::positionChord: assigned x to elt at " << elt->getViewAbsoluteTime() << endl;
+
+ if (citr == chord.getFinalElement())
+ break;
+ }
+
+ // Check for any ties going back, and if so work out how long they
+ // must have been and assign accordingly.
+
+ for (NotationElementList::iterator citr = chord.getInitialElement();
+ citr != staff.getViewElementList()->end(); ++citr) {
+
+ NotationElement *note = static_cast<NotationElement*>(*citr);
+ if (!note->isNote()) {
+ if (citr == chord.getFinalElement())
+ break;
+ continue;
+ }
+
+ bool tiedForwards = false;
+ bool tiedBack = false;
+
+ note->event()->get<Bool>(TIED_FORWARD, tiedForwards);
+ note->event()->get<Bool>(TIED_BACKWARD, tiedBack);
+
+ if (!note->event()->has(PITCH))
+ continue;
+ int pitch = note->event()->get<Int>(PITCH);
+
+ if (tiedBack) {
+ TieMap::iterator ti(tieMap.find(pitch));
+
+ if (ti != tieMap.end()) {
+ NotationElementList::iterator otherItr(ti->second);
+
+ if ((*otherItr)->getViewAbsoluteTime() +
+ (*otherItr)->getViewDuration() ==
+ note->getViewAbsoluteTime()) {
+
+ NOTATION_DEBUG << "Second note in tie at " << note->getViewAbsoluteTime() << ": found first note, it matches" << endl;
+
+ (*otherItr)->event()->setMaybe<Int>
+ (m_properties.TIE_LENGTH,
+ (int)(baseX - (*otherItr)->getLayoutX()));
+
+ } else {
+ NOTATION_DEBUG << "Second note in tie at " << note->getViewAbsoluteTime() << ": found first note but it ends at " << ((*otherItr)->getViewAbsoluteTime() + (*otherItr)->getViewDuration()) << endl;
+
+ tieMap.erase(pitch);
+ }
+ }
+ }
+
+ if (tiedForwards) {
+ note->event()->setMaybe<Int>(m_properties.TIE_LENGTH, 0);
+ tieMap[pitch] = citr;
+ } else {
+ note->event()->unset(m_properties.TIE_LENGTH);
+ }
+
+ if (citr == chord.getFinalElement())
+ break;
+ }
+
+ itr = chord.getFinalElement();
+ if (barEndsInChord) {
+ to = itr;
+ ++to;
+ }
+}
+
+float
+NotationHLayout::getLayoutWidth(ViewElement &ve,
+ NotePixmapFactory *npf,
+ const ::Rosegarden::Key &previousKey) const
+{
+ NotationElement& e = static_cast<NotationElement&>(ve);
+
+ if ((e.isNote() || e.isRest()) && e.event()->has(NOTE_TYPE)) {
+
+ long noteType = e.event()->get<Int>(NOTE_TYPE);
+ long dots = 0;
+ (void)e.event()->get<Int>(NOTE_DOTS, dots);
+
+ double bw = 0;
+
+ if (e.isNote()) {
+ bw = m_npf->getNoteBodyWidth(noteType)
+ + m_npf->getDotWidth() * dots;
+ } else {
+ bw = m_npf->getRestWidth(Note(noteType, dots));
+ }
+
+ double multiplier = double(Note(noteType, dots).getDuration()) /
+ double(Note(Note::Quaver).getDuration());
+ multiplier -= 1.0;
+ multiplier *= m_proportion / 100.0;
+ multiplier += 1.0;
+
+ double gap = m_npf->getNoteBodyWidth(noteType) * multiplier;
+
+ NOTATION_DEBUG << "note type " << noteType << ", isNote " << e.isNote() << ", dots " << dots << ", multiplier " << multiplier << ", gap " << gap << ", result " << (bw + gap * m_spacing / 100.0) << endl;
+
+ gap = gap * m_spacing / 100.0;
+ return bw + gap;
+
+ } else {
+
+ double w = getFixedItemSpacing();
+
+ if (e.event()->isa(Clef::EventType)) {
+
+ w += m_npf->getClefWidth(Clef(*e.event()));
+
+ } else if (e.event()->isa(::Rosegarden::Key::EventType)) {
+
+ ::Rosegarden::Key key(*e.event());
+
+ ::Rosegarden::Key cancelKey = previousKey;
+
+ if (m_keySigCancelMode == 0) { // only when entering C maj / A min
+
+ if (key.getAccidentalCount() != 0)
+ cancelKey = ::Rosegarden::Key();
+
+ } else if (m_keySigCancelMode == 1) { // only when reducing acc count
+
+ if (!(key.isSharp() == cancelKey.isSharp() &&
+ key.getAccidentalCount() < cancelKey.getAccidentalCount())) {
+ cancelKey = ::Rosegarden::Key();
+ }
+ }
+
+ w += m_npf->getKeyWidth(key, cancelKey);
+
+ } else if (e.event()->isa(Indication::EventType) ||
+ e.event()->isa(Text::EventType)) {
+
+ w = 0;
+
+ } else {
+ // NOTATION_DEBUG << "NotationHLayout::getLayoutWidth(): no case for event type " << e.event()->getType() << endl;
+ // w += 24;
+ w = 0;
+ }
+
+ return w;
+ }
+}
+
+int NotationHLayout::getBarMargin() const
+{
+ return (int)(m_npf->getBarMargin() * m_spacing / 100.0);
+}
+
+int NotationHLayout::getPreBarMargin() const
+{
+ return getBarMargin() / 3;
+}
+
+int NotationHLayout::getPostBarMargin() const
+{
+ return getBarMargin() - getPreBarMargin();
+}
+
+int NotationHLayout::getFixedItemSpacing() const
+{
+ return (int)((m_npf->getNoteBodyWidth() * 2.0 / 3.0) * m_spacing / 100.0);
+}
+
+void
+NotationHLayout::reset()
+{
+ for (BarDataMap::iterator i = m_barData.begin();
+ i != m_barData.end(); ++i) {
+ clearBarList(*i->first);
+ }
+
+ m_barData.clear();
+ m_barPositions.clear();
+ m_totalWidth = 0;
+}
+
+void
+NotationHLayout::resetStaff(Staff &staff, timeT startTime, timeT endTime)
+{
+ if (startTime == endTime) {
+ getBarData(staff).clear();
+ m_totalWidth = 0;
+ }
+}
+
+int
+NotationHLayout::getFirstVisibleBar() const
+{
+ int bar = 0;
+ bool haveBar = false;
+ for (BarDataMap::const_iterator i = m_barData.begin(); i != m_barData.end(); ++i) {
+ if (i->second.begin() == i->second.end())
+ continue;
+ int barHere = i->second.begin()->first;
+ if (barHere < bar || !haveBar) {
+ bar = barHere;
+ haveBar = true;
+ }
+ }
+
+ // NOTATION_DEBUG << "NotationHLayout::getFirstVisibleBar: returning " << bar << endl;
+
+ return bar;
+}
+
+int
+NotationHLayout::getFirstVisibleBarOnStaff(Staff &staff)
+{
+ BarDataList &bdl(getBarData(staff));
+
+ int bar = 0;
+ if (bdl.begin() != bdl.end())
+ bar = bdl.begin()->first;
+
+ // NOTATION_DEBUG << "NotationHLayout::getFirstVisibleBarOnStaff: returning " << bar << endl;
+
+ return bar;
+}
+
+int
+NotationHLayout::getLastVisibleBar() const
+{
+ int bar = 0;
+ bool haveBar = false;
+ for (BarDataMap::const_iterator i = m_barData.begin();
+ i != m_barData.end(); ++i) {
+ if (i->second.begin() == i->second.end())
+ continue;
+ int barHere = getLastVisibleBarOnStaff(*i->first);
+ if (barHere > bar || !haveBar) {
+ bar = barHere;
+ haveBar = true;
+ }
+ }
+
+ // NOTATION_DEBUG << "NotationHLayout::getLastVisibleBar: returning " << bar << endl;
+
+ return bar;
+}
+
+int
+NotationHLayout::getLastVisibleBarOnStaff(Staff &staff) const
+{
+ const BarDataList &bdl(getBarData(staff));
+ int bar = 0;
+
+ if (bdl.begin() != bdl.end()) {
+ BarDataList::const_iterator i = bdl.end();
+ bar = ((--i)->first) + 1; // last visible bar_line_
+ }
+
+ // NOTATION_DEBUG << "NotationHLayout::getLastVisibleBarOnStaff: returning " << bar << endl;
+
+ return bar;
+}
+
+double
+NotationHLayout::getBarPosition(int bar) const
+{
+ double position = 0.0;
+
+ BarPositionList::const_iterator i = m_barPositions.find(bar);
+
+ if (i != m_barPositions.end()) {
+
+ position = i->second;
+
+ } else {
+
+ i = m_barPositions.begin();
+ if (i != m_barPositions.end()) {
+ if (bar < i->first)
+ position = i->second;
+ else {
+ i = m_barPositions.end();
+ --i;
+ if (bar > i->first)
+ position = i->second;
+ }
+ }
+ }
+
+ // NOTATION_DEBUG << "NotationHLayout::getBarPosition: returning " << position << " for bar " << bar << endl;
+
+ return position;
+}
+
+bool
+NotationHLayout::isBarCorrectOnStaff(Staff &staff, int i)
+{
+ BarDataList &bdl(getBarData(staff));
+ ++i;
+
+ BarDataList::iterator bdli(bdl.find(i));
+ if (bdli != bdl.end())
+ return bdli->second.basicData.correct;
+ else
+ return true;
+}
+
+bool NotationHLayout::getTimeSignaturePosition(Staff &staff,
+ int i,
+ TimeSignature &timeSig,
+ double &timeSigX)
+{
+ BarDataList &bdl(getBarData(staff));
+
+ BarDataList::iterator bdli(bdl.find(i));
+ if (bdli != bdl.end()) {
+ timeSig = bdli->second.basicData.timeSignature;
+ timeSigX = (double)(bdli->second.layoutData.timeSigX);
+ return bdli->second.basicData.newTimeSig;
+ } else
+ return 0;
+}
+
+timeT
+NotationHLayout::getTimeForX(double x) const
+{
+ return RulerScale::getTimeForX(x);
+}
+
+double
+NotationHLayout::getXForTime(timeT t) const
+{
+ return RulerScale::getXForTime(t);
+}
+
+double
+NotationHLayout::getXForTimeByEvent(timeT time) const
+{
+ // NOTATION_DEBUG << "NotationHLayout::getXForTime(" << time << ")" << endl;
+
+ for (BarDataMap::const_iterator i = m_barData.begin(); i != m_barData.end(); ++i) {
+
+ Staff *staff = i->first;
+
+ if (staff->getSegment().getStartTime() <= time &&
+ staff->getSegment().getEndMarkerTime() > time) {
+
+ ViewElementList::iterator vli =
+ staff->getViewElementList()->findNearestTime(time);
+
+ bool found = false;
+ double x = 0.0, dx = 0.0;
+ timeT t = 0, dt = 0;
+
+ while (!found) {
+ if (vli == staff->getViewElementList()->end())
+ break;
+ NotationElement *element = static_cast<NotationElement *>(*vli);
+ if (element->getCanvasItem()) {
+ x = element->getLayoutX();
+ double temp;
+ element->getLayoutAirspace(temp, dx);
+ t = element->event()->getNotationAbsoluteTime();
+ dt = element->event()->getNotationDuration();
+ found = true;
+ break;
+ }
+ ++vli;
+ }
+
+ if (found) {
+ if (time > t) {
+
+ while (vli != staff->getViewElementList()->end() &&
+ ((*vli)->event()->getNotationAbsoluteTime() < time ||
+ !((static_cast<NotationElement *>(*vli))->getCanvasItem())))
+ ++vli;
+
+ if (vli != staff->getViewElementList()->end()) {
+ NotationElement *element = static_cast<NotationElement *>(*vli);
+ dx = element->getLayoutX() - x;
+ dt = element->event()->getNotationAbsoluteTime() - t;
+ }
+
+ if (dt > 0 && dx > 0) {
+ return x + dx * (time - t) / dt;
+ }
+ }
+
+ return x - 3;
+ }
+ }
+ }
+
+ return RulerScale::getXForTime(time);
+}
+
+std::vector<int> NotationHLayout::m_availableSpacings;
+std::vector<int> NotationHLayout::m_availableProportions;
+
+}
diff --git a/src/gui/editors/notation/NotationHLayout.h b/src/gui/editors/notation/NotationHLayout.h
new file mode 100644
index 0000000..9d7366b
--- /dev/null
+++ b/src/gui/editors/notation/NotationHLayout.h
@@ -0,0 +1,446 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTATIONHLAYOUT_H_
+#define _RG_NOTATIONHLAYOUT_H_
+
+#include "base/LayoutEngine.h"
+#include "base/NotationTypes.h"
+#include "NotationElement.h"
+#include "gui/general/ProgressReporter.h"
+#include <map>
+#include <vector>
+#include "base/Event.h"
+
+
+class TieMap;
+class QObject;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class Staff;
+class Quantizer;
+class NotePixmapFactory;
+class NotationProperties;
+class NotationGroup;
+class NotationChord;
+class Key;
+class Composition;
+class Clef;
+class AccidentalTable;
+
+
+/**
+ * Horizontal notation layout
+ *
+ * computes the X coordinates of notation elements
+ */
+
+class NotationHLayout : public ProgressReporter,
+ public HorizontalLayoutEngine
+{
+public:
+ NotationHLayout(Composition *c,
+ NotePixmapFactory *npf,
+ const NotationProperties &properties,
+ QObject* parent, const char* name = 0);
+
+ virtual ~NotationHLayout();
+
+ void setNotePixmapFactory(NotePixmapFactory *npf) {
+ m_npf = npf;
+ }
+
+ /**
+ * Precomputes layout data for a single staff. The resulting data
+ * is stored in the BarDataMap, keyed from the staff reference;
+ * the entire map is then used by reconcileBars() and layout().
+ * The map should be cleared (by calling reset()) before a full
+ * set of staffs is preparsed.
+ */
+ virtual void scanStaff(Staff &staff,
+ timeT startTime = 0,
+ timeT endTime = 0);
+
+ /**
+ * Resets internal data stores, notably the BarDataMap that is
+ * used to retain the data computed by scanStaff().
+ */
+ virtual void reset();
+
+ /**
+ * Resets internal data stores, notably the given staff's entry
+ * in the BarDataMap used to retain the data computed by scanStaff().
+ */
+ virtual void resetStaff(Staff &staff,
+ timeT startTime = 0,
+ timeT endTime = 0);
+
+ /**
+ * Lays out all staffs that have been scanned
+ */
+ virtual void finishLayout(timeT startTime = 0,
+ timeT endTime = 0);
+
+ /**
+ * Set page mode
+ */
+ virtual void setPageMode(bool pageMode) { m_pageMode = pageMode; }
+
+ /**
+ * Get the page mode setting
+ */
+ virtual bool isPageMode() { return m_pageMode; }
+
+ /**
+ * Set a page width
+ */
+ virtual void setPageWidth(double pageWidth) { m_pageWidth = pageWidth; }
+
+ /**
+ * Get the page width
+ */
+ virtual double getPageWidth() { return m_pageWidth; }
+
+ /**
+ * Gets the current spacing factor (100 == "normal" spacing)
+ */
+ int getSpacing() const { return m_spacing; }
+
+ /**
+ * Sets the current spacing factor (100 == "normal" spacing)
+ */
+ void setSpacing(int spacing) { m_spacing = spacing; }
+
+ /**
+ * Gets the range of "standard" spacing factors (you can
+ * setSpacing() to anything you want, but it makes sense to
+ * have a standard list for GUI use). The only guaranteed
+ * property of the returned list is that 100 will be in it.
+ */
+ static std::vector<int> getAvailableSpacings();
+
+ /**
+ * Gets the current proportion (100 == spaces proportional to
+ * durations, 0 == equal spacings)
+ */
+ int getProportion() const { return m_proportion; }
+
+ /**
+ * Sets the current proportion (100 == spaces proportional to
+ * durations, 0 == equal spacings)
+ */
+ void setProportion(int proportion) { m_proportion = proportion; }
+
+ /**
+ * Gets the range of "standard" proportion factors (you can
+ * setProportion() to anything you want, but it makes sense to
+ * have a standard list for GUI use). The only guaranteed
+ * property of the returned list is that 0, 100, and whatever the
+ * default proportion is will be in it.
+ */
+ static std::vector<int> getAvailableProportions();
+
+ /**
+ * Returns the total length of all elements once layout is done
+ * This is the x-coord of the end of the last element on the longest
+ * staff, plus the space allocated to that element
+ */
+ virtual double getTotalWidth() const { return m_totalWidth; }
+
+ /**
+ * Returns the number of the first visible bar line on the given
+ * staff
+ */
+ virtual int getFirstVisibleBarOnStaff(Staff &staff);
+
+ /**
+ * Returns the number of the first visible bar line on any
+ * staff
+ */
+ virtual int getFirstVisibleBar() const;
+
+ /**
+ * Returns the number of the last visible bar line on the given
+ * staff
+ */
+ virtual int getLastVisibleBarOnStaff(Staff &staff) const;
+
+ /**
+ * Returns the number of the first visible bar line on any
+ * staff
+ */
+ virtual int getLastVisibleBar() const;
+
+ /**
+ * Returns the x-coordinate of the given bar number
+ */
+ virtual double getBarPosition(int barNo) const;
+
+ /**
+ * Returns the nearest time value to the given X coord.
+ */
+ virtual timeT getTimeForX(double x) const;
+
+ /**
+ * Returns the X coord corresponding to the given time value.
+ * This RulerScale method works by interpolating between bar lines
+ * (the inverse of the way getTimeForX works), and should be used
+ * for any rulers associated with the layout.
+ */
+ virtual double getXForTime(timeT time) const;
+
+ /**
+ * Returns the X coord corresponding to the given time value.
+ * This method works by interpolating between event positions, and
+ * should be used for position pointer tracking during playback.
+ */
+ virtual double getXForTimeByEvent(timeT time) const;
+
+ /**
+ * Returns true if the specified bar has the correct length
+ */
+ virtual bool isBarCorrectOnStaff(Staff &staff, int barNo);
+
+ /**
+ * Returns true if there is a new time signature in the given bar,
+ * setting timeSignature appropriately and setting timeSigX to its
+ * x-coord
+ */
+ virtual bool getTimeSignaturePosition
+ (Staff &staff, int barNo,
+ TimeSignature &timeSig, double &timeSigX);
+
+ /// purely optional, used only for progress reporting
+ void setStaffCount(int staffCount) {
+ m_staffCount = staffCount;
+ }
+
+protected:
+
+ struct Chunk {
+ timeT duration;
+ short subordering;
+ float fixed;
+ float stretchy;
+ float x;
+
+ Chunk(timeT d, short sub, float f, float s) :
+ duration(d), subordering(sub), fixed(f), stretchy(s), x(0) { }
+ Chunk(short sub, float f) :
+ duration(0), subordering(sub), fixed(f), stretchy(0), x(0) { }
+ };
+ typedef std::vector<Chunk> ChunkList;
+
+ /**
+ * Inner class for bar data, used by scanStaff()
+ */
+ struct BarData
+ {
+ ChunkList chunks;
+
+ struct BasicData
+ { // slots that can be filled at construction time
+
+ NotationElementList::iterator start; // i.e. event following barline
+ bool correct; // bar preceding barline has correct duration
+ TimeSignature timeSignature;
+ bool newTimeSig;
+
+ } basicData;
+
+ struct SizeData
+ { // slots that can be filled when the following bar has been scanned
+
+ float idealWidth; // theoretical width of bar following barline
+ float reconciledWidth;
+ float fixedWidth; // width of non-chunk items in bar
+ int clefKeyWidth;
+ timeT actualDuration; // may exceed nominal duration
+
+ } sizeData;
+
+ struct LayoutData
+ { // slots either assumed, or only known at layout time
+ bool needsLayout;
+ double x; // coordinate for display of barline
+ int timeSigX;
+
+ } layoutData;
+
+ BarData(NotationElementList::iterator i,
+ bool correct, TimeSignature timeSig, bool newTimeSig) {
+ basicData.start = i;
+ basicData.correct = correct;
+ basicData.timeSignature = timeSig;
+ basicData.newTimeSig = newTimeSig;
+ sizeData.idealWidth = 0;
+ sizeData.reconciledWidth = 0;
+ sizeData.fixedWidth = 0;
+ sizeData.clefKeyWidth = 0;
+ sizeData.actualDuration = 0;
+ layoutData.needsLayout = true;
+ layoutData.x = -1;
+ layoutData.timeSigX = -1;
+ }
+ };
+
+ typedef std::map<int, BarData> BarDataList;
+ typedef BarDataList::value_type BarDataPair;
+ typedef std::map<Staff *, BarDataList> BarDataMap;
+ typedef std::map<int, double> BarPositionList;
+
+ typedef std::map<Staff *, int> StaffIntMap;
+ typedef std::map<long, NotationGroup *> NotationGroupMap;
+
+ void clearBarList(Staff &);
+
+
+ /**
+ * Set the basic data for the given barNo. If barNo is
+ * beyond the end of the existing bar data list, create new
+ * records and/or fill with empty ones as appropriate.
+ */
+ void setBarBasicData(Staff &staff, int barNo,
+ NotationElementList::iterator start, bool correct,
+ TimeSignature timeSig, bool newTimeSig);
+
+ /**
+ * Set the size data for the given barNo. If barNo is
+ * beyond the end of the existing bar data list, create new
+ * records and/or fill with empty ones as appropriate.
+ */
+ void setBarSizeData(Staff &staff, int barNo,
+ float fixedWidth, timeT actualDuration);
+
+ /**
+ * Returns the bar positions for a given staff, provided that
+ * staff has been preparsed since the last reset
+ */
+ BarDataList& getBarData(Staff &staff);
+ const BarDataList& getBarData(Staff &staff) const;
+
+ /// Find the staff in which bar "barNo" is widest
+ Staff *getStaffWithWidestBar(int barNo);
+
+ /// Find width of clef+key in the staff in which they're widest in this bar
+ int getMaxRepeatedClefAndKeyWidth(int barNo);
+
+ /// For a single bar, makes sure synchronisation points align in all staves
+ void preSquishBar(int barNo);
+
+ /// Tries to harmonize the bar positions for all the staves (linear mode)
+ void reconcileBarsLinear();
+
+ /// Tries to harmonize the bar positions for all the staves (page mode)
+ void reconcileBarsPage();
+
+ void layout(BarDataMap::iterator,
+ timeT startTime,
+ timeT endTime);
+
+ /// Find earliest element with quantized time of t or greater
+ NotationElementList::iterator getStartOfQuantizedSlice
+ (NotationElementList *, timeT t) const;
+
+ void scanChord
+ (NotationElementList *notes, NotationElementList::iterator &i,
+ const Clef &, const ::Rosegarden::Key &,
+ AccidentalTable &, float &lyricWidth,
+ ChunkList &chunks, NotePixmapFactory *, int ottavaShift,
+ NotationElementList::iterator &to);
+
+ typedef std::map<int, NotationElementList::iterator> TieMap;
+
+ // This modifies the NotationElementList::iterator passed to it,
+ // moving it on to the last note in the chord; updates the TieMap;
+ // and may modify the to-iterator if it turns out to point at a
+ // note within the chord
+ void positionChord
+ (Staff &staff,
+ NotationElementList::iterator &, const Clef &clef,
+ const ::Rosegarden::Key &key, TieMap &, NotationElementList::iterator &to);
+
+ void sampleGroupElement
+ (Staff &staff, const Clef &clef,
+ const ::Rosegarden::Key &key, const NotationElementList::iterator &);
+
+ /// Difference between absolute time of next event and of this
+ timeT getSpacingDuration
+ (Staff &staff, const NotationElementList::iterator &);
+
+ /// Difference between absolute time of chord and of first event not in it
+ timeT getSpacingDuration
+ (Staff &staff, const NotationChord &);
+
+ float getLayoutWidth(ViewElement &,
+ NotePixmapFactory *,
+ const ::Rosegarden::Key &) const;
+
+ int getBarMargin() const;
+ int getPreBarMargin() const;
+ int getPostBarMargin() const;
+ int getFixedItemSpacing() const;
+
+ NotePixmapFactory *getNotePixmapFactory(Staff &);
+ NotePixmapFactory *getGraceNotePixmapFactory(Staff &);
+
+ //--------------- Data members ---------------------------------
+
+ BarDataMap m_barData;
+ StaffIntMap m_staffNameWidths;
+ BarPositionList m_barPositions;
+ NotationGroupMap m_groupsExtant;
+
+ double m_totalWidth;
+ bool m_pageMode;
+ double m_pageWidth;
+ int m_spacing;
+ int m_proportion;
+ int m_keySigCancelMode;
+
+ //!!! This should not be here -- different staffs may have
+ //different sizes in principle, so we should always be referring
+ //to the npf of a particular staff
+ NotePixmapFactory *m_npf;
+
+ static std::vector<int> m_availableSpacings;
+ static std::vector<int> m_availableProportions;
+
+ const Quantizer *m_notationQuantizer;
+ const NotationProperties &m_properties;
+
+ int m_timePerProgressIncrement;
+ std::map<Staff *, bool> m_haveOttavaSomewhere;
+ int m_staffCount; // purely for progress reporting
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationProperties.cpp b/src/gui/editors/notation/NotationProperties.cpp
new file mode 100644
index 0000000..8c87cc3
--- /dev/null
+++ b/src/gui/editors/notation/NotationProperties.cpp
@@ -0,0 +1,85 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NotationProperties.h"
+
+#include "base/PropertyName.h"
+
+
+namespace Rosegarden
+{
+
+const PropertyName NotationProperties::NOTE_STYLE = "NoteStyle";
+const PropertyName NotationProperties::HEIGHT_ON_STAFF = "HeightOnStaff";
+const PropertyName NotationProperties::BEAMED = "Beamed";
+const PropertyName NotationProperties::BEAM_ABOVE = "BeamAbove";
+const PropertyName NotationProperties::SLASHES = "Slashes";
+const PropertyName NotationProperties::STEM_UP = "NoteStemUp";
+const PropertyName NotationProperties::USE_CAUTIONARY_ACCIDENTAL = "UseCautionaryAccidental";
+const PropertyName NotationProperties::OTTAVA_SHIFT = "OttavaShift";
+const PropertyName NotationProperties::SLUR_ABOVE = "SlurAbove";
+
+NotationProperties::NotationProperties(const std::string &prefix) :
+
+ VIEW_LOCAL_STEM_UP (prefix + "StemUp"),
+
+ MIN_WIDTH (prefix + "MinWidth"),
+
+ CALCULATED_ACCIDENTAL (prefix + "NoteCalculatedAccidental"),
+ DISPLAY_ACCIDENTAL (prefix + "NoteDisplayAccidental"),
+ DISPLAY_ACCIDENTAL_IS_CAUTIONARY(prefix + "NoteDisplayAccidentalIsCautionary"),
+ ACCIDENTAL_SHIFT (prefix + "NoteAccidentalShift"),
+ ACCIDENTAL_EXTRA_SHIFT (prefix + "NoteAccidentalExtraShift"),
+ UNBEAMED_STEM_LENGTH (prefix + "UnbeamedStemLength"),
+ DRAW_FLAG (prefix + "NoteDrawFlag"),
+ NOTE_HEAD_SHIFTED (prefix + "NoteHeadShifted"),
+ NEEDS_EXTRA_SHIFT_SPACE (prefix + "NeedsExtraShiftSpace"),
+ NOTE_DOT_SHIFTED (prefix + "NoteDotShifted"),
+ CHORD_PRIMARY_NOTE (prefix + "ChordPrimaryNote"),
+ CHORD_MARK_COUNT (prefix + "ChordMarkCount"),
+ TIE_LENGTH (prefix + "TieLength"),
+ SLUR_Y_DELTA (prefix + "SlurYDelta"),
+ SLUR_LENGTH (prefix + "SlurLength"),
+ LYRIC_EXTRA_WIDTH (prefix + "LyricExtraWidth"),
+ REST_TOO_SHORT (prefix + "RestTooShort"),
+ REST_OUTSIDE_STAVE (prefix + "RestOutsideStave"),
+
+ BEAM_GRADIENT (prefix + "BeamGradient"),
+ BEAM_SECTION_WIDTH (prefix + "BeamSectionWidth"),
+ BEAM_NEXT_BEAM_COUNT (prefix + "BeamNextBeamCount"),
+ BEAM_NEXT_PART_BEAMS (prefix + "BeamNextPartBeams"),
+ BEAM_THIS_PART_BEAMS (prefix + "BeamThisPartBeams"),
+ BEAM_MY_Y (prefix + "BeamMyY"),
+
+ TUPLING_LINE_MY_Y (prefix + "TuplingLineMyY"),
+ TUPLING_LINE_WIDTH (prefix + "TuplingLineWidth"),
+ TUPLING_LINE_GRADIENT (prefix + "TuplingLineGradient"),
+ TUPLING_LINE_FOLLOWS_BEAM (prefix + "TuplingLineFollowsBeam")
+
+{
+ // nothing else
+}
+
+}
diff --git a/src/gui/editors/notation/NotationProperties.h b/src/gui/editors/notation/NotationProperties.h
new file mode 100644
index 0000000..69a26cf
--- /dev/null
+++ b/src/gui/editors/notation/NotationProperties.h
@@ -0,0 +1,108 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTATIONPROPERTIES_H_
+#define _RG_NOTATIONPROPERTIES_H_
+
+#include "base/PropertyName.h"
+#include <string>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * Property names for properties that are computed and cached within
+ * the notation module, but that need not necessarily be saved with
+ * the file.
+ *
+ * If you add something here, remember to add the definition to
+ * notationproperties.cpp as well...
+ */
+
+class NotationProperties
+{
+public:
+ NotationProperties(const std::string &prefix);
+
+ // These are only of interest to notation views, but are the
+ // same across all notation views.
+
+ static const PropertyName HEIGHT_ON_STAFF;
+ static const PropertyName NOTE_STYLE;
+ static const PropertyName BEAMED;
+ static const PropertyName BEAM_ABOVE;
+ static const PropertyName SLASHES;
+ static const PropertyName STEM_UP;
+ static const PropertyName USE_CAUTIONARY_ACCIDENTAL;
+ static const PropertyName OTTAVA_SHIFT;
+ static const PropertyName SLUR_ABOVE;
+
+ // The rest are, or may be, view-local
+
+ const PropertyName VIEW_LOCAL_STEM_UP;
+ const PropertyName MIN_WIDTH;
+ const PropertyName CALCULATED_ACCIDENTAL;
+ const PropertyName DISPLAY_ACCIDENTAL;
+ const PropertyName DISPLAY_ACCIDENTAL_IS_CAUTIONARY;
+ const PropertyName ACCIDENTAL_SHIFT;
+ const PropertyName ACCIDENTAL_EXTRA_SHIFT;
+ const PropertyName UNBEAMED_STEM_LENGTH;
+ const PropertyName DRAW_FLAG;
+ const PropertyName NOTE_HEAD_SHIFTED;
+ const PropertyName NEEDS_EXTRA_SHIFT_SPACE;
+ const PropertyName NOTE_DOT_SHIFTED;
+ const PropertyName CHORD_PRIMARY_NOTE;
+ const PropertyName CHORD_MARK_COUNT;
+ const PropertyName TIE_LENGTH;
+ const PropertyName SLUR_Y_DELTA;
+ const PropertyName SLUR_LENGTH;
+ const PropertyName LYRIC_EXTRA_WIDTH;
+ const PropertyName REST_TOO_SHORT;
+ const PropertyName REST_OUTSIDE_STAVE;
+
+ // Set in applyBeam in notationsets.cpp:
+
+ const PropertyName BEAM_GRADIENT;
+ const PropertyName BEAM_SECTION_WIDTH;
+ const PropertyName BEAM_NEXT_BEAM_COUNT;
+ const PropertyName BEAM_NEXT_PART_BEAMS;
+ const PropertyName BEAM_THIS_PART_BEAMS;
+ const PropertyName BEAM_MY_Y;
+ const PropertyName TUPLING_LINE_MY_Y;
+ const PropertyName TUPLING_LINE_WIDTH;
+ const PropertyName TUPLING_LINE_GRADIENT;
+ const PropertyName TUPLING_LINE_FOLLOWS_BEAM;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationSelectionPaster.cpp b/src/gui/editors/notation/NotationSelectionPaster.cpp
new file mode 100644
index 0000000..3b008f2
--- /dev/null
+++ b/src/gui/editors/notation/NotationSelectionPaster.cpp
@@ -0,0 +1,89 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NotationSelectionPaster.h"
+
+#include <klocale.h>
+#include "base/Event.h"
+#include "base/Selection.h"
+#include "base/ViewElement.h"
+#include "commands/edit/PasteEventsCommand.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/LinedStaff.h"
+#include "document/RosegardenGUIDoc.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NotationElement.h"
+
+
+namespace Rosegarden
+{
+
+NotationSelectionPaster::NotationSelectionPaster(EventSelection& es,
+ NotationView* view)
+ : NotationTool("NotationPaster", view),
+ m_selection(es)
+{
+ m_nParentView->setCanvasCursor(Qt::crossCursor);
+}
+
+NotationSelectionPaster::~NotationSelectionPaster()
+{}
+
+void NotationSelectionPaster::handleLeftButtonPress(timeT,
+ int,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement*)
+{
+ if (staffNo < 0)
+ return ;
+ Event *clef = 0, *key = 0;
+
+ LinedStaff *staff = m_nParentView->getLinedStaff(staffNo);
+
+ NotationElementList::iterator closestElement =
+ staff->getClosestElementToCanvasCoords(e->x(), (int)e->y(),
+ clef, key, false, -1);
+
+ if (closestElement == staff->getViewElementList()->end())
+ return ;
+
+ timeT time = (*closestElement)->getViewAbsoluteTime();
+
+ Segment& segment = staff->getSegment();
+ PasteEventsCommand *command = new PasteEventsCommand
+ (segment, m_parentView->getDocument()->getClipboard(), time,
+ PasteEventsCommand::Restricted);
+
+ if (!command->isPossible()) {
+ m_parentView->slotStatusHelpMsg(i18n("Couldn't paste at this point"));
+ } else {
+ m_parentView->addCommandToHistory(command);
+ m_parentView->slotStatusHelpMsg(i18n("Ready."));
+ }
+}
+
+}
diff --git a/src/gui/editors/notation/NotationSelectionPaster.h b/src/gui/editors/notation/NotationSelectionPaster.h
new file mode 100644
index 0000000..e6a80dd
--- /dev/null
+++ b/src/gui/editors/notation/NotationSelectionPaster.h
@@ -0,0 +1,72 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTATIONSELECTIONPASTER_H_
+#define _RG_NOTATIONSELECTIONPASTER_H_
+
+#include "NotationTool.h"
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class NotationView;
+class EventSelection;
+
+
+/**
+ * Selection pasting - unused at the moment
+ */
+class NotationSelectionPaster : public NotationTool
+{
+public:
+
+ ~NotationSelectionPaster();
+
+ virtual void handleLeftButtonPress(timeT,
+ int height, int staffNo,
+ QMouseEvent*,
+ ViewElement* el);
+
+protected:
+ NotationSelectionPaster(EventSelection&,
+ NotationView*);
+
+ //--------------- Data members ---------------------------------
+
+ EventSelection& m_selection;
+
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationSelector.cpp b/src/gui/editors/notation/NotationSelector.cpp
new file mode 100644
index 0000000..221fbe3
--- /dev/null
+++ b/src/gui/editors/notation/NotationSelector.cpp
@@ -0,0 +1,957 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NotationSelector.h"
+#include "misc/Debug.h"
+
+#include <klocale.h>
+#include "base/Event.h"
+#include "base/NotationTypes.h"
+#include "base/PropertyName.h"
+#include "base/Selection.h"
+#include "base/ViewElement.h"
+#include "base/BaseProperties.h"
+#include "commands/edit/MoveAcrossSegmentsCommand.h"
+#include "commands/edit/MoveCommand.h"
+#include "commands/edit/TransposeCommand.h"
+#include "commands/notation/IncrementDisplacementsCommand.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/LinedStaff.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "gui/kdeext/QCanvasSimpleSprite.h"
+#include "NotationElement.h"
+#include "NotationProperties.h"
+#include "NotationStaff.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NotePixmapFactory.h"
+#include <kaction.h>
+#include <qapplication.h>
+#include <qiconset.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <qtimer.h>
+
+
+namespace Rosegarden
+{
+
+using namespace BaseProperties;
+
+NotationSelector::NotationSelector(NotationView* view)
+ : NotationTool("NotationSelector", view),
+ m_selectionRect(0),
+ m_updateRect(false),
+ m_selectedStaff(0),
+ m_clickedElement(0),
+ m_selectionToMerge(0),
+ m_justSelectedBar(false),
+ m_wholeStaffSelectionComplete(false)
+{
+ connect(m_parentView, SIGNAL(usedSelection()),
+ this, SLOT(slotHideSelection()));
+
+ connect(this, SIGNAL(editElement(NotationStaff *, NotationElement *, bool)),
+ m_parentView, SLOT(slotEditElement(NotationStaff *, NotationElement *, bool)));
+
+ QIconSet icon
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("crotchet")));
+ new KToggleAction(i18n("Switch to Insert Tool"), icon, 0, this,
+ SLOT(slotInsertSelected()), actionCollection(),
+ "insert");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ // (this crashed, and it might be superfluous with ^N anyway, so I'm
+ // commenting it out, but leaving it here in case I change my mind about
+ // fooling with it.) (DMM)
+ // new KAction(i18n("Normalize Rests"), 0, 0, this,
+ // SLOT(slotCollapseRests()), actionCollection(),
+ // "collapse_rests");
+
+ new KAction(i18n("Collapse Rests"), 0, 0, this,
+ SLOT(slotCollapseRestsHard()), actionCollection(),
+ "collapse_rests_aggressively");
+
+ new KAction(i18n("Respell as Flat"), 0, 0, this,
+ SLOT(slotRespellFlat()), actionCollection(),
+ "respell_flat");
+
+ new KAction(i18n("Respell as Sharp"), 0, 0, this,
+ SLOT(slotRespellSharp()), actionCollection(),
+ "respell_sharp");
+
+ new KAction(i18n("Respell as Natural"), 0, 0, this,
+ SLOT(slotRespellNatural()), actionCollection(),
+ "respell_natural");
+
+ new KAction(i18n("Collapse Notes"), 0, 0, this,
+ SLOT(slotCollapseNotes()), actionCollection(),
+ "collapse_notes");
+
+ new KAction(i18n("Interpret"), 0, 0, this,
+ SLOT(slotInterpret()), actionCollection(),
+ "interpret");
+
+ new KAction(i18n("Move to Staff Above"), 0, 0, this,
+ SLOT(slotStaffAbove()), actionCollection(),
+ "move_events_up_staff");
+
+ new KAction(i18n("Move to Staff Below"), 0, 0, this,
+ SLOT(slotStaffBelow()), actionCollection(),
+ "move_events_down_staff");
+
+ new KAction(i18n("Make Invisible"), 0, 0, this,
+ SLOT(slotMakeInvisible()), actionCollection(),
+ "make_invisible");
+
+ new KAction(i18n("Make Visible"), 0, 0, this,
+ SLOT(slotMakeVisible()), actionCollection(),
+ "make_visible");
+
+ createMenu("notationselector.rc");
+}
+
+NotationSelector::~NotationSelector()
+{
+ delete m_selectionToMerge;
+}
+
+void NotationSelector::handleLeftButtonPress(timeT t,
+ int height,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element)
+{
+ std::cerr << "NotationSelector::handleMousePress: time is " << t << ", staffNo is "
+ << staffNo << ", e and element are " << e << " and " << element << std::endl;
+
+ if (m_justSelectedBar) {
+ handleMouseTripleClick(t, height, staffNo, e, element);
+ m_justSelectedBar = false;
+ return ;
+ }
+
+ m_wholeStaffSelectionComplete = false;
+
+ delete m_selectionToMerge;
+ const EventSelection *selectionToMerge = 0;
+ if (e->state() & ShiftButton) {
+ m_clickedShift = true;
+ selectionToMerge = m_nParentView->getCurrentSelection();
+ } else {
+ m_clickedShift = false;
+ }
+ m_selectionToMerge =
+ (selectionToMerge ? new EventSelection(*selectionToMerge) : 0);
+
+ m_clickedElement = dynamic_cast<NotationElement*>(element);
+ if (m_clickedElement) {
+ m_selectedStaff = getStaffForElement(m_clickedElement);
+ m_lastDragPitch = -400;
+ m_lastDragTime = m_clickedElement->event()->getNotationAbsoluteTime();
+ } else {
+ m_selectedStaff = 0; // don't know yet; wait until we have an element
+ }
+
+ m_selectionRect->setX(e->x());
+ m_selectionRect->setY(e->y());
+ m_selectionRect->setSize(0, 0);
+
+ m_selectionRect->show();
+ m_updateRect = true;
+ m_startedFineDrag = false;
+
+ //m_parentView->setCursorPosition(p.x());
+}
+
+void NotationSelector::handleRightButtonPress(timeT t,
+ int height,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element)
+{
+ std::cerr << "NotationSelector::handleRightButtonPress" << std::endl;
+
+ const EventSelection *sel = m_nParentView->getCurrentSelection();
+
+ if (!sel || sel->getSegmentEvents().empty()) {
+
+ // if nothing selected, permit the possibility of selecting
+ // something before showing the menu
+
+ if (element) {
+ m_clickedElement = dynamic_cast<NotationElement*>(element);
+ m_selectedStaff = getStaffForElement(m_clickedElement);
+ m_nParentView->setSingleSelectedEvent
+ (m_selectedStaff->getId(), m_clickedElement->event(),
+ true, true);
+ }
+
+ handleLeftButtonPress(t, height, staffNo, e, element);
+ }
+
+ EditTool::handleRightButtonPress(t, height, staffNo, e, element);
+}
+
+void NotationSelector::slotClickTimeout()
+{
+ m_justSelectedBar = false;
+}
+
+void NotationSelector::handleMouseDoubleClick(timeT,
+ int,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element)
+{
+ NOTATION_DEBUG << "NotationSelector::handleMouseDoubleClick" << endl;
+ m_clickedElement = dynamic_cast<NotationElement*>(element);
+
+ NotationStaff *staff = m_nParentView->getNotationStaff(staffNo);
+ if (!staff)
+ return ;
+ m_selectedStaff = staff;
+
+ bool advanced = (e->state() & ShiftButton);
+
+ if (m_clickedElement) {
+
+ emit editElement(staff, m_clickedElement, advanced);
+
+ } else {
+
+ QRect rect = staff->getBarExtents(e->x(), e->y());
+
+ m_selectionRect->setX(rect.x() + 1);
+ m_selectionRect->setY(rect.y());
+ m_selectionRect->setSize(rect.width() - 1, rect.height());
+
+ m_selectionRect->show();
+ m_updateRect = false;
+
+ m_justSelectedBar = true;
+ QTimer::singleShot(QApplication::doubleClickInterval(), this,
+ SLOT(slotClickTimeout()));
+ }
+
+ return ;
+}
+
+void NotationSelector::handleMouseTripleClick(timeT t,
+ int height,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element)
+{
+ if (!m_justSelectedBar)
+ return ;
+ m_justSelectedBar = false;
+
+ NOTATION_DEBUG << "NotationSelector::handleMouseTripleClick" << endl;
+ m_clickedElement = dynamic_cast<NotationElement*>(element);
+
+ NotationStaff *staff = m_nParentView->getNotationStaff(staffNo);
+ if (!staff)
+ return ;
+ m_selectedStaff = staff;
+
+ if (m_clickedElement) {
+
+ // should be safe, as we've already set m_justSelectedBar false
+ handleLeftButtonPress(t, height, staffNo, e, element);
+ return ;
+
+ } else {
+
+ m_selectionRect->setX(staff->getX());
+ m_selectionRect->setY(staff->getY());
+ m_selectionRect->setSize(int(staff->getTotalWidth()) - 1,
+ staff->getTotalHeight() - 1);
+
+ m_selectionRect->show();
+ m_updateRect = false;
+ }
+
+ m_wholeStaffSelectionComplete = true;
+
+ return ;
+}
+
+int NotationSelector::handleMouseMove(timeT, int,
+ QMouseEvent* e)
+{
+ if (!m_updateRect)
+ return RosegardenCanvasView::NoFollow;
+
+ int w = int(e->x() - m_selectionRect->x());
+ int h = int(e->y() - m_selectionRect->y());
+
+ if (m_clickedElement /* && !m_clickedElement->isRest() */) {
+
+ if (m_startedFineDrag) {
+ dragFine(e->x(), e->y(), false);
+ } else if (m_clickedShift) {
+ if (w > 2 || w < -2 || h > 2 || h < -2) {
+ dragFine(e->x(), e->y(), false);
+ }
+ } else if (w > 3 || w < -3 || h > 3 || h < -3) {
+ drag(e->x(), e->y(), false);
+ }
+
+ } else {
+
+ // Qt rectangle dimensions appear to be 1-based
+ if (w > 0)
+ ++w;
+ else
+ --w;
+ if (h > 0)
+ ++h;
+ else
+ --h;
+
+ m_selectionRect->setSize(w, h);
+ setViewCurrentSelection(true);
+ m_nParentView->canvas()->update();
+ }
+
+ return RosegardenCanvasView::FollowHorizontal | RosegardenCanvasView::FollowVertical;
+}
+
+void NotationSelector::handleMouseRelease(timeT, int, QMouseEvent *e)
+{
+ NOTATION_DEBUG << "NotationSelector::handleMouseRelease" << endl;
+ m_updateRect = false;
+
+ NOTATION_DEBUG << "selectionRect width, height: " << m_selectionRect->width()
+ << ", " << m_selectionRect->height() << endl;
+
+ // Test how far we've moved from the original click position -- not
+ // how big the rectangle is (if we were dragging an event, the
+ // rectangle size will still be zero).
+
+ if (((e->x() - m_selectionRect->x()) > -3 &&
+ (e->x() - m_selectionRect->x()) < 3 &&
+ (e->y() - m_selectionRect->y()) > -3 &&
+ (e->y() - m_selectionRect->y()) < 3) &&
+ !m_startedFineDrag) {
+
+ if (m_clickedElement != 0 && m_selectedStaff) {
+
+ // If we didn't drag out a meaningful area, but _did_
+ // click on an individual event, then select just that
+ // event
+
+ if (m_selectionToMerge &&
+ m_selectionToMerge->getSegment() ==
+ m_selectedStaff->getSegment()) {
+
+ // if the event was already part of the selection, we want to
+ // remove it
+ if (m_selectionToMerge->contains(m_clickedElement->event())) {
+ m_selectionToMerge->removeEvent(m_clickedElement->event());
+ } else {
+ m_selectionToMerge->addEvent(m_clickedElement->event());
+ }
+
+ m_nParentView->setCurrentSelection(m_selectionToMerge,
+ true, true);
+ m_selectionToMerge = 0;
+
+ } else {
+
+ m_nParentView->setSingleSelectedEvent
+ (m_selectedStaff->getId(), m_clickedElement->event(),
+ true, true);
+ }
+ /*
+ } else if (m_selectedStaff) {
+
+ // If we clicked on no event but on a staff, move the
+ // insertion cursor to the point where we clicked.
+ // Actually we only really want this to happen if
+ // we aren't double-clicking -- consider using a timer
+ // to establish whether a double-click is going to happen
+
+ m_nParentView->slotSetInsertCursorPosition(e->x(), (int)e->y());
+ */
+ } else {
+ setViewCurrentSelection(false);
+ }
+
+ } else {
+
+ if (m_startedFineDrag) {
+ dragFine(e->x(), e->y(), true);
+ } else if (m_clickedElement /* && !m_clickedElement->isRest() */) {
+ drag(e->x(), e->y(), true);
+ } else {
+ setViewCurrentSelection(false);
+ }
+ }
+
+ m_clickedElement = 0;
+ m_selectionRect->hide();
+ m_wholeStaffSelectionComplete = false;
+ m_nParentView->canvas()->update();
+}
+
+void NotationSelector::drag(int x, int y, bool final)
+{
+ NOTATION_DEBUG << "NotationSelector::drag " << x << ", " << y << endl;
+
+ if (!m_clickedElement || !m_selectedStaff)
+ return ;
+
+ EventSelection *selection = m_nParentView->getCurrentSelection();
+ if (!selection || !selection->contains(m_clickedElement->event())) {
+ selection = new EventSelection(m_selectedStaff->getSegment());
+ selection->addEvent(m_clickedElement->event());
+ }
+ m_nParentView->setCurrentSelection(selection);
+
+ LinedStaff *targetStaff = m_nParentView->getStaffForCanvasCoords(x, y);
+ if (!targetStaff)
+ targetStaff = m_selectedStaff;
+
+ // Calculate time and height
+
+ timeT clickedTime = m_clickedElement->event()->getNotationAbsoluteTime();
+
+ Accidental clickedAccidental = Accidentals::NoAccidental;
+ (void)m_clickedElement->event()->get<String>(ACCIDENTAL, clickedAccidental);
+
+ long clickedPitch = 0;
+ (void)m_clickedElement->event()->get<Int>(PITCH, clickedPitch);
+
+ long clickedHeight = 0;
+ (void)m_clickedElement->event()->get<Int>
+ (NotationProperties::HEIGHT_ON_STAFF, clickedHeight);
+
+ Event *clefEvt = 0, *keyEvt = 0;
+ Clef clef;
+ ::Rosegarden::Key key;
+
+ timeT dragTime = clickedTime;
+ double layoutX = m_clickedElement->getLayoutX();
+ timeT duration = m_clickedElement->getViewDuration();
+
+ NotationElementList::iterator itr =
+ targetStaff->getElementUnderCanvasCoords(x, y, clefEvt, keyEvt);
+
+ if (itr != targetStaff->getViewElementList()->end()) {
+
+ NotationElement *elt = dynamic_cast<NotationElement *>(*itr);
+ dragTime = elt->getViewAbsoluteTime();
+ layoutX = elt->getLayoutX();
+
+ if (elt->isRest() && duration > 0 && elt->getCanvasItem()) {
+
+ double restX = 0, restWidth = 0;
+ elt->getCanvasAirspace(restX, restWidth);
+
+ timeT restDuration = elt->getViewDuration();
+
+ if (restWidth > 0 &&
+ restDuration >= duration * 2) {
+
+ int parts = restDuration / duration;
+ double encroachment = x - restX;
+ NOTATION_DEBUG << "encroachment is " << encroachment << ", restWidth is " << restWidth << endl;
+ int part = (int)((encroachment / restWidth) * parts);
+ if (part >= parts)
+ part = parts - 1;
+
+ dragTime += part * restDuration / parts;
+ layoutX += part * restWidth / parts +
+ (restX - elt->getCanvasX());
+ }
+ }
+ }
+
+ if (clefEvt)
+ clef = Clef(*clefEvt);
+ if (keyEvt)
+ key = ::Rosegarden::Key(*keyEvt);
+
+ int height = targetStaff->getHeightAtCanvasCoords(x, y);
+ int pitch = clickedPitch;
+
+ if (height != clickedHeight)
+ pitch =
+ Pitch
+ (height, clef, key, clickedAccidental).getPerformancePitch();
+
+ if (pitch < clickedPitch) {
+ if (height < -10) {
+ height = -10;
+ pitch = Pitch
+ (height, clef, key, clickedAccidental).getPerformancePitch();
+ }
+ } else if (pitch > clickedPitch) {
+ if (height > 18) {
+ height = 18;
+ pitch = Pitch
+ (height, clef, key, clickedAccidental).getPerformancePitch();
+ }
+ }
+
+ bool singleNonNotePreview = !m_clickedElement->isNote() &&
+ selection->getSegmentEvents().size() == 1;
+
+ if (!final && !singleNonNotePreview) {
+
+ if ((pitch != m_lastDragPitch || dragTime != m_lastDragTime) &&
+ m_clickedElement->isNote()) {
+
+ m_nParentView->showPreviewNote(targetStaff->getId(),
+ layoutX, pitch, height,
+ Note::getNearestNote(duration),
+ m_clickedElement->isGrace());
+ m_lastDragPitch = pitch;
+ m_lastDragTime = dragTime;
+ }
+
+ } else {
+
+ m_nParentView->clearPreviewNote();
+
+ KMacroCommand *command = new KMacroCommand(MoveCommand::getGlobalName());
+ bool haveSomething = false;
+
+ MoveCommand *mc = 0;
+ Event *lastInsertedEvent = 0;
+
+ if (pitch != clickedPitch && m_clickedElement->isNote()) {
+ command->addCommand(new TransposeCommand(pitch - clickedPitch,
+ *selection));
+ haveSomething = true;
+ }
+
+ if (targetStaff != m_selectedStaff) {
+ command->addCommand(new MoveAcrossSegmentsCommand
+ (m_selectedStaff->getSegment(),
+ targetStaff->getSegment(),
+ dragTime - clickedTime + selection->getStartTime(),
+ true,
+ *selection));
+ haveSomething = true;
+ } else {
+ if (dragTime != clickedTime) {
+ mc = new MoveCommand
+ (m_selectedStaff->getSegment(), //!!!sort
+ dragTime - clickedTime, true, *selection);
+ command->addCommand(mc);
+ haveSomething = true;
+ }
+ }
+
+ if (haveSomething) {
+
+ m_nParentView->addCommandToHistory(command);
+
+ if (mc && singleNonNotePreview) {
+
+ lastInsertedEvent = mc->getLastInsertedEvent();
+
+ if (lastInsertedEvent) {
+ m_nParentView->setSingleSelectedEvent(targetStaff->getId(),
+ lastInsertedEvent);
+
+ ViewElementList::iterator vli =
+ targetStaff->findEvent(lastInsertedEvent);
+
+ if (vli != targetStaff->getViewElementList()->end()) {
+ m_clickedElement = dynamic_cast<NotationElement *>(*vli);
+ } else {
+ m_clickedElement = 0;
+ }
+
+ m_selectionRect->setX(x);
+ m_selectionRect->setY(y);
+ }
+ }
+ } else {
+ delete command;
+ }
+ }
+}
+
+void NotationSelector::dragFine(int x, int y, bool final)
+{
+ NOTATION_DEBUG << "NotationSelector::drag " << x << ", " << y << endl;
+
+ if (!m_clickedElement || !m_selectedStaff)
+ return ;
+
+ EventSelection *selection = m_nParentView->getCurrentSelection();
+ if (!selection)
+ selection = new EventSelection(m_selectedStaff->getSegment());
+ if (!selection->contains(m_clickedElement->event()))
+ selection->addEvent(m_clickedElement->event());
+ m_nParentView->setCurrentSelection(selection);
+
+ // Fine drag modifies the DISPLACED_X and DISPLACED_Y properties on
+ // each event. The modifications have to be relative to the previous
+ // values of these properties, not to zero, so for each event we need
+ // to store the previous value at the time the drag starts.
+
+ static PropertyName xProperty("temporary-displaced-x");
+ static PropertyName yProperty("temporary-displaced-y");
+
+ if (!m_startedFineDrag) {
+ // back up original properties
+
+ for (EventSelection::eventcontainer::iterator i =
+ selection->getSegmentEvents().begin();
+ i != selection->getSegmentEvents().end(); ++i) {
+ long prevX = 0, prevY = 0;
+ (*i)->get
+ <Int>(DISPLACED_X, prevX);
+ (*i)->get
+ <Int>(DISPLACED_Y, prevY);
+ (*i)->setMaybe<Int>(xProperty, prevX);
+ (*i)->setMaybe<Int>(yProperty, prevY);
+ }
+
+ m_startedFineDrag = true;
+ }
+
+ // We want the displacements in 1/1000ths of a staff space
+
+ double dx = x - m_selectionRect->x();
+ double dy = y - m_selectionRect->y();
+
+ double noteBodyWidth = m_nParentView->getNotePixmapFactory()->getNoteBodyWidth();
+ double lineSpacing = m_nParentView->getNotePixmapFactory()->getLineSpacing();
+ dx = (1000.0 * dx) / noteBodyWidth;
+ dy = (1000.0 * dy) / lineSpacing;
+
+ if (final) {
+
+ // reset original values (and remove backup values) before
+ // applying command
+
+ for (EventSelection::eventcontainer::iterator i =
+ selection->getSegmentEvents().begin();
+ i != selection->getSegmentEvents().end(); ++i) {
+ long prevX = 0, prevY = 0;
+ (*i)->get
+ <Int>(xProperty, prevX);
+ (*i)->get
+ <Int>(yProperty, prevY);
+ (*i)->setMaybe<Int>(DISPLACED_X, prevX);
+ (*i)->setMaybe<Int>(DISPLACED_Y, prevY);
+ (*i)->unset(xProperty);
+ (*i)->unset(yProperty);
+ }
+
+ IncrementDisplacementsCommand *command = new IncrementDisplacementsCommand
+ (*selection, long(dx), long(dy));
+ m_nParentView->addCommandToHistory(command);
+
+ } else {
+
+ timeT startTime = 0, endTime = 0;
+
+ for (EventSelection::eventcontainer::iterator i =
+ selection->getSegmentEvents().begin();
+ i != selection->getSegmentEvents().end(); ++i) {
+ long prevX = 0, prevY = 0;
+ (*i)->get
+ <Int>(xProperty, prevX);
+ (*i)->get
+ <Int>(yProperty, prevY);
+ (*i)->setMaybe<Int>(DISPLACED_X, prevX + long(dx));
+ (*i)->setMaybe<Int>(DISPLACED_Y, prevY + long(dy));
+ if (i == selection->getSegmentEvents().begin()) {
+ startTime = (*i)->getAbsoluteTime();
+ }
+ endTime = (*i)->getAbsoluteTime() + (*i)->getDuration();
+ }
+
+ if (startTime == endTime)
+ ++endTime;
+ selection->getSegment().updateRefreshStatuses(startTime, endTime);
+ m_nParentView->update();
+ }
+}
+
+void NotationSelector::ready()
+{
+ m_selectionRect = new QCanvasRectangle(m_nParentView->canvas());
+
+ m_selectionRect->hide();
+ m_selectionRect->setPen(GUIPalette::getColour(GUIPalette::SelectionRectangle));
+
+ m_nParentView->setCanvasCursor(Qt::arrowCursor);
+ m_nParentView->setHeightTracking(false);
+}
+
+void NotationSelector::stow()
+{
+ delete m_selectionRect;
+ m_selectionRect = 0;
+ m_nParentView->canvas()->update();
+}
+
+void NotationSelector::slotHideSelection()
+{
+ if (!m_selectionRect)
+ return ;
+ m_selectionRect->hide();
+ m_selectionRect->setSize(0, 0);
+ m_nParentView->canvas()->update();
+}
+
+void NotationSelector::slotInsertSelected()
+{
+ m_nParentView->slotLastNoteAction();
+}
+
+void NotationSelector::slotEraseSelected()
+{
+ m_parentView->actionCollection()->action("erase")->activate();
+}
+
+void NotationSelector::slotCollapseRestsHard()
+{
+ m_parentView->actionCollection()->action("collapse_rests_aggressively")->activate();
+}
+
+void NotationSelector::slotRespellFlat()
+{
+ m_parentView->actionCollection()->action("respell_flat")->activate();
+}
+
+void NotationSelector::slotRespellSharp()
+{
+ m_parentView->actionCollection()->action("respell_sharp")->activate();
+}
+
+void NotationSelector::slotRespellNatural()
+{
+ m_parentView->actionCollection()->action("respell_natural")->activate();
+}
+
+void NotationSelector::slotCollapseNotes()
+{
+ m_parentView->actionCollection()->action("collapse_notes")->activate();
+}
+
+void NotationSelector::slotInterpret()
+{
+ m_parentView->actionCollection()->action("interpret")->activate();
+}
+
+void NotationSelector::slotStaffAbove()
+{
+ m_parentView->actionCollection()->action("move_events_up_staff")->activate();
+}
+
+void NotationSelector::slotStaffBelow()
+{
+ m_parentView->actionCollection()->action("move_events_down_staff")->activate();
+}
+
+void NotationSelector::slotMakeInvisible()
+{
+ m_parentView->actionCollection()->action("make_invisible")->activate();
+}
+
+void NotationSelector::slotMakeVisible()
+{
+ m_parentView->actionCollection()->action("make_visible")->activate();
+}
+
+void NotationSelector::setViewCurrentSelection(bool preview)
+{
+ EventSelection *selection = getSelection();
+
+ if (m_selectionToMerge) {
+ if (selection &&
+ m_selectionToMerge->getSegment() == selection->getSegment()) {
+ selection->addFromSelection(m_selectionToMerge);
+ } else {
+ return ;
+ }
+ }
+
+ m_nParentView->setCurrentSelection(selection, preview, true);
+}
+
+NotationStaff *
+NotationSelector::getStaffForElement(NotationElement *elt)
+{
+ for (int i = 0; i < m_nParentView->getStaffCount(); ++i) {
+ NotationStaff *staff = m_nParentView->getNotationStaff(i);
+ if (staff->getSegment().findSingle(elt->event()) !=
+ staff->getSegment().end())
+ return staff;
+ }
+ return 0;
+}
+
+EventSelection* NotationSelector::getSelection()
+{
+ // If selection rect is not visible or too small,
+ // return 0
+ //
+ if (!m_selectionRect->visible()) return 0;
+
+ // NOTATION_DEBUG << "Selection x,y: " << m_selectionRect->x() << ","
+ // << m_selectionRect->y() << "; w,h: " << m_selectionRect->width() << "," << m_selectionRect->height() << endl;
+
+ if (m_selectionRect->width() > -3 &&
+ m_selectionRect->width() < 3 &&
+ m_selectionRect->height() > -3 &&
+ m_selectionRect->height() < 3) return 0;
+
+ QCanvasItemList itemList = m_selectionRect->collisions(false);
+ QCanvasItemList::Iterator it;
+
+ QRect rect = m_selectionRect->rect().normalize();
+ QCanvasNotationSprite *sprite = 0;
+
+ if (!m_selectedStaff) {
+
+ // Scan the list of collisions, looking for a valid notation
+ // element; if we find one, initialise m_selectedStaff from it.
+ // If we don't find one, we have no selection. This is a little
+ // inefficient but we only do it for the first event in the
+ // selection.
+
+ for (it = itemList.begin(); it != itemList.end(); ++it) {
+
+ if ((sprite = dynamic_cast<QCanvasNotationSprite*>(*it))) {
+
+ NotationElement &el = sprite->getNotationElement();
+
+ NotationStaff *staff = getStaffForElement(&el);
+ if (!staff) continue;
+
+ int x = (int)(*it)->x();
+ bool shifted = false;
+ int nbw = staff->getNotePixmapFactory(false).getNoteBodyWidth();
+
+
+ // #957364 (Notation: Hard to select upper note in
+ // chords of seconds) -- adjust x-coord for shifted
+ // note head
+ if (el.event()->get<Rosegarden::Bool>
+ (staff->getProperties().NOTE_HEAD_SHIFTED, shifted) && shifted) {
+ x += nbw;
+ }
+
+ if (!rect.contains(x, int((*it)->y()), true)) {
+ // #988217 (Notation: Special column of pixels
+ // prevents sweep selection) -- for notes, test
+ // again with centred x-coord
+ if (!el.isNote() || !rect.contains(x + nbw/2, int((*it)->y()), true)) {
+ continue;
+ }
+ }
+
+ m_selectedStaff = staff;
+ break;
+ }
+ }
+ }
+
+ if (!m_selectedStaff) return 0;
+ Segment& originalSegment = m_selectedStaff->getSegment();
+
+ // If we selected the whole staff, force that to happen explicitly
+ // rather than relying on collisions with the rectangle -- because
+ // events way off the currently visible area might not even have
+ // been drawn yet, and so will not appear in the collision list.
+ // (We did still need the collision list to determine which staff
+ // to use though.)
+
+ if (m_wholeStaffSelectionComplete) {
+ EventSelection *selection = new EventSelection(originalSegment,
+ originalSegment.getStartTime(),
+ originalSegment.getEndMarkerTime());
+ return selection;
+ }
+
+ EventSelection* selection = new EventSelection(originalSegment);
+
+ for (it = itemList.begin(); it != itemList.end(); ++it) {
+
+ if ((sprite = dynamic_cast<QCanvasNotationSprite*>(*it))) {
+
+ NotationElement &el = sprite->getNotationElement();
+
+ int x = (int)(*it)->x();
+ bool shifted = false;
+ int nbw = m_selectedStaff->getNotePixmapFactory(false).getNoteBodyWidth();
+
+ // #957364 (Notation: Hard to select upper note in chords
+ // of seconds) -- adjust x-coord for shifted note head
+ if (el.event()->get<Rosegarden::Bool>
+ (m_selectedStaff->getProperties().NOTE_HEAD_SHIFTED, shifted)
+ && shifted) {
+ x += nbw;
+ }
+
+ // check if the element's rect
+ // is actually included in the selection rect.
+ //
+ if (!rect.contains(x, int((*it)->y()), true)) {
+ // #988217 (Notation: Special column of pixels
+ // prevents sweep selection) -- for notes, test again
+ // with centred x-coord
+ if (!el.isNote() || !rect.contains(x + nbw/2, int((*it)->y()), true)) {
+ continue;
+ }
+ }
+
+ // must be in the same segment as we first started on,
+ // we can't select events across multiple segments
+ if (selection->getSegment().findSingle(el.event()) !=
+ selection->getSegment().end()) {
+ selection->addEvent(el.event());
+ }
+ }
+ }
+
+ if (selection->getAddedEvents() > 0) {
+ return selection;
+ } else {
+ delete selection;
+ return 0;
+ }
+}
+
+const QString NotationSelector::ToolName = "notationselector";
+
+}
+#include "NotationSelector.moc"
diff --git a/src/gui/editors/notation/NotationSelector.h b/src/gui/editors/notation/NotationSelector.h
new file mode 100644
index 0000000..7266fd5
--- /dev/null
+++ b/src/gui/editors/notation/NotationSelector.h
@@ -0,0 +1,197 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTATIONSELECTOR_H_
+#define _RG_NOTATIONSELECTOR_H_
+
+#include "NotationTool.h"
+#include "NotationElement.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+class QCanvasRectangle;
+class m_clickedElement;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class NotationView;
+class NotationStaff;
+class NotationElement;
+class EventSelection;
+class Event;
+
+
+/**
+ * Rectangular note selection
+ */
+class NotationSelector : public NotationTool
+{
+ Q_OBJECT
+
+ friend class NotationToolBox;
+
+public:
+
+ ~NotationSelector();
+
+ virtual void handleLeftButtonPress(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement* el);
+
+ virtual void handleRightButtonPress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement*);
+
+ virtual int handleMouseMove(timeT,
+ int height,
+ QMouseEvent*);
+
+ virtual void handleMouseRelease(timeT time,
+ int height,
+ QMouseEvent*);
+
+ virtual void handleMouseDoubleClick(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement*);
+
+ virtual void handleMouseTripleClick(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement*);
+
+ /**
+ * Create the selection rect
+ *
+ * We need this because NotationView deletes all QCanvasItems
+ * along with it. This happens before the NotationSelector is
+ * deleted, so we can't delete the selection rect in
+ * ~NotationSelector because that leads to double deletion.
+ */
+ virtual void ready();
+
+ /**
+ * Delete the selection rect.
+ */
+ virtual void stow();
+
+ /**
+ * Returns the currently selected events
+ *
+ * The returned result is owned by the caller
+ */
+ EventSelection* getSelection();
+
+ /**
+ * Respond to an event being deleted -- it may be the one the tool
+ * is remembering as the current event.
+ */
+ virtual void handleEventRemoved(Event *event) {
+ if (m_clickedElement && m_clickedElement->event() == event) {
+ m_clickedElement = 0;
+ }
+ }
+
+ static const QString ToolName;
+
+signals:
+ void editElement(NotationStaff *, NotationElement *, bool advanced);
+
+public slots:
+ /**
+ * Hide the selection rectangle
+ *
+ * Should be called after a cut or a copy has been
+ * performed
+ */
+ void slotHideSelection();
+
+ void slotInsertSelected();
+ void slotEraseSelected();
+// void slotCollapseRests();
+ void slotCollapseRestsHard();
+ void slotRespellFlat();
+ void slotRespellSharp();
+ void slotRespellNatural();
+ void slotCollapseNotes();
+ void slotInterpret();
+ void slotStaffAbove();
+ void slotStaffBelow();
+ void slotMakeInvisible();
+ void slotMakeVisible();
+
+ void slotClickTimeout();
+
+protected:
+ NotationSelector(NotationView*);
+
+ /**
+ * Set the current selection on the parent NotationView
+ */
+ void setViewCurrentSelection(bool preview);
+
+ /**
+ * Look up the staff containing the given notation element
+ */
+ NotationStaff *getStaffForElement(NotationElement *elt);
+
+ void drag(int x, int y, bool final);
+ void dragFine(int x, int y, bool final);
+
+ //--------------- Data members ---------------------------------
+
+ QCanvasRectangle* m_selectionRect;
+ bool m_updateRect;
+
+ NotationStaff *m_selectedStaff;
+ NotationElement *m_clickedElement;
+ bool m_clickedShift;
+ bool m_startedFineDrag;
+
+ EventSelection *m_selectionToMerge;
+
+ long m_lastDragPitch;
+ timeT m_lastDragTime;
+
+ bool m_justSelectedBar;
+ bool m_wholeStaffSelectionComplete;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationStaff.cpp b/src/gui/editors/notation/NotationStaff.cpp
new file mode 100644
index 0000000..c5219b4
--- /dev/null
+++ b/src/gui/editors/notation/NotationStaff.cpp
@@ -0,0 +1,2300 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NotationStaff.h"
+#include "misc/Debug.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/Composition.h"
+#include "base/Device.h"
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/MidiTypes.h"
+#include "base/NotationQuantizer.h"
+#include "base/NotationRules.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/SnapGrid.h"
+#include "base/Staff.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "base/ViewElement.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/editors/guitar/Chord.h"
+#include "gui/general/LinedStaff.h"
+#include "gui/general/PixmapFunctions.h"
+#include "gui/general/ProgressReporter.h"
+#include "gui/kdeext/QCanvasSimpleSprite.h"
+#include "NotationChord.h"
+#include "NotationElement.h"
+#include "NotationProperties.h"
+#include "NotationView.h"
+#include "NoteFontFactory.h"
+#include "NotePixmapFactory.h"
+#include "NotePixmapParameters.h"
+#include "NoteStyleFactory.h"
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <qcanvas.h>
+#include <qpainter.h>
+#include <qpoint.h>
+#include <qrect.h>
+
+
+namespace Rosegarden
+{
+
+NotationStaff::NotationStaff(QCanvas *canvas, Segment *segment,
+ SnapGrid *snapGrid, int id,
+ NotationView *view,
+ std::string fontName, int resolution) :
+ ProgressReporter(0),
+ LinedStaff(canvas, segment, snapGrid, id, resolution,
+ resolution / 16 + 1, // line thickness
+ LinearMode, 0, 0, // pageMode, pageWidth and pageHeight set later
+ 0 // row spacing
+ ),
+ m_notePixmapFactory(0),
+ m_graceNotePixmapFactory(0),
+ m_previewSprite(0),
+ m_staffName(0),
+ m_notationView(view),
+ m_legerLineCount(8),
+ m_barNumbersEvery(0),
+ m_colourQuantize(true),
+ m_showUnknowns(true),
+ m_showRanges(true),
+ m_showCollisions(true),
+ m_printPainter(0),
+ m_ready(false),
+ m_lastRenderedBar(0)
+{
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+ m_colourQuantize = config->readBoolEntry("colourquantize", false);
+
+ // Shouldn't change these during the lifetime of the staff, really:
+ m_showUnknowns = config->readBoolEntry("showunknowns", false);
+ m_showRanges = config->readBoolEntry("showranges", true);
+ m_showCollisions = config->readBoolEntry("showcollisions", true);
+
+ m_keySigCancelMode = config->readNumEntry("keysigcancelmode", 1);
+
+ changeFont(fontName, resolution);
+}
+
+NotationStaff::~NotationStaff()
+{
+ deleteTimeSignatures();
+ delete m_notePixmapFactory;
+ delete m_graceNotePixmapFactory;
+}
+
+void
+NotationStaff::changeFont(std::string fontName, int size)
+{
+ setResolution(size);
+
+ delete m_notePixmapFactory;
+ m_notePixmapFactory = new NotePixmapFactory(fontName, size);
+
+ std::vector<int> sizes = NoteFontFactory::getScreenSizes(fontName);
+ int graceSize = size;
+ for (unsigned int i = 0; i < sizes.size(); ++i) {
+ if (sizes[i] == size || sizes[i] > size*3 / 4)
+ break;
+ graceSize = sizes[i];
+ }
+ delete m_graceNotePixmapFactory;
+ m_graceNotePixmapFactory = new NotePixmapFactory(fontName, graceSize);
+
+ setLineThickness(m_notePixmapFactory->getStaffLineThickness());
+}
+
+void
+NotationStaff::insertTimeSignature(double layoutX,
+ const TimeSignature &timeSig)
+{
+ if (timeSig.isHidden())
+ return ;
+
+ m_notePixmapFactory->setSelected(false);
+ QCanvasPixmap *pixmap = m_notePixmapFactory->makeTimeSigPixmap(timeSig);
+ QCanvasTimeSigSprite *sprite =
+ new QCanvasTimeSigSprite(layoutX, pixmap, m_canvas);
+
+ LinedStaffCoords sigCoords =
+ getCanvasCoordsForLayoutCoords(layoutX, getLayoutYForHeight(4));
+
+ sprite->move(sigCoords.first, (double)sigCoords.second);
+ sprite->show();
+ m_timeSigs.insert(sprite);
+}
+
+void
+NotationStaff::deleteTimeSignatures()
+{
+ // NOTATION_DEBUG << "NotationStaff::deleteTimeSignatures()" << endl;
+
+ for (SpriteSet::iterator i = m_timeSigs.begin();
+ i != m_timeSigs.end(); ++i) {
+ delete *i;
+ }
+
+ m_timeSigs.clear();
+}
+
+void
+NotationStaff::insertRepeatedClefAndKey(double layoutX, int barNo)
+{
+ bool needClef = false, needKey = false;
+ timeT t;
+
+ timeT barStart = getSegment().getComposition()->getBarStart(barNo);
+
+ Clef clef = getSegment().getClefAtTime(barStart, t);
+ if (t < barStart)
+ needClef = true;
+
+ ::Rosegarden::Key key = getSegment().getKeyAtTime(barStart, t);
+ if (t < barStart)
+ needKey = true;
+
+ double dx = m_notePixmapFactory->getBarMargin() / 2;
+
+ if (!m_notationView->isInPrintMode())
+ m_notePixmapFactory->setShaded(true);
+
+ if (needClef) {
+
+ int layoutY = getLayoutYForHeight(clef.getAxisHeight());
+
+ LinedStaffCoords coords =
+ getCanvasCoordsForLayoutCoords(layoutX + dx, layoutY);
+
+ QCanvasPixmap *pixmap = m_notePixmapFactory->makeClefPixmap(clef);
+
+ QCanvasNonElementSprite *sprite =
+ new QCanvasNonElementSprite(pixmap, m_canvas);
+
+ sprite->move(coords.first, coords.second);
+ sprite->show();
+ m_repeatedClefsAndKeys.insert(sprite);
+
+ dx += pixmap->width() + m_notePixmapFactory->getNoteBodyWidth() * 2 / 3;
+ }
+
+ if (needKey) {
+
+ int layoutY = getLayoutYForHeight(12);
+
+ LinedStaffCoords coords =
+ getCanvasCoordsForLayoutCoords(layoutX + dx, layoutY);
+
+ QCanvasPixmap *pixmap = m_notePixmapFactory->makeKeyPixmap(key, clef);
+
+ QCanvasNonElementSprite *sprite =
+ new QCanvasNonElementSprite(pixmap, m_canvas);
+
+ sprite->move(coords.first, coords.second);
+ sprite->show();
+ m_repeatedClefsAndKeys.insert(sprite);
+
+ dx += pixmap->width();
+ }
+
+ /* attempt to blot out things like slurs & ties that overrun this area: doesn't work
+
+ if (m_notationView->isInPrintMode() && (needClef || needKey)) {
+
+ int layoutY = getLayoutYForHeight(14);
+ int h = getLayoutYForHeight(-8) - layoutY;
+
+ LinedStaffCoords coords =
+ getCanvasCoordsForLayoutCoords(layoutX, layoutY);
+
+ QCanvasRectangle *rect = new QCanvasRectangle(coords.first, coords.second,
+ dx, h, m_canvas);
+ rect->setPen(Qt::black);
+ rect->setBrush(Qt::white);
+ rect->setZ(1);
+ rect->show();
+
+ m_repeatedClefsAndKeys.insert(rect);
+ }
+ */
+
+ m_notePixmapFactory->setShaded(false);
+}
+
+void
+NotationStaff::deleteRepeatedClefsAndKeys()
+{
+ for (ItemSet::iterator i = m_repeatedClefsAndKeys.begin();
+ i != m_repeatedClefsAndKeys.end(); ++i) {
+ delete *i;
+ }
+
+ m_repeatedClefsAndKeys.clear();
+}
+
+void
+NotationStaff::drawStaffName()
+{
+ delete m_staffName;
+
+ m_staffNameText =
+ getSegment().getComposition()->
+ getTrackById(getSegment().getTrack())->getLabel();
+
+ QCanvasPixmap *map =
+ m_notePixmapFactory->makeTextPixmap
+ (Text(m_staffNameText, Text::StaffName));
+
+ m_staffName = new QCanvasStaffNameSprite(map, m_canvas);
+
+ int layoutY = getLayoutYForHeight(3);
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords(0, layoutY);
+ m_staffName->move(getX() + getMargin() + m_notePixmapFactory->getNoteBodyWidth(),
+ coords.second - map->height() / 2);
+ m_staffName->show();
+}
+
+bool
+NotationStaff::isStaffNameUpToDate()
+{
+ return (m_staffNameText ==
+ getSegment().getComposition()->
+ getTrackById(getSegment().getTrack())->getLabel());
+}
+
+timeT
+NotationStaff::getTimeAtCanvasCoords(double cx, int cy) const
+{
+ LinedStaffCoords layoutCoords = getLayoutCoordsForCanvasCoords(cx, cy);
+ RulerScale * rs = m_notationView->getHLayout();
+ return rs->getTimeForX(layoutCoords.first);
+}
+
+void
+NotationStaff::getClefAndKeyAtCanvasCoords(double cx, int cy,
+ Clef &clef,
+ ::Rosegarden::Key &key) const
+{
+ LinedStaffCoords layoutCoords = getLayoutCoordsForCanvasCoords(cx, cy);
+ int i;
+
+ for (i = 0; i < m_clefChanges.size(); ++i) {
+ if (m_clefChanges[i].first > layoutCoords.first)
+ break;
+ clef = m_clefChanges[i].second;
+ }
+
+ for (i = 0; i < m_keyChanges.size(); ++i) {
+ if (m_keyChanges[i].first > layoutCoords.first)
+ break;
+ key = m_keyChanges[i].second;
+ }
+}
+
+ViewElementList::iterator
+NotationStaff::getClosestElementToLayoutX(double x,
+ Event *&clef,
+ Event *&key,
+ bool notesAndRestsOnly,
+ int proximityThreshold)
+{
+ START_TIMING;
+
+ double minDist = 10e9, prevDist = 10e9;
+
+ NotationElementList *notes = getViewElementList();
+ NotationElementList::iterator it, result;
+
+ // TODO: this is grossly inefficient
+
+ for (it = notes->begin(); it != notes->end(); ++it) {
+ NotationElement *el = static_cast<NotationElement*>(*it);
+
+ bool before = ((*it)->getLayoutX() < x);
+
+ if (!el->isNote() && !el->isRest()) {
+ if (before) {
+ if ((*it)->event()->isa(Clef::EventType)) {
+ clef = (*it)->event();
+ } else if ((*it)->event()->isa(::Rosegarden::Key::EventType)) {
+ key = (*it)->event();
+ }
+ }
+ if (notesAndRestsOnly)
+ continue;
+ }
+
+ double dx = x - (*it)->getLayoutX();
+ if (dx < 0)
+ dx = -dx;
+
+ if (dx < minDist) {
+ minDist = dx;
+ result = it;
+ } else if (!before) {
+ break;
+ }
+
+ prevDist = dx;
+ }
+
+ if (proximityThreshold > 0 && minDist > proximityThreshold) {
+ NOTATION_DEBUG << "NotationStaff::getClosestElementToLayoutX() : element is too far away : "
+ << minDist << endl;
+ return notes->end();
+ }
+
+ NOTATION_DEBUG << "NotationStaff::getClosestElementToLayoutX: found element at layout " << (*result)->getLayoutX() << " - we're at layout " << x << endl;
+
+ PRINT_ELAPSED("NotationStaff::getClosestElementToLayoutX");
+
+ return result;
+}
+
+ViewElementList::iterator
+NotationStaff::getElementUnderLayoutX(double x,
+ Event *&clef,
+ Event *&key)
+{
+ NotationElementList *notes = getViewElementList();
+ NotationElementList::iterator it;
+
+ // TODO: this is grossly inefficient
+
+ for (it = notes->begin(); it != notes->end(); ++it) {
+ NotationElement* el = static_cast<NotationElement*>(*it);
+
+ bool before = ((*it)->getLayoutX() <= x);
+
+ if (!el->isNote() && !el->isRest()) {
+ if (before) {
+ if ((*it)->event()->isa(Clef::EventType)) {
+ clef = (*it)->event();
+ } else if ((*it)->event()->isa(::Rosegarden::Key::EventType)) {
+ key = (*it)->event();
+ }
+ }
+ }
+
+ double airX, airWidth;
+ el->getLayoutAirspace(airX, airWidth);
+ if (x >= airX && x < airX + airWidth) {
+ return it;
+ } else if (!before) {
+ if (it != notes->begin())
+ --it;
+ return it;
+ }
+ }
+
+ return notes->end();
+}
+
+std::string
+NotationStaff::getNoteNameAtCanvasCoords(double x, int y,
+ Accidental) const
+{
+ Clef clef;
+ ::Rosegarden::Key key;
+ getClefAndKeyAtCanvasCoords(x, y, clef, key);
+
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ int baseOctave = config->readNumEntry("midipitchoctave", -2);
+
+ Pitch p(getHeightAtCanvasCoords(x, y), clef, key);
+ //!!! i18n() how?
+ return p.getAsString(key.isSharp(), true, baseOctave);
+}
+
+void
+NotationStaff::renderElements(NotationElementList::iterator from,
+ NotationElementList::iterator to)
+{
+ // NOTATION_DEBUG << "NotationStaff " << this << "::renderElements()" << endl;
+ Profiler profiler("NotationStaff::renderElements");
+
+ emit setOperationName(i18n("Rendering staff %1...").arg(getId() + 1));
+ emit setProgress(0);
+
+ throwIfCancelled();
+
+ // These are only used when rendering keys, and we don't have the
+ // right start data here so we choose not to render keys at all in
+ // this method (see below) so that we can pass bogus clef and key
+ // data to renderSingleElement
+ Clef currentClef;
+ ::Rosegarden::Key currentKey;
+
+ int elementCount = 0;
+ timeT endTime =
+ (to != getViewElementList()->end() ? (*to)->getViewAbsoluteTime() :
+ getSegment().getEndMarkerTime());
+ timeT startTime = (from != to ? (*from)->getViewAbsoluteTime() : endTime);
+
+ for (NotationElementList::iterator it = from, nextIt = from;
+ it != to; it = nextIt) {
+
+ ++nextIt;
+
+ if (isDirectlyPrintable(*it)) {
+ // notes are renderable direct to the printer, so don't render
+ // them to the canvas here
+ continue;
+ }
+
+ if ((*it)->event()->isa(::Rosegarden::Key::EventType)) {
+ // force rendering in positionElements instead
+ NotationElement* el = static_cast<NotationElement*>(*it);
+ el->removeCanvasItem();
+ continue;
+ }
+
+ bool selected = isSelected(it);
+ // NOTATION_DEBUG << "Rendering at " << (*it)->getAbsoluteTime()
+ // << " (selected = " << selected << ")" << endl;
+
+ renderSingleElement(it, currentClef, currentKey, selected);
+
+ if ((endTime > startTime) &&
+ (++elementCount % 200 == 0)) {
+
+ timeT myTime = (*it)->getViewAbsoluteTime();
+ emit setProgress((myTime - startTime) * 100 / (endTime - startTime));
+ throwIfCancelled();
+ }
+ }
+
+ // NOTATION_DEBUG << "NotationStaff " << this << "::renderElements: "
+ // << elementCount << " elements rendered" << endl;
+}
+
+void
+NotationStaff::renderPrintable(timeT from, timeT to)
+{
+ if (!m_printPainter)
+ return ;
+
+ Profiler profiler("NotationStaff::renderElements");
+
+ emit setOperationName(i18n("Rendering notes on staff %1...").arg(getId() + 1));
+ emit setProgress(0);
+
+ throwIfCancelled();
+
+ // These are only used when rendering keys, and we don't do that
+ // here, so we don't care what they are
+ Clef currentClef;
+ ::Rosegarden::Key currentKey;
+
+ Composition *composition = getSegment().getComposition();
+ NotationElementList::iterator beginAt =
+ getViewElementList()->findTime(composition->getBarStartForTime(from));
+ NotationElementList::iterator endAt =
+ getViewElementList()->findTime(composition->getBarEndForTime(to));
+
+ int elementCount = 0;
+
+ for (NotationElementList::iterator it = beginAt, nextIt = beginAt;
+ it != endAt; it = nextIt) {
+
+ ++nextIt;
+
+ if (!isDirectlyPrintable(*it)) {
+ continue;
+ }
+
+ bool selected = isSelected(it);
+ // NOTATION_DEBUG << "Rendering at " << (*it)->getAbsoluteTime()
+ // << " (selected = " << selected << ")" << endl;
+
+ renderSingleElement(it, currentClef, currentKey, selected);
+
+ if ((to > from) && (++elementCount % 200 == 0)) {
+
+ timeT myTime = (*it)->getViewAbsoluteTime();
+ emit setProgress((myTime - from) * 100 / (to - from));
+ throwIfCancelled();
+ }
+ }
+
+ // NOTATION_DEBUG << "NotationStaff " << this << "::renderElements: "
+ // << elementCount << " elements rendered" << endl;
+}
+
+const NotationProperties &
+NotationStaff::getProperties() const
+{
+ return m_notationView->getProperties();
+}
+
+void
+NotationStaff::positionElements(timeT from, timeT to)
+{
+ // NOTATION_DEBUG << "NotationStaff " << this << "::positionElements()"
+ // << from << " -> " << to << endl;
+ Profiler profiler("NotationStaff::positionElements");
+
+ // Following 4 lines are a workaround to not have m_clefChanges and
+ // m_keyChanges truncated when positionElements() is called with
+ // args outside current segment.
+ // Maybe a better fix would be not to call positionElements() with
+ // such args ...
+ int startTime = getSegment().getStartTime();
+ if (from < startTime) from = startTime;
+ if (to < startTime) to = startTime;
+ if (to == from) return;
+
+ emit setOperationName(i18n("Positioning staff %1...").arg(getId() + 1));
+ emit setProgress(0);
+ throwIfCancelled();
+
+ const NotationProperties &properties(getProperties());
+
+ int elementsPositioned = 0;
+ int elementsRendered = 0; // diagnostic
+
+ Composition *composition = getSegment().getComposition();
+
+ timeT nextBarTime = composition->getBarEndForTime(to);
+
+ NotationElementList::iterator beginAt =
+ getViewElementList()->findTime(composition->getBarStartForTime(from));
+
+ NotationElementList::iterator endAt =
+ getViewElementList()->findTime(composition->getBarEndForTime(to));
+
+ if (beginAt == getViewElementList()->end())
+ return ;
+
+ truncateClefsAndKeysAt(static_cast<int>((*beginAt)->getLayoutX()));
+
+ Clef currentClef; // used for rendering key sigs
+ bool haveCurrentClef = false;
+
+ ::Rosegarden::Key currentKey;
+ bool haveCurrentKey = false;
+
+ for (NotationElementList::iterator it = beginAt, nextIt = beginAt;
+ it != endAt; it = nextIt) {
+
+ NotationElement * el = static_cast<NotationElement*>(*it);
+
+ ++nextIt;
+
+ if (el->event()->isa(Clef::EventType)) {
+
+ currentClef = Clef(*el->event());
+ m_clefChanges.push_back(ClefChange(int(el->getLayoutX()),
+ currentClef));
+ haveCurrentClef = true;
+
+ } else if (el->event()->isa(::Rosegarden::Key::EventType)) {
+
+ m_keyChanges.push_back
+ (KeyChange(int(el->getLayoutX()),
+ ::Rosegarden::Key(*el->event())));
+
+ if (!haveCurrentClef) { // need this to know how to present the key
+ currentClef = getSegment().getClefAtTime
+ (el->event()->getAbsoluteTime());
+ haveCurrentClef = true;
+ }
+
+ if (!haveCurrentKey) { // stores the key _before_ this one
+ currentKey = getSegment().getKeyAtTime
+ (el->event()->getAbsoluteTime() - 1);
+ haveCurrentKey = true;
+ }
+
+ } else if (isDirectlyPrintable(el)) {
+ // these are rendered by renderPrintable for printing
+ continue;
+ }
+
+ bool selected = isSelected(it);
+ bool needNewSprite = (selected != el->isSelected());
+
+ if (!el->getCanvasItem()) {
+
+ needNewSprite = true;
+
+ } else if (el->isNote() && !el->isRecentlyRegenerated()) {
+
+ // If the note's y-coordinate has changed, we should
+ // redraw it -- its stem direction may have changed, or it
+ // may need leger lines. This will happen e.g. if the
+ // user inserts a new clef; unfortunately this means
+ // inserting clefs is rather slow.
+
+ needNewSprite = needNewSprite || !elementNotMovedInY(el);
+
+ if (!needNewSprite) {
+
+ // If the event is a beamed or tied-forward note, then
+ // we might need a new sprite if the distance from
+ // this note to the next has changed (because the beam
+ // or tie is part of the note's sprite).
+
+ bool spanning = false;
+ (void)(el->event()->get
+ <Bool>
+ (properties.BEAMED, spanning));
+ if (!spanning) {
+ (void)(el->event()->get
+ <Bool>(BaseProperties::TIED_FORWARD, spanning));
+ }
+
+ if (spanning) {
+ needNewSprite =
+ (el->getViewAbsoluteTime() < nextBarTime ||
+ !elementShiftedOnly(it));
+ }
+ }
+
+ } else if (el->event()->isa(Indication::EventType) &&
+ !el->isRecentlyRegenerated()) {
+ needNewSprite = true;
+ }
+
+ if (needNewSprite) {
+ renderSingleElement(it, currentClef, currentKey, selected);
+ ++elementsRendered;
+ }
+
+ if (el->event()->isa(::Rosegarden::Key::EventType)) {
+ // update currentKey after rendering, not before
+ currentKey = ::Rosegarden::Key(*el->event());
+ }
+
+ if (!needNewSprite) {
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords
+ (el->getLayoutX(), (int)el->getLayoutY());
+ el->reposition(coords.first, (double)coords.second);
+ }
+
+ el->setSelected(selected);
+
+ if ((to > from) &&
+ (++elementsPositioned % 300 == 0)) {
+ timeT myTime = el->getViewAbsoluteTime();
+ emit setProgress((myTime - from) * 100 / (to - from));
+ throwIfCancelled();
+ }
+ }
+
+ // NOTATION_DEBUG << "NotationStaff " << this << "::positionElements "
+ // << from << " -> " << to << ": "
+ // << elementsPositioned << " elements positioned, "
+ // << elementsRendered << " re-rendered"
+ // << endl;
+
+ // NotePixmapFactory::dumpStats(std::cerr);
+}
+
+void
+NotationStaff::truncateClefsAndKeysAt(int x)
+{
+ for (FastVector<ClefChange>::iterator i = m_clefChanges.begin();
+ i != m_clefChanges.end(); ++i) {
+ if (i->first >= x) {
+ m_clefChanges.erase(i, m_clefChanges.end());
+ break;
+ }
+ }
+
+ for (FastVector<KeyChange>::iterator i = m_keyChanges.begin();
+ i != m_keyChanges.end(); ++i) {
+ if (i->first >= x) {
+ m_keyChanges.erase(i, m_keyChanges.end());
+ break;
+ }
+ }
+}
+
+NotationElementList::iterator
+NotationStaff::findUnchangedBarStart(timeT from)
+{
+ NotationElementList *nel = (NotationElementList *)getViewElementList();
+
+ // Track back bar-by-bar until we find one whose start position
+ // hasn't changed
+
+ NotationElementList::iterator beginAt = nel->begin();
+ do {
+ from = getSegment().getComposition()->getBarStartForTime(from - 1);
+ beginAt = nel->findTime(from);
+ } while (beginAt != nel->begin() &&
+ (beginAt == nel->end() || !elementNotMoved(static_cast<NotationElement*>(*beginAt))));
+
+ return beginAt;
+}
+
+NotationElementList::iterator
+NotationStaff::findUnchangedBarEnd(timeT to)
+{
+ NotationElementList *nel = (NotationElementList *)getViewElementList();
+
+ // Track forward to the end, similarly. Here however it's very
+ // common for all the positions to have changed right up to the
+ // end of the piece; so we save time by assuming that to be the
+ // case if we get more than (arbitrary) 3 changed bars.
+
+ // We also record the start of the bar following the changed
+ // section, for later use.
+
+ NotationElementList::iterator endAt = nel->end();
+
+ int changedBarCount = 0;
+ NotationElementList::iterator candidate = nel->end();
+ do {
+ candidate = nel->findTime(getSegment().getBarEndForTime(to));
+ if (candidate != nel->end()) {
+ to = (*candidate)->getViewAbsoluteTime();
+ }
+ ++changedBarCount;
+ } while (changedBarCount < 4 &&
+ candidate != nel->end() &&
+ !elementNotMoved(static_cast<NotationElement*>(*candidate)));
+
+ if (changedBarCount < 4)
+ return candidate;
+ else
+ return endAt;
+}
+
+bool
+NotationStaff::elementNotMoved(NotationElement *elt)
+{
+ if (!elt->getCanvasItem())
+ return false;
+
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords
+ (elt->getLayoutX(), (int)elt->getLayoutY());
+
+ bool ok =
+ (int)(elt->getCanvasX()) == (int)(coords.first) &&
+ (int)(elt->getCanvasY()) == (int)(coords.second);
+
+ if (!ok) {
+ NOTATION_DEBUG
+ << "elementNotMoved: elt at " << elt->getViewAbsoluteTime() <<
+ ", ok is " << ok << endl;
+ NOTATION_DEBUG << "(cf " << (int)(elt->getCanvasX()) << " vs "
+ << (int)(coords.first) << ", "
+ << (int)(elt->getCanvasY()) << " vs "
+ << (int)(coords.second) << ")" << endl;
+ } else {
+ NOTATION_DEBUG << "elementNotMoved: elt at " << elt->getViewAbsoluteTime()
+ << " is ok" << endl;
+ }
+
+ return ok;
+}
+
+bool
+NotationStaff::elementNotMovedInY(NotationElement *elt)
+{
+ if (!elt->getCanvasItem())
+ return false;
+
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords
+ (elt->getLayoutX(), (int)elt->getLayoutY());
+
+ bool ok = (int)(elt->getCanvasY()) == (int)(coords.second);
+
+ // if (!ok) {
+ // NOTATION_DEBUG
+ // << "elementNotMovedInY: elt at " << elt->getAbsoluteTime() <<
+ // ", ok is " << ok << endl;
+ // NOTATION_DEBUG << "(cf " << (int)(elt->getCanvasY()) << " vs "
+ // << (int)(coords.second) << ")" << std::endl;
+ // }
+ return ok;
+}
+
+bool
+NotationStaff::elementShiftedOnly(NotationElementList::iterator i)
+{
+ int shift = 0;
+ bool ok = false;
+
+ for (NotationElementList::iterator j = i;
+ j != getViewElementList()->end(); ++j) {
+
+ NotationElement *elt = static_cast<NotationElement*>(*j);
+ if (!elt->getCanvasItem())
+ break;
+
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords
+ (elt->getLayoutX(), (int)elt->getLayoutY());
+
+ // regard any shift in y as suspicious
+ if ((int)(elt->getCanvasY()) != (int)(coords.second))
+ break;
+
+ int myShift = (int)(elt->getCanvasX()) - (int)(coords.first);
+ if (j == i)
+ shift = myShift;
+ else if (myShift != shift)
+ break;
+
+ if (elt->getViewAbsoluteTime() > (*i)->getViewAbsoluteTime()) {
+ // all events up to and including this one have passed
+ ok = true;
+ break;
+ }
+ }
+
+ if (!ok) {
+ NOTATION_DEBUG
+ << "elementShiftedOnly: elt at " << (*i)->getViewAbsoluteTime()
+ << ", ok is " << ok << endl;
+ }
+
+ return ok;
+}
+
+bool
+NotationStaff::isDirectlyPrintable(ViewElement *velt)
+{
+ if (!m_printPainter)
+ return false;
+ return (velt->event()->isa(Note::EventType) ||
+ velt->event()->isa(Note::EventRestType) ||
+ velt->event()->isa(Text::EventType) ||
+ velt->event()->isa(Indication::EventType));
+}
+
+void
+NotationStaff::renderSingleElement(ViewElementList::iterator &vli,
+ const Clef &currentClef,
+ const ::Rosegarden::Key &currentKey,
+ bool selected)
+{
+ const NotationProperties &properties(getProperties());
+ static NotePixmapParameters restParams(Note::Crotchet, 0);
+
+ NotationElement* elt = static_cast<NotationElement*>(*vli);
+
+ bool invisible = false;
+ if (elt->event()->get
+ <Bool>(BaseProperties::INVISIBLE, invisible) && invisible) {
+ if (m_printPainter)
+ return ;
+ KConfig *config = kapp->config();
+ config->setGroup("Notation Options");
+ bool showInvisibles = config->readBoolEntry("showinvisibles", true);
+ if (!showInvisibles)
+ return ;
+ }
+
+ try {
+ m_notePixmapFactory->setNoteStyle
+ (NoteStyleFactory::getStyleForEvent(elt->event()));
+
+ } catch (NoteStyleFactory::StyleUnavailable u) {
+
+ std::cerr << "WARNING: Note style unavailable: "
+ << u.getMessage() << std::endl;
+
+ static bool warned = false;
+ if (!warned) {
+ KMessageBox::error(0, i18n(strtoqstr(u.getMessage())));
+ warned = true;
+ }
+ }
+
+ try {
+
+ QCanvasPixmap *pixmap = 0;
+
+ m_notePixmapFactory->setSelected(selected);
+ m_notePixmapFactory->setShaded(invisible);
+ int z = selected ? 3 : 0;
+
+ // these are actually only used for the printer stuff
+ LinedStaffCoords coords;
+ if (m_printPainter)
+ coords = getCanvasCoordsForLayoutCoords
+ (elt->getLayoutX(), (int)elt->getLayoutY());
+
+ FitPolicy policy = PretendItFittedAllAlong;
+
+ if (elt->isNote()) {
+
+ renderNote(vli);
+
+ } else if (elt->isRest()) {
+
+ bool ignoreRest = false;
+ // NotationHLayout sets this property if it finds the rest
+ // in the middle of a chord -- Quantizer still sometimes gets
+ // this wrong
+ elt->event()->get
+ <Bool>(properties.REST_TOO_SHORT, ignoreRest);
+
+ if (!ignoreRest) {
+
+ Note::Type note = elt->event()->get
+ <Int>(BaseProperties::NOTE_TYPE);
+ int dots = elt->event()->get
+ <Int>(BaseProperties::NOTE_DOTS);
+ restParams.setNoteType(note);
+ restParams.setDots(dots);
+ setTuplingParameters(elt, restParams);
+ restParams.setQuantized(false);
+ bool restOutside = false;
+ elt->event()->get
+ <Bool>(properties.REST_OUTSIDE_STAVE,
+ restOutside);
+ restParams.setRestOutside(restOutside);
+ if (restOutside) {
+ NOTATION_DEBUG << "NotationStaff::renderSingleElement() : rest outside staff" << endl;
+ if (note == Note::DoubleWholeNote) {
+ NOTATION_DEBUG << "NotationStaff::renderSingleElement() : breve rest needs leger lines" << endl;
+ restParams.setLegerLines(5);
+ }
+ }
+
+ if (m_printPainter) {
+ m_notePixmapFactory->drawRest
+ (restParams,
+ *m_printPainter, int(coords.first), coords.second);
+ } else {
+ pixmap = m_notePixmapFactory->makeRestPixmap(restParams);
+ }
+ }
+
+ } else if (elt->event()->isa(Clef::EventType)) {
+
+ pixmap = m_notePixmapFactory->makeClefPixmap
+ (Clef(*elt->event()));
+
+ } else if (elt->event()->isa(::Rosegarden::Key::EventType)) {
+
+ ::Rosegarden::Key key(*elt->event());
+ ::Rosegarden::Key cancelKey = currentKey;
+
+ if (m_keySigCancelMode == 0) { // only when entering C maj / A min
+
+ if (key.getAccidentalCount() != 0)
+ cancelKey = ::Rosegarden::Key();
+
+ } else if (m_keySigCancelMode == 1) { // only when reducing acc count
+
+ if (!(key.isSharp() == cancelKey.isSharp() &&
+ key.getAccidentalCount() < cancelKey.getAccidentalCount())) {
+ cancelKey = ::Rosegarden::Key();
+ }
+ }
+
+ pixmap = m_notePixmapFactory->makeKeyPixmap
+ (key, currentClef, cancelKey);
+
+ } else if (elt->event()->isa(Text::EventType)) {
+
+ policy = MoveBackToFit;
+
+ if (elt->event()->has(Text::TextTypePropertyName) &&
+ elt->event()->get
+ <String>
+ (Text::TextTypePropertyName) ==
+ Text::Annotation &&
+ !m_notationView->areAnnotationsVisible()) {
+
+ // nothing I guess
+
+ }
+ else if (elt->event()->has(Text::TextTypePropertyName) &&
+ elt->event()->get
+ <String>
+ (Text::TextTypePropertyName) ==
+ Text::LilyPondDirective &&
+ !m_notationView->areLilyPondDirectivesVisible()) {
+
+ // nothing here either
+
+ }
+ else {
+
+ try {
+ if (m_printPainter) {
+ Text text(*elt->event());
+ int length = m_notePixmapFactory->getTextWidth(text);
+ for (double w = -1, inc = 0; w != 0; inc += w) {
+ w = setPainterClipping(m_printPainter,
+ elt->getLayoutX(),
+ int(elt->getLayoutY()),
+ int(inc), length, coords,
+ policy);
+ m_notePixmapFactory->drawText
+ (text, *m_printPainter, int(coords.first), coords.second);
+ m_printPainter->restore();
+ }
+ } else {
+ pixmap = m_notePixmapFactory->makeTextPixmap
+ (Text(*elt->event()));
+ }
+ } catch (Exception e) { // Text ctor failed
+ NOTATION_DEBUG << "Bad text event" << endl;
+ }
+ }
+
+ } else if (elt->event()->isa(Indication::EventType)) {
+
+ policy = SplitToFit;
+
+ try {
+ Indication indication(*elt->event());
+
+ timeT indicationDuration = indication.getIndicationDuration();
+ timeT indicationEndTime =
+ elt->getViewAbsoluteTime() + indicationDuration;
+
+ NotationElementList::iterator indicationEnd =
+ getViewElementList()->findTime(indicationEndTime);
+
+ std::string indicationType = indication.getIndicationType();
+
+ int length, y1;
+
+ if ((indicationType == Indication::Slur ||
+ indicationType == Indication::PhrasingSlur) &&
+ indicationEnd != getViewElementList()->begin()) {
+ --indicationEnd;
+ }
+
+ if ((indicationType != Indication::Slur &&
+ indicationType != Indication::PhrasingSlur) &&
+ indicationEnd != getViewElementList()->begin() &&
+ (indicationEnd == getViewElementList()->end() ||
+ indicationEndTime ==
+ getSegment().getBarStartForTime(indicationEndTime))) {
+
+ while (indicationEnd == getViewElementList()->end() ||
+ (*indicationEnd)->getViewAbsoluteTime() >= indicationEndTime)
+ --indicationEnd;
+
+ double x, w;
+ static_cast<NotationElement *>(*indicationEnd)->
+ getLayoutAirspace(x, w);
+ length = (int)(x + w - elt->getLayoutX() -
+ m_notePixmapFactory->getBarMargin());
+
+ } else {
+
+ length = (int)((*indicationEnd)->getLayoutX() -
+ elt->getLayoutX());
+
+ if (indication.isOttavaType()) {
+ length -= m_notePixmapFactory->getNoteBodyWidth();
+ }
+ }
+
+ y1 = (int)(*indicationEnd)->getLayoutY();
+
+ if (length < m_notePixmapFactory->getNoteBodyWidth()) {
+ length = m_notePixmapFactory->getNoteBodyWidth();
+ }
+
+ if (indicationType == Indication::Crescendo ||
+ indicationType == Indication::Decrescendo) {
+
+ if (m_printPainter) {
+ for (double w = -1, inc = 0; w != 0; inc += w) {
+ w = setPainterClipping(m_printPainter,
+ elt->getLayoutX(),
+ int(elt->getLayoutY()),
+ int(inc), length, coords,
+ policy);
+ m_notePixmapFactory->drawHairpin
+ (length, indicationType == Indication::Crescendo,
+ *m_printPainter, int(coords.first), coords.second);
+ m_printPainter->restore();
+ }
+ } else {
+ pixmap = m_notePixmapFactory->makeHairpinPixmap
+ (length, indicationType == Indication::Crescendo);
+ }
+
+ } else if (indicationType == Indication::Slur ||
+ indicationType == Indication::PhrasingSlur) {
+
+ bool above = true;
+ long dy = 0;
+ long length = 10;
+
+ elt->event()->get
+ <Bool>(properties.SLUR_ABOVE, above);
+ elt->event()->get
+ <Int>(properties.SLUR_Y_DELTA, dy);
+ elt->event()->get
+ <Int>(properties.SLUR_LENGTH, length);
+
+ if (m_printPainter) {
+ for (double w = -1, inc = 0; w != 0; inc += w) {
+ w = setPainterClipping(m_printPainter,
+ elt->getLayoutX(),
+ int(elt->getLayoutY()),
+ int(inc), length, coords,
+ policy);
+ m_notePixmapFactory->drawSlur
+ (length, dy, above,
+ indicationType == Indication::PhrasingSlur,
+ *m_printPainter, int(coords.first), coords.second);
+ m_printPainter->restore();
+ }
+ } else {
+ pixmap = m_notePixmapFactory->makeSlurPixmap
+ (length, dy, above,
+ indicationType == Indication::PhrasingSlur);
+ }
+
+ } else {
+
+ int octaves = indication.getOttavaShift();
+
+ if (octaves != 0) {
+ if (m_printPainter) {
+ for (double w = -1, inc = 0; w != 0; inc += w) {
+ w = setPainterClipping(m_printPainter,
+ elt->getLayoutX(),
+ int(elt->getLayoutY()),
+ int(inc), length, coords,
+ policy);
+ m_notePixmapFactory->drawOttava
+ (length, octaves,
+ *m_printPainter, int(coords.first), coords.second);
+ m_printPainter->restore();
+ }
+ } else {
+ pixmap = m_notePixmapFactory->makeOttavaPixmap
+ (length, octaves);
+ }
+ } else {
+
+ NOTATION_DEBUG
+ << "Unrecognised indicationType " << indicationType << endl;
+ if (m_showUnknowns) {
+ pixmap = m_notePixmapFactory->makeUnknownPixmap();
+ }
+ }
+ }
+ } catch (...) {
+ NOTATION_DEBUG << "Bad indication!" << endl;
+ }
+
+ } else if (elt->event()->isa(Controller::EventType)) {
+
+ bool isSustain = false;
+
+ long controlNumber = 0;
+ elt->event()->get
+ <Int>(Controller::NUMBER, controlNumber);
+
+ Studio *studio = &m_notationView->getDocument()->getStudio();
+ Track *track = getSegment().getComposition()->getTrackById
+ (getSegment().getTrack());
+
+ if (track) {
+
+ Instrument *instrument = studio->getInstrumentById
+ (track->getInstrument());
+ if (instrument) {
+ MidiDevice *device = dynamic_cast<MidiDevice *>
+ (instrument->getDevice());
+ if (device) {
+ for (ControlList::const_iterator i =
+ device->getControlParameters().begin();
+ i != device->getControlParameters().end(); ++i) {
+ if (i->getType() == Controller::EventType &&
+ i->getControllerValue() == controlNumber) {
+ if (i->getName() == "Sustain" ||
+ strtoqstr(i->getName()) == i18n("Sustain")) {
+ isSustain = true;
+ }
+ break;
+ }
+ }
+ } else if (instrument->getDevice() &&
+ instrument->getDevice()->getType() == Device::SoftSynth) {
+ if (controlNumber == 64) {
+ isSustain = true;
+ }
+ }
+ }
+ }
+
+ if (isSustain) {
+ long value = 0;
+ elt->event()->get
+ <Int>(Controller::VALUE, value);
+ if (value > 0) {
+ pixmap = m_notePixmapFactory->makePedalDownPixmap();
+ } else {
+ pixmap = m_notePixmapFactory->makePedalUpPixmap();
+ }
+
+ } else {
+
+ if (m_showUnknowns) {
+ pixmap = m_notePixmapFactory->makeUnknownPixmap();
+ }
+ }
+ } else if (elt->event()->isa(Guitar::Chord::EventType)) {
+
+ // Create a guitar chord pixmap
+ try {
+
+ Guitar::Chord chord (*elt->event());
+
+ /* UNUSED - for printing, just use a large pixmap as below
+ if (m_printPainter) {
+
+ int length = m_notePixmapFactory->getTextWidth(text);
+ for (double w = -1, inc = 0; w != 0; inc += w) {
+ w = setPainterClipping(m_printPainter,
+ elt->getLayoutX(),
+ int(elt->getLayoutY()),
+ int(inc), length, coords,
+ policy);
+ m_notePixmapFactory->drawText
+ (text, *m_printPainter, int(coords.first), coords.second);
+ m_printPainter->restore();
+ }
+ } else {
+ */
+
+ pixmap = m_notePixmapFactory->makeGuitarChordPixmap (chord.getFingering(),
+ int(coords.first),
+ coords.second);
+ // }
+ } catch (Exception e) { // GuitarChord ctor failed
+ NOTATION_DEBUG << "Bad guitar chord event" << endl;
+ }
+
+ } else {
+
+ if (m_showUnknowns) {
+ pixmap = m_notePixmapFactory->makeUnknownPixmap();
+ }
+ }
+
+ // Show the result, one way or another
+
+ if (elt->isNote()) {
+
+ // No need, we already set and showed it in renderNote
+
+ } else if (pixmap) {
+
+ setPixmap(elt, pixmap, z, policy);
+
+ } else {
+ elt->removeCanvasItem();
+ }
+
+ // NOTATION_DEBUG << "NotationStaff::renderSingleElement: Setting selected at " << elt->getAbsoluteTime() << " to " << selected << endl;
+
+ } catch (...) {
+ std::cerr << "Event lacks the proper properties: "
+ << std::endl;
+ elt->event()->dump(std::cerr);
+ }
+
+ m_notePixmapFactory->setSelected(false);
+ m_notePixmapFactory->setShaded(false);
+}
+
+double
+NotationStaff::setPainterClipping(QPainter *painter, double lx, int ly,
+ double dx, double w, LinedStaffCoords &coords,
+ FitPolicy policy)
+{
+ painter->save();
+
+ // NOTATION_DEBUG << "NotationStaff::setPainterClipping: lx " << lx << ", dx " << dx << ", w " << w << endl;
+
+ coords = getCanvasCoordsForLayoutCoords(lx + dx, ly);
+ int row = getRowForLayoutX(lx + dx);
+ double rightMargin = getCanvasXForRightOfRow(row);
+ double available = rightMargin - coords.first;
+
+ // NOTATION_DEBUG << "NotationStaff::setPainterClipping: row " << row << ", rightMargin " << rightMargin << ", available " << available << endl;
+
+ switch (policy) {
+
+ case SplitToFit: {
+ bool fit = (w - dx <= available + m_notePixmapFactory->getNoteBodyWidth());
+ if (dx > 0.01 || !fit) {
+ int clipLeft = int(coords.first), clipWidth = int(available);
+ if (dx < 0.01) {
+ // never clip the left side of the first part of something
+ clipWidth += clipLeft;
+ clipLeft = 0;
+ }
+ QRect clip(clipLeft, coords.second - getRowSpacing() / 2,
+ clipWidth, getRowSpacing());
+ painter->setClipRect(clip, QPainter::CoordPainter);
+ coords.first -= dx;
+ }
+ if (fit) {
+ return 0.0;
+ }
+ return available;
+ }
+
+ case MoveBackToFit:
+ if (w - dx > available + m_notePixmapFactory->getNoteBodyWidth()) {
+ coords.first -= (w - dx) - available;
+ }
+ return 0.0;
+
+ default:
+ return 0.0;
+ }
+}
+
+void
+NotationStaff::setPixmap(NotationElement *elt, QCanvasPixmap *pixmap, int z,
+ FitPolicy policy)
+{
+ double layoutX = elt->getLayoutX();
+ int layoutY = (int)elt->getLayoutY();
+
+ elt->removeCanvasItem();
+
+ while (1) {
+
+ LinedStaffCoords coords =
+ getCanvasCoordsForLayoutCoords(layoutX, layoutY);
+
+ double canvasX = coords.first;
+ int canvasY = coords.second;
+
+ QCanvasItem *item = 0;
+
+ if (m_pageMode == LinearMode || policy == PretendItFittedAllAlong) {
+
+ item = new QCanvasNotationSprite(*elt, pixmap, m_canvas);
+
+ } else {
+
+ int row = getRowForLayoutX(layoutX);
+ double rightMargin = getCanvasXForRightOfRow(row);
+ double extent = canvasX + pixmap->width();
+
+ // NOTATION_DEBUG << "NotationStaff::setPixmap: row " << row << ", right margin " << rightMargin << ", extent " << extent << endl;
+
+ if (extent > rightMargin + m_notePixmapFactory->getNoteBodyWidth()) {
+
+ if (policy == SplitToFit) {
+
+ // NOTATION_DEBUG << "splitting at " << (rightMargin-canvasX) << endl;
+
+ std::pair<QPixmap, QPixmap> split =
+ PixmapFunctions::splitPixmap(*pixmap,
+ int(rightMargin - canvasX));
+
+ QCanvasPixmap *leftCanvasPixmap = new QCanvasPixmap
+ (split.first, QPoint(pixmap->offsetX(), pixmap->offsetY()));
+
+ QCanvasPixmap *rightCanvasPixmap = new QCanvasPixmap
+ (split.second, QPoint(0, pixmap->offsetY()));
+
+ item = new QCanvasNotationSprite(*elt, leftCanvasPixmap, m_canvas);
+ item->setZ(z);
+
+ if (elt->getCanvasItem()) {
+ elt->addCanvasItem(item, canvasX, canvasY);
+ } else {
+ elt->setCanvasItem(item, canvasX, canvasY);
+ }
+
+ item->show();
+
+ delete pixmap;
+ pixmap = rightCanvasPixmap;
+
+ layoutX += rightMargin - canvasX + 0.01; // ensure flip to next row
+
+ continue;
+
+ } else { // policy == MoveBackToFit
+
+ item = new QCanvasNotationSprite(*elt, pixmap, m_canvas);
+ elt->setLayoutX(elt->getLayoutX() - (extent - rightMargin));
+ coords = getCanvasCoordsForLayoutCoords(layoutX, layoutY);
+ canvasX = coords.first;
+ }
+ } else {
+ item = new QCanvasNotationSprite(*elt, pixmap, m_canvas);
+ }
+ }
+
+ item->setZ(z);
+ if (elt->getCanvasItem()) {
+ elt->addCanvasItem(item, canvasX, canvasY);
+ } else {
+ elt->setCanvasItem(item, canvasX, canvasY);
+ }
+ item->show();
+ break;
+ }
+}
+
+void
+NotationStaff::renderNote(ViewElementList::iterator &vli)
+{
+ NotationElement* elt = static_cast<NotationElement*>(*vli);
+
+ const NotationProperties &properties(getProperties());
+ static NotePixmapParameters params(Note::Crotchet, 0);
+
+ Note::Type note = elt->event()->get
+ <Int>(BaseProperties::NOTE_TYPE);
+ int dots = elt->event()->get
+ <Int>(BaseProperties::NOTE_DOTS);
+
+ Accidental accidental = Accidentals::NoAccidental;
+ (void)elt->event()->get
+ <String>(properties.DISPLAY_ACCIDENTAL, accidental);
+
+ bool cautionary = false;
+ if (accidental != Accidentals::NoAccidental) {
+ (void)elt->event()->get
+ <Bool>(properties.DISPLAY_ACCIDENTAL_IS_CAUTIONARY,
+ cautionary);
+ }
+
+ bool up = true;
+ // (void)(elt->event()->get<Bool>(properties.STEM_UP, up));
+ (void)(elt->event()->get
+ <Bool>(properties.VIEW_LOCAL_STEM_UP, up));
+
+ bool flag = true;
+ (void)(elt->event()->get
+ <Bool>(properties.DRAW_FLAG, flag));
+
+ bool beamed = false;
+ (void)(elt->event()->get
+ <Bool>(properties.BEAMED, beamed));
+
+ bool shifted = false;
+ (void)(elt->event()->get
+ <Bool>(properties.NOTE_HEAD_SHIFTED, shifted));
+
+ bool dotShifted = false;
+ (void)(elt->event()->get
+ <Bool>(properties.NOTE_DOT_SHIFTED, dotShifted));
+
+ long stemLength = m_notePixmapFactory->getNoteBodyHeight();
+ (void)(elt->event()->get
+ <Int>(properties.UNBEAMED_STEM_LENGTH, stemLength));
+
+ long heightOnStaff = 0;
+ int legerLines = 0;
+
+ (void)(elt->event()->get
+ <Int>(properties.HEIGHT_ON_STAFF, heightOnStaff));
+ if (heightOnStaff < 0) {
+ legerLines = heightOnStaff;
+ } else if (heightOnStaff > 8) {
+ legerLines = heightOnStaff - 8;
+ }
+
+ long slashes = 0;
+ (void)(elt->event()->get
+ <Int>(properties.SLASHES, slashes));
+
+ bool quantized = false;
+ if (m_colourQuantize && !elt->isTuplet()) {
+ quantized =
+ (elt->getViewAbsoluteTime() != elt->event()->getAbsoluteTime() ||
+ elt->getViewDuration() != elt->event()->getDuration());
+ }
+ params.setQuantized(quantized);
+
+ bool trigger = false;
+ if (elt->event()->has(BaseProperties::TRIGGER_SEGMENT_ID))
+ trigger = true;
+ params.setTrigger(trigger);
+
+ bool inRange = true;
+ Pitch p(*elt->event());
+ Segment *segment = &getSegment();
+ if (m_showRanges) {
+ int pitch = p.getPerformancePitch();
+ if (pitch > segment->getHighestPlayable() ||
+ pitch < segment->getLowestPlayable()) {
+ inRange = false;
+ }
+ }
+ params.setInRange(inRange);
+
+ params.setNoteType(note);
+ params.setDots(dots);
+ params.setAccidental(accidental);
+ params.setAccidentalCautionary(cautionary);
+ params.setNoteHeadShifted(shifted);
+ params.setNoteDotShifted(dotShifted);
+ params.setDrawFlag(flag);
+ params.setDrawStem(true);
+ params.setStemGoesUp(up);
+ params.setLegerLines(legerLines);
+ params.setSlashes(slashes);
+ params.setBeamed(false);
+ params.setIsOnLine(heightOnStaff % 2 == 0);
+ params.removeMarks();
+ params.setSafeVertDistance(0);
+
+ bool primary = false;
+ int safeVertDistance = 0;
+
+ if (elt->event()->get
+ <Bool>(properties.CHORD_PRIMARY_NOTE, primary)
+ && primary) {
+
+ long marks = 0;
+ elt->event()->get
+ <Int>(properties.CHORD_MARK_COUNT, marks);
+ if (marks) {
+ NotationChord chord(*getViewElementList(), vli,
+ m_segment.getComposition()->getNotationQuantizer(),
+ properties);
+ params.setMarks(chord.getMarksForChord());
+ }
+
+ // params.setMarks(Marks::getMarks(*elt->event()));
+
+ if (up && note < Note::Semibreve) {
+ safeVertDistance = m_notePixmapFactory->getStemLength();
+ safeVertDistance = std::max(safeVertDistance, int(stemLength));
+ }
+ }
+
+ long tieLength = 0;
+ (void)(elt->event()->get<Int>(properties.TIE_LENGTH, tieLength));
+ if (tieLength > 0) {
+ params.setTied(true);
+ params.setTieLength(tieLength);
+ } else {
+ params.setTied(false);
+ }
+
+ if (elt->event()->has(BaseProperties::TIE_IS_ABOVE)) {
+ params.setTiePosition
+ (true, elt->event()->get<Bool>(BaseProperties::TIE_IS_ABOVE));
+ } else {
+ params.setTiePosition(false, false); // the default
+ }
+
+ long accidentalShift = 0;
+ bool accidentalExtra = false;
+ if (elt->event()->get<Int>(properties.ACCIDENTAL_SHIFT, accidentalShift)) {
+ elt->event()->get<Bool>(properties.ACCIDENTAL_EXTRA_SHIFT, accidentalExtra);
+ }
+ params.setAccidentalShift(accidentalShift);
+ params.setAccExtraShift(accidentalExtra);
+
+ double airX, airWidth;
+ elt->getLayoutAirspace(airX, airWidth);
+ params.setWidth(int(airWidth));
+
+ if (beamed) {
+
+ if (elt->event()->get<Bool>(properties.CHORD_PRIMARY_NOTE, primary)
+ && primary) {
+
+ int myY = elt->event()->get<Int>(properties.BEAM_MY_Y);
+
+ stemLength = myY - (int)elt->getLayoutY();
+ if (stemLength < 0)
+ stemLength = -stemLength;
+
+ int nextBeamCount =
+ elt->event()->get
+ <Int>(properties.BEAM_NEXT_BEAM_COUNT);
+ int width =
+ elt->event()->get
+ <Int>(properties.BEAM_SECTION_WIDTH);
+ int gradient =
+ elt->event()->get
+ <Int>(properties.BEAM_GRADIENT);
+
+ bool thisPartialBeams(false), nextPartialBeams(false);
+ (void)elt->event()->get
+ <Bool>
+ (properties.BEAM_THIS_PART_BEAMS, thisPartialBeams);
+ (void)elt->event()->get
+ <Bool>
+ (properties.BEAM_NEXT_PART_BEAMS, nextPartialBeams);
+
+ params.setBeamed(true);
+ params.setNextBeamCount(nextBeamCount);
+ params.setThisPartialBeams(thisPartialBeams);
+ params.setNextPartialBeams(nextPartialBeams);
+ params.setWidth(width);
+ params.setGradient((double)gradient / 100.0);
+ if (up)
+ safeVertDistance = stemLength;
+
+ }
+ else {
+ params.setBeamed(false);
+ params.setDrawStem(false);
+ }
+ }
+
+ if (heightOnStaff < 7) {
+ int gap = (((7 - heightOnStaff) * m_notePixmapFactory->getLineSpacing()) / 2);
+ if (safeVertDistance < gap)
+ safeVertDistance = gap;
+ }
+
+ params.setStemLength(stemLength);
+ params.setSafeVertDistance(safeVertDistance);
+ setTuplingParameters(elt, params);
+
+ NotePixmapFactory *factory = m_notePixmapFactory;
+
+ if (elt->isGrace()) {
+ // lift this code from elsewhere to fix #1930309, and it seems to work a
+ // treat, as y'all Wrongpondians are wont to say
+ params.setLegerLines(heightOnStaff < 0 ? heightOnStaff :
+ heightOnStaff > 8 ? heightOnStaff - 8 : 0);
+ m_graceNotePixmapFactory->setSelected(m_notePixmapFactory->isSelected());
+ m_graceNotePixmapFactory->setShaded(m_notePixmapFactory->isShaded());
+ factory = m_graceNotePixmapFactory;
+ }
+
+ if (m_printPainter) {
+
+ // Return no canvas item, but instead render straight to
+ // the printer.
+
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords
+ (elt->getLayoutX(), (int)elt->getLayoutY());
+
+ // We don't actually know how wide the note drawing will be,
+ // but we should be able to use a fairly pessimistic estimate
+ // without causing any problems
+ int length = tieLength + 10 * m_notePixmapFactory->getNoteBodyWidth();
+
+ for (double w = -1, inc = 0; w != 0; inc += w) {
+
+ w = setPainterClipping(m_printPainter,
+ elt->getLayoutX(),
+ int(elt->getLayoutY()),
+ int(inc), length, coords,
+ SplitToFit);
+
+ factory->drawNote
+ (params, *m_printPainter, int(coords.first), coords.second);
+
+ m_printPainter->restore(); // save() called by setPainterClipping
+ }
+
+ } else {
+
+ // The normal on-screen case
+
+ bool collision = false;
+ QCanvasItem * haloItem = 0;
+ if (m_showCollisions) {
+ collision = elt->isColliding();
+ if (collision) {
+ // Make collision halo
+ QCanvasPixmap *haloPixmap = factory->makeNoteHaloPixmap(params);
+ haloItem = new QCanvasNotationSprite(*elt, haloPixmap, m_canvas);
+ haloItem->setZ(-1);
+ }
+ }
+
+ QCanvasPixmap *pixmap = factory->makeNotePixmap(params);
+
+ int z = 0;
+ if (factory->isSelected())
+ z = 3;
+ else if (quantized)
+ z = 2;
+
+ setPixmap(elt, pixmap, z, SplitToFit);
+
+ if (collision) {
+ // Display collision halo
+ LinedStaffCoords coords =
+ getCanvasCoordsForLayoutCoords(elt->getLayoutX(),
+ elt->getLayoutY());
+ double canvasX = coords.first;
+ int canvasY = coords.second;
+ elt->addCanvasItem(haloItem, canvasX, canvasY);
+ haloItem->show();
+ }
+ }
+}
+
+void
+NotationStaff::setTuplingParameters(NotationElement *elt,
+ NotePixmapParameters &params)
+{
+ const NotationProperties &properties(getProperties());
+
+ params.setTupletCount(0);
+ long tuplingLineY = 0;
+ bool tupled = (elt->event()->get
+ <Int>(properties.TUPLING_LINE_MY_Y, tuplingLineY));
+
+ if (tupled) {
+
+ long tuplingLineWidth = 0;
+ if (!elt->event()->get
+ <Int>(properties.TUPLING_LINE_WIDTH, tuplingLineWidth)) {
+ std::cerr << "WARNING: Tupled event at " << elt->event()->getAbsoluteTime() << " has no tupling line width" << std::endl;
+ }
+
+ long tuplingLineGradient = 0;
+ if (!(elt->event()->get
+ <Int>(properties.TUPLING_LINE_GRADIENT,
+ tuplingLineGradient))) {
+ std::cerr << "WARNING: Tupled event at " << elt->event()->getAbsoluteTime() << " has no tupling line gradient" << std::endl;
+ }
+
+ bool tuplingLineFollowsBeam = false;
+ elt->event()->get
+ <Bool>(properties.TUPLING_LINE_FOLLOWS_BEAM,
+ tuplingLineFollowsBeam);
+
+ long tupletCount;
+ if (elt->event()->get<Int>
+ (BaseProperties::BEAMED_GROUP_UNTUPLED_COUNT, tupletCount)) {
+
+ params.setTupletCount(tupletCount);
+ params.setTuplingLineY(tuplingLineY - (int)elt->getLayoutY());
+ params.setTuplingLineWidth(tuplingLineWidth);
+ params.setTuplingLineGradient(double(tuplingLineGradient) / 100.0);
+ params.setTuplingLineFollowsBeam(tuplingLineFollowsBeam);
+ }
+ }
+}
+
+bool
+NotationStaff::isSelected(NotationElementList::iterator it)
+{
+ const EventSelection *selection =
+ m_notationView->getCurrentSelection();
+ return selection && selection->contains((*it)->event());
+}
+
+void
+NotationStaff::showPreviewNote(double layoutX, int heightOnStaff,
+ const Note &note, bool grace)
+{
+ NotePixmapFactory *npf = m_notePixmapFactory;
+ if (grace) npf = m_graceNotePixmapFactory;
+
+ NotePixmapParameters params(note.getNoteType(), note.getDots());
+ NotationRules rules;
+
+ params.setAccidental(Accidentals::NoAccidental);
+ params.setNoteHeadShifted(false);
+ params.setDrawFlag(true);
+ params.setDrawStem(true);
+ params.setStemGoesUp(rules.isStemUp(heightOnStaff));
+ params.setLegerLines(heightOnStaff < 0 ? heightOnStaff :
+ heightOnStaff > 8 ? heightOnStaff - 8 : 0);
+ params.setBeamed(false);
+ params.setIsOnLine(heightOnStaff % 2 == 0);
+ params.setTied(false);
+ params.setBeamed(false);
+ params.setTupletCount(0);
+ params.setSelected(false);
+ params.setHighlighted(true);
+
+ delete m_previewSprite;
+ m_previewSprite = new QCanvasSimpleSprite
+ (npf->makeNotePixmap(params), m_canvas);
+
+ int layoutY = getLayoutYForHeight(heightOnStaff);
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords(layoutX, layoutY);
+
+ m_previewSprite->move(coords.first, (double)coords.second);
+ m_previewSprite->setZ(4);
+ m_previewSprite->show();
+ m_canvas->update();
+}
+
+void
+NotationStaff::clearPreviewNote()
+{
+ delete m_previewSprite;
+ m_previewSprite = 0;
+}
+
+bool
+NotationStaff::wrapEvent(Event *e)
+{
+ bool wrap = true;
+
+ /*!!! always wrap unknowns, just don't necessarily render them?
+
+ if (!m_showUnknowns) {
+ std::string etype = e->getType();
+ if (etype != Note::EventType &&
+ etype != Note::EventRestType &&
+ etype != Clef::EventType &&
+ etype != Key::EventType &&
+ etype != Indication::EventType &&
+ etype != Text::EventType) {
+ wrap = false;
+ }
+ }
+ */
+
+ if (wrap)
+ wrap = Staff::wrapEvent(e);
+
+ return wrap;
+}
+
+void
+NotationStaff::eventRemoved(const Segment *segment,
+ Event *event)
+{
+ LinedStaff::eventRemoved(segment, event);
+ m_notationView->handleEventRemoved(event);
+}
+
+void
+NotationStaff::markChanged(timeT from, timeT to, bool movedOnly)
+{
+ // first time through this, m_ready is false -- we mark it true
+
+ NOTATION_DEBUG << "NotationStaff::markChanged (" << from << " -> " << to << ") " << movedOnly << endl;
+
+ drawStaffName();//!!!
+
+ if (from == to) {
+
+ m_status.clear();
+
+ if (!movedOnly && m_ready) { // undo all the rendering we've already done
+ for (NotationElementList::iterator i = getViewElementList()->begin();
+ i != getViewElementList()->end(); ++i) {
+ static_cast<NotationElement *>(*i)->removeCanvasItem();
+ }
+
+ m_clefChanges.clear();
+ m_keyChanges.clear();
+ }
+
+ drawStaffName();
+
+ } else {
+
+ Segment *segment = &getSegment();
+ Composition *composition = segment->getComposition();
+
+ NotationElementList::iterator unchanged = findUnchangedBarEnd(to);
+
+ int finalBar;
+ if (unchanged == getViewElementList()->end()) {
+ finalBar = composition->getBarNumber(segment->getEndMarkerTime());
+ } else {
+ finalBar = composition->getBarNumber((*unchanged)->getViewAbsoluteTime());
+ }
+
+ int fromBar = composition->getBarNumber(from);
+ int toBar = composition->getBarNumber(to);
+ if (finalBar < toBar)
+ finalBar = toBar;
+
+ for (int bar = fromBar; bar <= finalBar; ++bar) {
+
+ if (bar > toBar)
+ movedOnly = true;
+
+ // NOTATION_DEBUG << "bar " << bar << " status " << m_status[bar] << endl;
+
+ if (bar >= m_lastRenderCheck.first &&
+ bar <= m_lastRenderCheck.second) {
+
+ // NOTATION_DEBUG << "bar " << bar << " rendering and positioning" << endl;
+
+ if (!movedOnly || m_status[bar] == UnRendered) {
+ renderElements
+ (getViewElementList()->findTime(composition->getBarStart(bar)),
+ getViewElementList()->findTime(composition->getBarEnd(bar)));
+ }
+ positionElements(composition->getBarStart(bar),
+ composition->getBarEnd(bar));
+ m_status[bar] = Positioned;
+
+ } else if (!m_ready) {
+ // NOTATION_DEBUG << "bar " << bar << " rendering and positioning" << endl;
+
+ // first time through -- we don't need a separate render phase,
+ // only to mark as not yet positioned
+ m_status[bar] = Rendered;
+
+ } else if (movedOnly) {
+ if (m_status[bar] == Positioned) {
+ // NOTATION_DEBUG << "bar " << bar << " marking unpositioned" << endl;
+ m_status[bar] = Rendered;
+ }
+
+ } else {
+ // NOTATION_DEBUG << "bar " << bar << " marking unrendered" << endl;
+
+ m_status[bar] = UnRendered;
+ }
+ }
+ }
+
+ m_ready = true;
+}
+
+void
+NotationStaff::setPrintPainter(QPainter *painter)
+{
+ m_printPainter = painter;
+}
+
+bool
+NotationStaff::checkRendered(timeT from, timeT to)
+{
+ if (!m_ready)
+ return false;
+ Composition *composition = getSegment().getComposition();
+ if (!composition) {
+ NOTATION_DEBUG << "NotationStaff::checkRendered: warning: segment has no composition -- is my paint event late?" << endl;
+ return false;
+ }
+
+ // NOTATION_DEBUG << "NotationStaff::checkRendered: " << from << " -> " << to << endl;
+
+ int fromBar = composition->getBarNumber(from);
+ int toBar = composition->getBarNumber(to);
+ bool something = false;
+
+ if (fromBar > toBar)
+ std::swap(fromBar, toBar);
+
+ for (int bar = fromBar; bar <= toBar; ++bar) {
+ // NOTATION_DEBUG << "NotationStaff::checkRendered: bar " << bar << " status "
+ // << m_status[bar] << endl;
+
+ switch (m_status[bar]) {
+
+ case UnRendered:
+ renderElements
+ (getViewElementList()->findTime(composition->getBarStart(bar)),
+ getViewElementList()->findTime(composition->getBarEnd(bar)));
+
+ case Rendered:
+ positionElements
+ (composition->getBarStart(bar),
+ composition->getBarEnd(bar));
+ m_lastRenderedBar = bar;
+
+ something = true;
+
+ case Positioned:
+ break;
+ }
+
+ m_status[bar] = Positioned;
+ }
+
+ m_lastRenderCheck = std::pair<int, int>(fromBar, toBar);
+ return something;
+}
+
+bool
+NotationStaff::doRenderWork(timeT from, timeT to)
+{
+ if (!m_ready)
+ return true;
+ Composition *composition = getSegment().getComposition();
+
+ int fromBar = composition->getBarNumber(from);
+ int toBar = composition->getBarNumber(to);
+
+ if (fromBar > toBar)
+ std::swap(fromBar, toBar);
+
+ for (int bar = fromBar; bar <= toBar; ++bar) {
+
+ switch (m_status[bar]) {
+
+ case UnRendered:
+ renderElements
+ (getViewElementList()->findTime(composition->getBarStart(bar)),
+ getViewElementList()->findTime(composition->getBarEnd(bar)));
+ m_status[bar] = Rendered;
+ return true;
+
+ case Rendered:
+ positionElements
+ (composition->getBarStart(bar),
+ composition->getBarEnd(bar));
+ m_status[bar] = Positioned;
+ m_lastRenderedBar = bar;
+ return true;
+
+ case Positioned:
+ // The bars currently displayed are rendered before the others.
+ // Later, when preceding bars are rendered, truncateClefsAndKeysAt()
+ // is called and possible clefs and/or keys from the bars previously
+ // rendered may be lost. Following code should restore these clefs
+ // and keys in m_clefChanges and m_keyChanges lists.
+ if (bar > m_lastRenderedBar)
+ checkAndCompleteClefsAndKeys(bar);
+ continue;
+ }
+ }
+
+ return false;
+}
+
+void
+NotationStaff::checkAndCompleteClefsAndKeys(int bar)
+{
+ // Look for Clef or Key in current bar
+ Composition *composition = getSegment().getComposition();
+ timeT barStartTime = composition->getBarStart(bar);
+ timeT barEndTime = composition->getBarEnd(bar);
+
+ for (ViewElementList::iterator it =
+ getViewElementList()->findTime(barStartTime);
+ (it != getViewElementList()->end())
+ && ((*it)->getViewAbsoluteTime() < barEndTime); ++it) {
+ if ((*it)->event()->isa(Clef::EventType)) {
+ // Clef found
+ Clef clef = *(*it)->event();
+
+ // Is this clef already in m_clefChanges list ?
+ int xClef = int((*it)->getLayoutX());
+ bool found = false;
+ for (int i = 0; i < m_clefChanges.size(); ++i) {
+ if ( (m_clefChanges[i].first == xClef)
+ && (m_clefChanges[i].second == clef)) {
+ found = true;
+ break;
+ }
+ }
+
+ // If not, add it
+ if (!found) {
+ m_clefChanges.push_back(ClefChange(xClef, clef));
+ }
+
+ } else if ((*it)->event()->isa(::Rosegarden::Key::EventType)) {
+ ::Rosegarden::Key key = *(*it)->event();
+
+ // Is this key already in m_keyChanges list ?
+ int xKey = int((*it)->getLayoutX());
+ bool found = false;
+ for (int i = 0; i < m_keyChanges.size(); ++i) {
+ if ( (m_keyChanges[i].first == xKey)
+ && (m_keyChanges[i].second == key)) {
+ found = true;
+ break;
+ }
+ }
+
+ // If not, add it
+ if (!found) {
+ m_keyChanges.push_back(KeyChange(xKey, key));
+ }
+ }
+ }
+}
+
+LinedStaff::BarStyle
+NotationStaff::getBarStyle(int barNo) const
+{
+ const Segment *s = &getSegment();
+ Composition *c = s->getComposition();
+
+ int firstBar = c->getBarNumber(s->getStartTime());
+ int lastNonEmptyBar = c->getBarNumber(s->getEndMarkerTime() - 1);
+
+ // Currently only the first and last bar in a segment have any
+ // possibility of getting special treatment:
+ if (barNo > firstBar && barNo <= lastNonEmptyBar)
+ return PlainBar;
+
+ // First and last bar in a repeating segment get repeat bars.
+
+ if (s->isRepeating()) {
+ if (barNo == firstBar)
+ return RepeatStartBar;
+ else if (barNo == lastNonEmptyBar + 1)
+ return RepeatEndBar;
+ }
+
+ if (barNo <= lastNonEmptyBar)
+ return PlainBar;
+
+ // Last bar on a given track gets heavy double bars. Exploit the
+ // fact that Composition's iterator returns segments in track
+ // order.
+
+ Segment *lastSegmentOnTrack = 0;
+
+ for (Composition::iterator i = c->begin(); i != c->end(); ++i) {
+ if ((*i)->getTrack() == s->getTrack()) {
+ lastSegmentOnTrack = *i;
+ } else if (lastSegmentOnTrack != 0) {
+ break;
+ }
+ }
+
+ if (&getSegment() == lastSegmentOnTrack)
+ return HeavyDoubleBar;
+ else
+ return PlainBar;
+}
+
+double
+NotationStaff::getBarInset(int barNo, bool isFirstBarInRow) const
+{
+ LinedStaff::BarStyle style = getBarStyle(barNo);
+
+ NOTATION_DEBUG << "getBarInset(" << barNo << "," << isFirstBarInRow << ")" << endl;
+
+ if (!(style == RepeatStartBar || style == RepeatBothBar))
+ return 0.0;
+
+ const Segment &s = getSegment();
+ Composition *composition = s.getComposition();
+ timeT barStart = composition->getBarStart(barNo);
+
+ double inset = 0.0;
+
+ NOTATION_DEBUG << "ready" << endl;
+
+ bool haveKey = false, haveClef = false;
+
+ ::Rosegarden::Key key;
+ ::Rosegarden::Key cancelKey;
+ Clef clef;
+
+ for (Segment::iterator i = s.findTime(barStart);
+ s.isBeforeEndMarker(i) && ((*i)->getNotationAbsoluteTime() == barStart);
+ ++i) {
+
+ NOTATION_DEBUG << "type " << (*i)->getType() << " at " << (*i)->getNotationAbsoluteTime() << endl;
+
+ if ((*i)->isa(::Rosegarden::Key::EventType)) {
+
+ try {
+ key = ::Rosegarden::Key(**i);
+
+ if (barNo > composition->getBarNumber(s.getStartTime())) {
+ cancelKey = s.getKeyAtTime(barStart - 1);
+ }
+
+ if (m_keySigCancelMode == 0) { // only when entering C maj / A min
+
+ if (key.getAccidentalCount() != 0)
+ cancelKey = ::Rosegarden::Key();
+
+ } else if (m_keySigCancelMode == 1) { // only when reducing acc count
+
+ if (!(key.isSharp() == cancelKey.isSharp() &&
+ key.getAccidentalCount() < cancelKey.getAccidentalCount())) {
+ cancelKey = ::Rosegarden::Key();
+ }
+ }
+
+ haveKey = true;
+
+ } catch (...) {
+ NOTATION_DEBUG << "getBarInset: Bad key in event" << endl;
+ }
+
+ } else if ((*i)->isa(Clef::EventType)) {
+
+ try {
+ clef = Clef(**i);
+ haveClef = true;
+ } catch (...) {
+ NOTATION_DEBUG << "getBarInset: Bad clef in event" << endl;
+ }
+ }
+ }
+
+ if (isFirstBarInRow) {
+ if (!haveKey) {
+ key = s.getKeyAtTime(barStart);
+ haveKey = true;
+ }
+ if (!haveClef) {
+ clef = s.getClefAtTime(barStart);
+ haveClef = true;
+ }
+ }
+
+ if (haveKey) {
+ inset += m_notePixmapFactory->getKeyWidth(key, cancelKey);
+ }
+ if (haveClef) {
+ inset += m_notePixmapFactory->getClefWidth(clef);
+ }
+ if (haveClef || haveKey) {
+ inset += m_notePixmapFactory->getBarMargin() / 3;
+ }
+ if (haveClef && haveKey) {
+ inset += m_notePixmapFactory->getNoteBodyWidth() / 2;
+ }
+
+ NOTATION_DEBUG << "getBarInset(" << barNo << "," << isFirstBarInRow << "): inset " << inset << endl;
+
+
+ return inset;
+}
+
+Rosegarden::ViewElement* NotationStaff::makeViewElement(Rosegarden::Event* e)
+{
+ return new NotationElement(e);
+}
+
+}
diff --git a/src/gui/editors/notation/NotationStaff.h b/src/gui/editors/notation/NotationStaff.h
new file mode 100644
index 0000000..4a0302c
--- /dev/null
+++ b/src/gui/editors/notation/NotationStaff.h
@@ -0,0 +1,488 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTATIONSTAFF_H_
+#define _RG_NOTATIONSTAFF_H_
+
+#include "base/FastVector.h"
+#include "base/Staff.h"
+#include "base/ViewElement.h"
+#include "gui/general/LinedStaff.h"
+#include "gui/general/ProgressReporter.h"
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+#include "base/Event.h"
+#include "NotationElement.h"
+
+
+class QPainter;
+class QCanvasPixmap;
+class QCanvasItem;
+class QCanvas;
+class LinedStaffCoords;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class TimeSignature;
+class SnapGrid;
+class Segment;
+class QCanvasSimpleSprite;
+class NotePixmapParameters;
+class NotePixmapFactory;
+class Note;
+class NotationView;
+class NotationProperties;
+class Key;
+class Event;
+class Clef;
+
+
+/**
+ * The Staff is a repository for information about the notation
+ * representation of a single Segment. This includes all of the
+ * NotationElements representing the Events on that Segment, the staff
+ * lines, as well as basic positional and size data. This class
+ * used to be in gui/staff.h, but it's been moved and renamed
+ * following the introduction of the core Staff base class, and
+ * much of the functionality has been extracted into the LinedStaff
+ * base class.
+ */
+
+class NotationStaff : public ProgressReporter, public LinedStaff
+{
+public:
+
+ /**
+ * Creates a new NotationStaff for the specified Segment
+ * \a id is the id of the staff in the NotationView
+ */
+ NotationStaff(QCanvas *, Segment *, SnapGrid *,
+ int id, NotationView *view,
+ std::string fontName, int resolution);
+ virtual ~NotationStaff();
+
+ /**
+ * Changes the resolution of the note pixmap factory and the
+ * staff lines, etc
+ */
+ virtual void changeFont(std::string fontName, int resolution);
+
+ void setLegerLineCount(int legerLineCount) {
+ if (legerLineCount == -1) m_legerLineCount = 8;
+ else m_legerLineCount = legerLineCount;
+ }
+
+ void setBarNumbersEvery(int barNumbersEvery) {
+ m_barNumbersEvery = barNumbersEvery;
+ }
+
+ LinedStaff::setPageMode;
+ LinedStaff::setPageWidth;
+ LinedStaff::setRowsPerPage;
+ LinedStaff::setRowSpacing;
+ LinedStaff::setConnectingLineLength;
+
+ /**
+ * Gets a read-only reference to the pixmap factory used by the
+ * staff. (For use by NotationHLayout, principally.) This
+ * reference isn't const because the NotePixmapFactory maintains
+ * too much state for its methods to be const, but you should
+ * treat the returned reference as if it were const anyway.
+ */
+ virtual NotePixmapFactory& getNotePixmapFactory(bool grace) {
+ return grace ? *m_graceNotePixmapFactory : *m_notePixmapFactory;
+ }
+
+ /**
+ * Generate or re-generate sprites for all the elements between
+ * from and to. Call this when you've just made a change,
+ * specifying the extents of the change in the from and to
+ * parameters.
+ *
+ * This method does not reposition any elements outside the given
+ * range -- so after any edit that may change the visible extents
+ * of a range, you will then need to call positionElements for the
+ * changed range and the entire remainder of the staff.
+ */
+ virtual void renderElements(NotationElementList::iterator from,
+ NotationElementList::iterator to);
+
+
+ /**
+ * Assign suitable coordinates to the elements on the staff,
+ * based entirely on the layout X and Y coordinates they were
+ * given by the horizontal and vertical layout processes.
+ *
+ * This is necessary because the sprites that are being positioned
+ * may have been created either after the layout process completed
+ * (by renderElements) or before (by the previous renderElements
+ * call, if the sprites are unchanged but have moved) -- so
+ * neither the layout nor renderElements can authoritatively set
+ * their final positions.
+ *
+ * This method also updates the selected-ness of any elements it
+ * sees (i.e. it turns the selected ones blue and the unselected
+ * ones black), and re-generates sprites for any elements for
+ * which it seems necessary. In general it will only notice a
+ * element needs regenerating if its position has changed, not if
+ * the nature of the element has changed, so this is no substitute
+ * for calling renderElements.
+ *
+ * The from and to arguments are used to indicate the extents of a
+ * changed area within the staff. The actual area within which the
+ * elements end up being repositioned will begin at the start of
+ * the bar containing the changed area's start, and will end at the
+ * start of the next bar whose first element hasn't moved, after
+ * the changed area's end.
+ *
+ * Call this after renderElements, or after changing the selection,
+ * passing from and to arguments corresponding to the times of those
+ * passed to renderElements.
+ */
+ virtual void positionElements(timeT from,
+ timeT to);
+
+ /**
+ * Re-render and position elements as necessary, based on the
+ * given extents and any information obtained from calls to
+ * markChanged(). This provides a render-on-demand mechanism. If
+ * you are going to use this rendering mechanism, it's generally
+ * wise to avoid explicitly calling
+ * renderElements/positionElements as well.
+ *
+ * Returns true if something needed re-rendering.
+ */
+ virtual bool checkRendered(timeT from,
+ timeT to);
+
+ /**
+ * Find something between the given times that has not yet been
+ * rendered, and render a small amount of it. Return true if it
+ * found something to do. This is to be used as a background work
+ * procedure for rendering not-yet-visible areas of notation.
+ */
+ virtual bool doRenderWork(timeT from,
+ timeT to);
+
+ /**
+ * Mark a region of staff as changed, for use by the on-demand
+ * rendering mechanism. If fromBar == toBar == -1, mark the
+ * entire staff as changed (and recover the memory used for its
+ * elements). Pass movedOnly as true to indicate that elements
+ * have not changed but only been repositioned, for example as a
+ * consequence of a modification on another staff that caused a
+ * relayout.
+ */
+ virtual void markChanged(timeT from = 0,
+ timeT to = 0,
+ bool movedOnly = false);
+
+ /**
+ * Set a painter as the printer output. If this painter is
+ * non-null, subsequent renderElements calls will only render
+ * those elements that cannot be rendered directly to a print
+ * painter; those that can, will be rendered by renderPrintable()
+ * instead.
+ */
+ virtual void setPrintPainter(QPainter *painter);
+
+ /**
+ * Render to the current print painter those elements that can be
+ * rendered directly to a print painter. If no print painter is
+ * set, do nothing.
+ */
+ virtual void renderPrintable(timeT from,
+ timeT to);
+
+ /**
+ * Insert time signature at x-coordinate \a x.
+ */
+ virtual void insertTimeSignature(double layoutX,
+ const TimeSignature &timeSig);
+
+ /**
+ * Delete all time signatures
+ */
+ virtual void deleteTimeSignatures();
+
+ /**
+ * Insert repeated clef and key at start of new line, at x-coordinate \a x.
+ */
+ virtual void insertRepeatedClefAndKey(double layoutX, int barNo);
+
+ /**
+ * Delete all repeated clefs and keys.
+ */
+ virtual void deleteRepeatedClefsAndKeys();
+
+ /**
+ * (Re)draw the staff name from the track's current name
+ */
+ virtual void drawStaffName();
+
+ /**
+ * Return true if the staff name as currently drawn is up-to-date
+ * with that in the composition
+ */
+ virtual bool isStaffNameUpToDate();
+
+ /**
+ * Return the clef and key in force at the given canvas
+ * coordinates
+ */
+ virtual void getClefAndKeyAtCanvasCoords(double x, int y,
+ Clef &clef,
+ ::Rosegarden::Key &key) const;
+
+ /**
+ * Return the note name (C4, Bb3, whatever) corresponding to the
+ * given canvas coordinates
+ */
+ virtual std::string getNoteNameAtCanvasCoords
+ (double x, int y,
+ Accidental accidental =
+ Accidentals::NoAccidental) const;
+
+ /**
+ * Find the NotationElement whose layout x-coord is closest to x,
+ * without regard to its y-coord.
+ *
+ * If notesAndRestsOnly is true, will return the closest note
+ * or rest but will never return any other kind of element.
+ *
+ * If the closest event is further than \a proximityThreshold
+ * horizontally away from x, in pixels, end() is returned.
+ * (If proximityThreshold is negative, there will be no limit
+ * to the distances that will be considered.)
+ *
+ * Also returns the clef and key in force at the given coordinate.
+ */
+ virtual ViewElementList::iterator getClosestElementToLayoutX
+ (double x, Event *&clef, Event *&key,
+ bool notesAndRestsOnly = false,
+ int proximityThreshold = 10);
+
+ /**
+ * Find the NotationElement "under" the given layout x-coord,
+ * without regard to its y-coord.
+ *
+ * Also returns the clef and key in force at the given coordinates.
+ */
+ virtual ViewElementList::iterator getElementUnderLayoutX
+ (double x, Event *&clef, Event *&key);
+
+ /**
+ * Draw a note on the staff to show an insert position prior to
+ * an insert.
+ */
+ virtual void showPreviewNote(double layoutX, int heightOnStaff,
+ const Note &note, bool grace);
+
+ /**
+ * Remove any visible preview note.
+ */
+ virtual void clearPreviewNote();
+
+ /**
+ * Overridden from Staff<T>.
+ * We want to avoid wrapping things like controller events, if
+ * our showUnknowns preference is off
+ */
+ virtual bool wrapEvent(Event *);
+
+ /**
+ * Override from Staff<T>
+ * Let tools know if their current element has gone
+ */
+ virtual void eventRemoved(const Segment *, Event *);
+
+ /**
+ * Return the view-local PropertyName definitions for this staff's view
+ */
+ const NotationProperties &getProperties() const;
+
+ virtual double getBarInset(int barNo, bool isFirstBarInRow) const;
+
+ /**
+ * Return the time at the given canvas coordinates
+ */
+ timeT getTimeAtCanvasCoords(double x, int y) const;
+
+protected:
+
+ virtual ViewElement* makeViewElement(Event*);
+
+ // definition of staff
+ virtual int getLineCount() const { return 5; }
+ virtual int getLegerLineCount() const { return m_legerLineCount; }
+ virtual int getBottomLineHeight() const { return 0; }
+ virtual int getHeightPerLine() const { return 2; }
+ virtual int showBarNumbersEvery() const { return m_barNumbersEvery; }
+
+ virtual BarStyle getBarStyle(int barNo) const;
+
+ /**
+ * Assign a suitable sprite to the given element (the clef is
+ * needed in case it's a key event, in which case we need to judge
+ * the correct pitch for the key)
+ */
+ virtual void renderSingleElement(ViewElementList::iterator &,
+ const Clef &,
+ const ::Rosegarden::Key &,
+ bool selected);
+
+ bool isDirectlyPrintable(ViewElement *elt);
+
+ void setTuplingParameters(NotationElement *, NotePixmapParameters &);
+
+ /**
+ * Set a sprite representing the given note event to the given notation element
+ */
+ virtual void renderNote(ViewElementList::iterator &);
+
+ /**
+ * Return a NotationElementList::iterator pointing to the
+ * start of a bar prior to the given time that doesn't appear
+ * to have been affected by any changes around that time
+ */
+ NotationElementList::iterator findUnchangedBarStart(timeT);
+
+ /**
+ * Return a NotationElementList::iterator pointing to the
+ * end of a bar subsequent to the given time that doesn't appear
+ * to have been affected by any changes around that time
+ */
+ NotationElementList::iterator findUnchangedBarEnd(timeT);
+
+ /**
+ * Return true if the element has a canvas item that is already
+ * at the correct coordinates
+ */
+ virtual bool elementNotMoved(NotationElement *);
+
+ /**
+ * Return true if the element has a canvas item that is already
+ * at the correct y-coordinate
+ */
+ virtual bool elementNotMovedInY(NotationElement *);
+
+ /**
+ * Returns true if the item at the given iterator appears to have
+ * moved horizontally without the spacing around it changing.
+ *
+ * In practice, calculates the offset between the intended layout
+ * and current canvas coordinates of the item at the given
+ * iterator, and returns true if this offset is equal to those of
+ * all other following iterators at the same time as well as the
+ * first iterator found at a greater time.
+ */
+ virtual bool elementShiftedOnly(NotationElementList::iterator);
+
+ enum FitPolicy {
+ PretendItFittedAllAlong = 0,
+ MoveBackToFit,
+ SplitToFit
+ };
+
+ /**
+ * Prepare a painter to draw an object of logical width w at
+ * layout-x coord x, starting at offset dx into the object, by
+ * setting the painter's clipping so as to crop the object at the
+ * right edge of the row if it would otherwise overrun. The
+ * return value is the amount of the object visible on this row
+ * (i.e. the increment in offset for the next call to this method)
+ * or zero if no crop was necessary. The canvas coords at which
+ * the object should subsequently be drawn are returned in coords.
+ *
+ * This function calls painter.save(), and the caller must call
+ * painter.restore() after use.
+ */
+ virtual double setPainterClipping(QPainter *, double layoutX, int layoutY,
+ double dx, double w, LinedStaffCoords &coords,
+ FitPolicy policy);
+
+ /**
+ * Set a single pixmap to a notation element, or split it into
+ * bits if it overruns the end of a row and set the bits
+ * separately.
+ */
+ virtual void setPixmap(NotationElement *, QCanvasPixmap *, int z,
+ FitPolicy policy);
+
+ bool isSelected(NotationElementList::iterator);
+
+ typedef std::set<QCanvasSimpleSprite *> SpriteSet;
+ SpriteSet m_timeSigs;
+
+ typedef std::set<QCanvasItem *> ItemSet;
+ ItemSet m_repeatedClefsAndKeys;
+
+ typedef std::pair<int, Clef> ClefChange;
+ FastVector<ClefChange> m_clefChanges;
+
+ typedef std::pair<int, ::Rosegarden::Key> KeyChange;
+ FastVector<KeyChange> m_keyChanges;
+
+ void truncateClefsAndKeysAt(int);
+
+ /** Verify that a possible Clef or Key in bar is already inserted
+ * in m_clefChange or m_keyChange.
+ * If not, do the insertion.
+ */
+ void checkAndCompleteClefsAndKeys(int bar);
+
+ NotePixmapFactory *m_notePixmapFactory;
+ NotePixmapFactory *m_graceNotePixmapFactory;
+ QCanvasSimpleSprite *m_previewSprite;
+ QCanvasSimpleSprite *m_staffName;
+ std::string m_staffNameText;
+ NotationView *m_notationView;
+ int m_legerLineCount;
+ int m_barNumbersEvery;
+ bool m_colourQuantize;
+ bool m_showUnknowns;
+ bool m_showRanges;
+ bool m_showCollisions;
+ int m_keySigCancelMode;
+
+ QPainter *m_printPainter;
+
+ enum BarStatus { UnRendered = 0, Rendered, Positioned };
+ typedef std::map<int, BarStatus> BarStatusMap;
+ BarStatusMap m_status;
+ std::pair<int, int> m_lastRenderCheck;
+ bool m_ready;
+
+ int m_lastRenderedBar;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationStrings.cpp b/src/gui/editors/notation/NotationStrings.cpp
new file mode 100644
index 0000000..6f8defd
--- /dev/null
+++ b/src/gui/editors/notation/NotationStrings.cpp
@@ -0,0 +1,301 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NotationStrings.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/Exception.h"
+#include "base/NotationTypes.h"
+#include "gui/configuration/GeneralConfigurationPage.h"
+#include <kconfig.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+QString
+NotationStrings::addDots(QString s, int dots,
+ bool hyphenate, bool internationalize)
+{
+ if (!dots)
+ return s;
+
+ if (internationalize) {
+ if (dots > 1) {
+ if (hyphenate)
+ return i18n("%1-dotted-%2").arg(dots).arg(s);
+ else
+ return i18n("%1-dotted %2").arg(dots).arg(s);
+ } else {
+ if (hyphenate)
+ return i18n("dotted-%1").arg(s);
+ else
+ return i18n("dotted %1").arg(s);
+ }
+ } else {
+ if (dots > 1) {
+ if (hyphenate)
+ return QString("%1-dotted-%2").arg(dots).arg(s);
+ else
+ return QString("%1-dotted %2").arg(dots).arg(s);
+ } else {
+ if (hyphenate)
+ return QString("dotted-%1").arg(s);
+ else
+ return QString("dotted %1").arg(s);
+ }
+ }
+}
+
+QString
+NotationStrings::getNoteName(Note note, bool plural, bool triplet)
+{
+ Note::Type type = note.getNoteType();
+ int dots = note.getDots();
+
+ static const QString names[] = {
+ i18n("sixty-fourth note"), i18n("thirty-second note"),
+ i18n("sixteenth note"), i18n("eighth note"),
+ i18n("quarter note"), i18n("half note"),
+ i18n("whole note"), i18n("double whole note")
+ };
+ static const QString pluralnames[] = {
+ i18n("sixty-fourth notes"), i18n("thirty-second notes"),
+ i18n("sixteenth notes"), i18n("eighth notes"),
+ i18n("quarter notes"), i18n("half notes"),
+ i18n("whole notes"), i18n("double whole notes")
+ };
+
+ if (plural && triplet) {
+ return addDots(i18n("%1 triplets").arg(names[type]), dots, false, true); // TODO PLURAL - this is broken because it assumes there's only 1 plural form
+ } else if (plural) {
+ return addDots(pluralnames[type], dots, false, true);
+ } else if (triplet) {
+ return addDots(i18n("%1 triplet").arg(names[type]), dots, false, true);
+ } else {
+ return addDots(names[type], dots, false, true);
+ }
+}
+
+QString
+NotationStrings::getAmericanName(Note note, bool plural, bool triplet)
+{
+ Note::Type type = note.getNoteType();
+ int dots = note.getDots();
+
+ static const QString names[] = {
+ "sixty-fourth note", "thirty-second note",
+ "sixteenth note", "eighth note",
+ "quarter note", "half note",
+ "whole note", "double whole note"
+ };
+ static const QString pluralnames[] = {
+ "sixty-fourth notes", "thirty-second notes",
+ "sixteenth notes", "eighth notes",
+ "quarter notes", "half notes",
+ "whole notes", "double whole notes"
+ };
+
+ if (plural && triplet) {
+ return addDots(QString("%1 triplets").arg(names[type]), dots, false, false);
+ } else if (plural) {
+ return addDots(pluralnames[type], dots, false, false);
+ } else if (triplet) {
+ return addDots(QString("%1 triplet").arg(names[type]), dots, false, false);
+ } else {
+ return addDots(names[type], dots, false, false);
+ }
+}
+
+QString
+NotationStrings::getShortNoteName(Note note, bool plural, bool triplet)
+{
+ Note::Type type = note.getNoteType();
+ int dots = note.getDots();
+
+ static const QString names[] = {
+ i18n("64th"), i18n("32nd"), i18n("16th"), i18n("8th"),
+ i18n("quarter"), i18n("half"), i18n("whole"),
+ i18n("double whole")
+ };
+ static const QString pluralnames[] = {
+ i18n("64ths"), i18n("32nds"), i18n("16ths"), i18n("8ths"),
+ i18n("quarters"), i18n("halves"), i18n("wholes"),
+ i18n("double wholes")
+ };
+
+ if (plural && triplet) {
+ return addDots(i18n("%1 triplets").arg(names[type]), dots, false, true); // TODO - this is broken because it assumes there's only 1 plural form
+ } else if (plural) {
+ return addDots(pluralnames[type], dots, false, true);
+ } else if (triplet) {
+ return addDots(i18n("%1 triplet").arg(names[type]), dots, false, true);
+ } else {
+ return addDots(names[type], dots, false, true);
+ }
+}
+
+QString
+NotationStrings::getReferenceName(Note note, bool isRest)
+{
+ Note::Type type = note.getNoteType();
+ int dots = note.getDots();
+
+ static const QString names[] = {
+ "hemidemisemi", "demisemi", "semiquaver",
+ "quaver", "crotchet", "minim", "semibreve", "breve"
+ };
+
+ QString name(names[type]);
+ if (isRest)
+ name = "rest-" + name;
+ return addDots(name, dots, true, false);
+}
+
+Note
+NotationStrings::getNoteForName(QString name)
+{
+ std::string origName(qstrtostr(name));
+ int pos = name.find('-');
+ int dots = 0;
+
+ if (pos > 0 && pos < 6 && pos < int(name.length()) - 1) {
+ dots = name.left(pos).toInt();
+ name = name.right(name.length() - pos - 1);
+ if (dots < 2) {
+ throw MalformedNoteName("Non-numeric or invalid dot count in \"" +
+ origName + "\"");
+ }
+ }
+
+ if (name.length() > 7 &&
+ (name.left(7) == "dotted " || name.left(7) == "dotted-")) {
+ if (dots == 0)
+ dots = 1;
+ name = name.right(name.length() - 7);
+ } else {
+ if (dots > 1) {
+ throw MalformedNoteName("Dot count without dotted tag in \"" +
+ origName + "\"");
+ }
+ }
+
+ if (name.length() > 5 && name.right(5) == " note") {
+ name = name.left(name.length() - 5);
+ }
+
+ Note::Type type;
+
+ static const char *names[][4] = {
+ { "64th", "sixty-fourth", "hemidemisemi", "hemidemisemiquaver"
+ },
+ { "32nd", "thirty-second", "demisemi", "demisemiquaver" },
+ { "16th", "sixteenth", "semi", "semiquaver" },
+ { "8th", "eighth", 0, "quaver" },
+ { "quarter", 0, 0, "crotchet", },
+ { "half", 0, 0, "minim" },
+ { "whole", 0, 0, "semibreve" },
+ { "double whole", 0, 0, "breve" }
+ };
+
+ for (type = Note::Shortest; type <= Note::Longest; ++type) {
+ for (int i = 0; i < 4; ++i) {
+ if (!names[type][i])
+ continue;
+ if (name == names[type][i])
+ return Note(type, dots);
+ }
+ }
+
+ throw MalformedNoteName("Can't parse note name \"" + origName + "\"");
+}
+
+QString
+NotationStrings::makeNoteMenuLabel(timeT duration,
+ bool brief,
+ timeT &errorReturn,
+ bool plural)
+{
+ Note nearestNote = Note::getNearestNote(duration);
+ bool triplet = false;
+ errorReturn = 0;
+
+ if (duration == 0)
+ return "0";
+
+ if (nearestNote.getDuration() != duration) {
+ Note tripletNote = Note::getNearestNote(duration * 3 / 2);
+ if (tripletNote.getDuration() == duration * 3 / 2) {
+ nearestNote = tripletNote;
+ triplet = true;
+ } else {
+ errorReturn = duration - nearestNote.getDuration();
+ duration = nearestNote.getDuration();
+ }
+ }
+
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ GeneralConfigurationPage::NoteNameStyle noteNameStyle =
+ (GeneralConfigurationPage::NoteNameStyle)
+ config->readUnsignedNumEntry
+ ("notenamestyle", GeneralConfigurationPage::Local);
+
+ if (brief) {
+
+ timeT wholeNote = Note(Note::Semibreve).getDuration();
+ if ((wholeNote / duration) * duration == wholeNote) {
+ return QString("1/%1").arg(wholeNote / duration);
+ } else if ((duration / wholeNote) * wholeNote == duration) {
+ return QString("%1/1").arg(duration / wholeNote);
+ } else {
+ return i18n("%1 ticks").arg(duration);
+ plural = false;
+ }
+
+ } else {
+ QString noteName;
+
+ switch (noteNameStyle) {
+
+ case GeneralConfigurationPage::American:
+ noteName = getAmericanName(nearestNote, plural, triplet);
+ break;
+
+ case GeneralConfigurationPage::Local:
+ noteName = getNoteName(nearestNote, plural, triplet);
+ break;
+ }
+
+ // Already internationalised, if appropriate
+ return noteName;
+ }
+}
+
+}
diff --git a/src/gui/editors/notation/NotationStrings.h b/src/gui/editors/notation/NotationStrings.h
new file mode 100644
index 0000000..d79dff3
--- /dev/null
+++ b/src/gui/editors/notation/NotationStrings.h
@@ -0,0 +1,121 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTATIONSTRINGS_H_
+#define _RG_NOTATIONSTRINGS_H_
+
+#include "base/Exception.h"
+#include "base/NotationTypes.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * String factory for note names, etc. used in the GUI
+ * Replaces use of base/NotationTypes.h strings which should
+ * be used only for non-user purposes.
+ */
+class NotationStrings
+{
+public:
+ NotationStrings();
+ ~NotationStrings();
+
+
+ /**
+ * Get the name of a note. The default return values are American
+ * (e.g. quarter note, dotted sixteenth note). If the app is
+ * internationalised, you will get return names local to your
+ * region. Note that this includes English note names- set your
+ * LC_LANG to en_GB.
+ */
+ static QString getNoteName(Note note,
+ bool plural = false, bool triplet = false);
+
+ /**
+ * Get the UNTRANSLATED American name of a note. This may be
+ * useful if the user has specified that they'd prefer American
+ * names to local names.
+ */
+ static QString getAmericanName(Note note,
+ bool plural = false, bool triplet = false);
+
+ /**
+ * Get the short name of a note. The default return values are
+ * American (e.g. quarter, dotted 16th). If the app is
+ * internationalised, you will get return names local to your
+ * region. Note that this includes English note names- set your
+ * LC_LANG to en_GB.
+ */
+ static QString getShortNoteName(Note note,
+ bool plural = false, bool triplet = false);
+
+
+ /**
+ * Get the UNTRANSLATED reference name of a note or rest. This is the
+ * formal name used to name pixmap files and the like, so the exact
+ * values of these strings are pretty sensitive.
+ */
+ static QString getReferenceName(Note note, bool isRest = false);
+
+ typedef Exception MalformedNoteName;
+
+ /**
+ * Get the note corresponding to the given string, which must be a
+ * reference name or an untranslated British, American or short name.
+ * May throw MalformedNoteName.
+ */
+ static Note getNoteForName(QString name);
+
+ /**
+ * Construct a label to describe the given duration as a note name in
+ * the proper locale. Uses the nearest available note to the duration
+ * and returns a non-zero value in errorReturn if it was not an exact
+ * match for the required duration.
+ */
+ static QString makeNoteMenuLabel(timeT duration,
+ bool brief,
+ timeT &errorReturn,
+ bool plural = false);
+
+private:
+ /**
+ * Return a string representing the dotted version of the input str.
+ */
+ static QString addDots(QString s, int dots,
+ bool hyphenate, bool internationalize);
+
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationTool.cpp b/src/gui/editors/notation/NotationTool.cpp
new file mode 100644
index 0000000..8e82107
--- /dev/null
+++ b/src/gui/editors/notation/NotationTool.cpp
@@ -0,0 +1,57 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NotationTool.h"
+#include "misc/Debug.h"
+
+#include "gui/general/EditTool.h"
+#include "NotationView.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+NotationTool::NotationTool(const QString& menuName, NotationView* view)
+ : EditTool(menuName, view),
+ m_nParentView(view)
+{}
+
+NotationTool::~NotationTool()
+{
+ NOTATION_DEBUG << "NotationTool::~NotationTool()" << endl;
+
+ // delete m_menu;
+ // m_parentView->factory()->removeClient(this);
+ // m_instance = 0;
+}
+
+void NotationTool::ready()
+{
+ m_nParentView->setCanvasCursor(Qt::arrowCursor);
+ m_nParentView->setHeightTracking(false);
+}
+
+}
diff --git a/src/gui/editors/notation/NotationTool.h b/src/gui/editors/notation/NotationTool.h
new file mode 100644
index 0000000..ab1020a
--- /dev/null
+++ b/src/gui/editors/notation/NotationTool.h
@@ -0,0 +1,93 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTATIONTOOL_H_
+#define _RG_NOTATIONTOOL_H_
+
+#include "gui/general/EditTool.h"
+
+
+class QString;
+
+
+namespace Rosegarden
+{
+
+class NotationView;
+
+
+/**
+ * Notation tool base class.
+ *
+ * A NotationTool represents one of the items on the notation toolbars
+ * (notes, rests, clefs, eraser, etc...). It handle mouse click events
+ * for the NotationView ('State' design pattern).
+ *
+ * A NotationTool can have a menu, normally activated through a right
+ * mouse button click. This menu is defined in an XML file, see
+ * NoteInserter and noteinserter.rc for an example.
+ *
+ * This class is a "semi-singleton", that is, only one instance per
+ * NotationView window is created. This is because menu creation is
+ * slow, and the fact that a tool can trigger the setting of another
+ * tool through a menu choice). This is maintained with the
+ * NotationToolBox class This means we can't rely on the ctor/dtor to
+ * perform setting up, like mouse cursor changes for instance. Use the
+ * ready() and stow() method for this.
+ *
+ * @see NotationView#setTool()
+ * @see NotationToolBox
+ */
+class NotationTool : public EditTool
+{
+ friend class NotationToolBox;
+
+public:
+ virtual ~NotationTool();
+
+ /**
+ * Is called by NotationView when the tool is set as current
+ * Add any setup here
+ */
+ virtual void ready();
+
+protected:
+ /**
+ * Create a new NotationTool
+ *
+ * \a menuName : the name of the menu defined in the XML rc file
+ */
+ NotationTool(const QString& menuName, NotationView*);
+
+ //--------------- Data members ---------------------------------
+
+ NotationView* m_nParentView;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationToolBox.cpp b/src/gui/editors/notation/NotationToolBox.cpp
new file mode 100644
index 0000000..769bcaf
--- /dev/null
+++ b/src/gui/editors/notation/NotationToolBox.cpp
@@ -0,0 +1,102 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NotationToolBox.h"
+
+#include "gui/general/EditToolBox.h"
+#include "gui/general/EditTool.h"
+#include "NotationView.h"
+#include "NoteInserter.h"
+#include "RestInserter.h"
+#include "ClefInserter.h"
+#include "TextInserter.h"
+#include "GuitarChordInserter.h"
+#include "NotationEraser.h"
+#include "NotationSelector.h"
+
+#include <qstring.h>
+#include <kmessagebox.h>
+
+namespace Rosegarden
+{
+
+NotationToolBox::NotationToolBox(NotationView *parent)
+ : EditToolBox(parent),
+ m_nParentView(parent)
+{
+ //m_tools.setAutoDelete(true);
+}
+
+EditTool* NotationToolBox::createTool(const QString& toolName)
+{
+ NotationTool* tool = 0;
+
+ QString toolNamelc = toolName.lower();
+
+ if (toolNamelc == NoteInserter::ToolName)
+
+ tool = new NoteInserter(m_nParentView);
+
+ else if (toolNamelc == RestInserter::ToolName)
+
+ tool = new RestInserter(m_nParentView);
+
+ else if (toolNamelc == ClefInserter::ToolName)
+
+ tool = new ClefInserter(m_nParentView);
+
+ else if (toolNamelc == TextInserter::ToolName)
+
+ tool = new TextInserter(m_nParentView);
+
+ else if (toolNamelc == GuitarChordInserter::ToolName)
+
+ tool = new GuitarChordInserter(m_nParentView);
+
+/* else if (toolNamelc == LilyPondDirectiveInserter::ToolName)
+
+ tool = new LilyPondDirectiveInserter(m_nParentView);*/
+
+ else if (toolNamelc == NotationEraser::ToolName)
+
+ tool = new NotationEraser(m_nParentView);
+
+ else if (toolNamelc == NotationSelector::ToolName)
+
+ tool = new NotationSelector(m_nParentView);
+
+ else {
+ KMessageBox::error(0, QString("NotationToolBox::createTool : unrecognised toolname %1 (%2)")
+ .arg(toolName).arg(toolNamelc));
+ return 0;
+ }
+
+ m_tools.insert(toolName, tool);
+
+ return tool;
+}
+
+}
+#include "NotationToolBox.moc"
diff --git a/src/gui/editors/notation/NotationToolBox.h b/src/gui/editors/notation/NotationToolBox.h
new file mode 100644
index 0000000..48b1202
--- /dev/null
+++ b/src/gui/editors/notation/NotationToolBox.h
@@ -0,0 +1,65 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTATIONTOOLBOX_H_
+#define _RG_NOTATIONTOOLBOX_H_
+
+#include "gui/general/EditToolBox.h"
+
+
+class QString;
+
+
+namespace Rosegarden
+{
+
+class NotationView;
+class EditTool;
+
+
+/**
+ * NotationToolBox : maintains a single instance of each registered tool
+ *
+ * Tools are fetched from a name
+ */
+class NotationToolBox : public EditToolBox
+{
+ Q_OBJECT
+public:
+ NotationToolBox(NotationView* parent);
+
+protected:
+ virtual EditTool* createTool(const QString& toolName);
+
+ //--------------- Data members ---------------------------------
+
+ NotationView* m_nParentView;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationVLayout.cpp b/src/gui/editors/notation/NotationVLayout.cpp
new file mode 100644
index 0000000..c746a30
--- /dev/null
+++ b/src/gui/editors/notation/NotationVLayout.cpp
@@ -0,0 +1,731 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include <cmath>
+#include "NotationVLayout.h"
+#include "misc/Debug.h"
+
+#include <klocale.h>
+#include "base/Composition.h"
+#include "base/Event.h"
+#include "base/LayoutEngine.h"
+#include "base/NotationRules.h"
+#include "base/NotationTypes.h"
+#include "base/NotationQuantizer.h"
+#include "base/Staff.h"
+#include "gui/general/ProgressReporter.h"
+#include "gui/editors/guitar/Chord.h"
+#include "NotationChord.h"
+#include "NotationElement.h"
+#include "NotationProperties.h"
+#include "NotationStaff.h"
+#include "NotePixmapFactory.h"
+#include <kmessagebox.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+using namespace BaseProperties;
+
+
+NotationVLayout::NotationVLayout(Composition *c, NotePixmapFactory *npf,
+ const NotationProperties &properties,
+ QObject* parent, const char* name) :
+ ProgressReporter(parent, name),
+ m_composition(c),
+ m_npf(npf),
+ m_notationQuantizer(c->getNotationQuantizer()),
+ m_properties(properties)
+{
+ // empty
+}
+
+NotationVLayout::~NotationVLayout()
+{
+ // empty
+}
+
+NotationVLayout::SlurList &
+
+NotationVLayout::getSlurList(Staff &staff)
+{
+ SlurListMap::iterator i = m_slurs.find(&staff);
+ if (i == m_slurs.end()) {
+ m_slurs[&staff] = SlurList();
+ }
+
+ return m_slurs[&staff];
+}
+
+void
+NotationVLayout::reset()
+{
+ m_slurs.clear();
+}
+
+void
+NotationVLayout::resetStaff(Staff &staff, timeT, timeT)
+{
+ getSlurList(staff).clear();
+}
+
+void
+NotationVLayout::scanStaff(Staff &staffBase, timeT, timeT)
+{
+ START_TIMING;
+
+ NotationStaff &staff = dynamic_cast<NotationStaff &>(staffBase);
+ NotationElementList *notes = staff.getViewElementList();
+
+ NotationElementList::iterator from = notes->begin();
+ NotationElementList::iterator to = notes->end();
+ NotationElementList::iterator i;
+
+ for (i = from; i != to; ++i) {
+
+ NotationElement *el = static_cast<NotationElement*>(*i);
+
+ // Displaced Y will only be used for certain events -- in
+ // particular not for notes, whose y-coord is obviously kind
+ // of meaningful.
+ double displacedY = 0.0;
+ long dyRaw = 0;
+ el->event()->get<Int>(DISPLACED_Y, dyRaw);
+ displacedY = double(dyRaw * m_npf->getLineSpacing()) / 1000.0;
+
+ el->setLayoutY(staff.getLayoutYForHeight( -9) + displacedY);
+
+ if (el->isRest()) {
+
+ // rests for notes longer than the minim have hotspots
+ // aligned with the line above the middle line; the rest
+ // are aligned with the middle line
+
+ long noteType;
+ bool hasNoteType = el->event()->get
+ <Int>(NOTE_TYPE, noteType);
+ if (hasNoteType && noteType > Note::Minim) {
+ el->setLayoutY(staff.getLayoutYForHeight(6) + displacedY);
+ } else {
+ el->setLayoutY(staff.getLayoutYForHeight(4) + displacedY);
+ }
+
+ // Fix for bug 1090767 Rests outside staves have wrong glyphs
+ // by William <rosegarden4c AT orthoset.com>
+ // We use a "rest-outside-stave" glyph for any minim/semibreve/breve
+ // rest that has been displaced vertically e.g. by fine-positioning
+ // outside the stave. For small vertical displacements that keep
+ // the rest inside the stave, we use the "rest-inside-stave" glyph
+ // and also discretise the displacement into multiples of the
+ // stave-line spacing. The outside-stave glyphs match the character
+ // numbers 1D13A, 1D13B and 1D13C in the Unicode 4.0 standard.
+
+ if (hasNoteType && (displacedY > 0.1 || displacedY < -0.1)) {
+
+ // a fiddly check for transition from inside to outside:
+
+ int min = -1, max = 1;
+
+ switch (noteType) {
+ case Note::Breve:
+ min = -1;
+ max = 2;
+ break;
+ case Note::Semibreve:
+ min = -1;
+ max = 3;
+ break;
+ case Note::Minim:
+ min = -2;
+ max = 2;
+ break;
+ case Note::Crotchet:
+ min = -1;
+ max = 3;
+ break;
+ case Note::Quaver:
+ min = -2;
+ max = 3;
+ break;
+ case Note::Semiquaver:
+ min = -3;
+ max = 3;
+ break;
+ case Note::Demisemiquaver:
+ min = -3;
+ max = 4;
+ break;
+ case Note::Hemidemisemiquaver:
+ min = -4;
+ max = 4;
+ break;
+ }
+
+ bool outside = false;
+
+ if (noteType == Note::Breve) {
+ if (nearbyint(displacedY) < min * m_npf->getLineSpacing() ||
+ nearbyint(displacedY) > max * m_npf->getLineSpacing()) {
+ outside = true;
+ }
+ } else {
+ if ((int)displacedY < min * m_npf->getLineSpacing() ||
+ (int)displacedY > max * m_npf->getLineSpacing()) {
+ outside = true;
+ }
+ }
+
+ el->event()->setMaybe<Bool>(m_properties.REST_OUTSIDE_STAVE,
+ outside);
+
+ if (!outside) {
+ displacedY = (double)m_npf->getLineSpacing() *
+ (int(nearbyint((double)displacedY /
+ m_npf->getLineSpacing())));
+ if (noteType > Note::Minim)
+ el->setLayoutY(staff.getLayoutYForHeight(6) + displacedY);
+ else
+ el->setLayoutY(staff.getLayoutYForHeight(4) + displacedY);
+ }
+
+ // if (displacedY != 0.0)
+ // NOTATION_DEBUG << "REST_OUTSIDE_STAVE AFTER "
+ // << " : displacedY : " << displacedY
+ // << " line-spacing : " << m_npf->getLineSpacing()
+ // << " time : " << (el->getViewAbsoluteTime())
+ // << endl;
+ } else {
+ el->event()->setMaybe<Bool>(m_properties.REST_OUTSIDE_STAVE,
+ false);
+ }
+
+ } else if (el->isNote()) {
+
+ NotationChord chord(*notes, i, m_notationQuantizer, m_properties);
+ if (chord.size() == 0)
+ continue;
+
+ std::vector<int> h;
+ for (unsigned int j = 0; j < chord.size(); ++j) {
+ long height = 0;
+ if (!(*chord[j])->event()->get
+ <Int>
+ (m_properties.HEIGHT_ON_STAFF, height)) {
+ std::cerr << QString("ERROR: Event in chord at %1 has no HEIGHT_ON_STAFF property!\nThis is a bug (the program would previously have crashed by now)").arg((*chord[j])->getViewAbsoluteTime()) << std::endl;
+ (*chord[j])->event()->dump(std::cerr);
+ }
+ h.push_back(height);
+ }
+ bool stemmed = chord.hasStem();
+ bool stemUp = chord.hasStemUp();
+ bool hasNoteHeadShifted = chord.hasNoteHeadShifted();
+
+ unsigned int flaggedNote = (stemUp ? chord.size() - 1 : 0);
+
+ bool hasShifted = chord.hasNoteHeadShifted();
+
+ double y0 = -1E50; // A very unlikely Y layout value
+
+ for (unsigned int j = 0; j < chord.size(); ++j) {
+
+ el = static_cast<NotationElement*>(*chord[j]);
+ el->setLayoutY(staff.getLayoutYForHeight(h[j]));
+
+ // Look for collision
+ const double eps = 0.001;
+ Event *eel = el->event();
+ double y = el->getLayoutY();
+ if (eel->has("pitch")) {
+ el->setIsColliding(fabs(y - y0) < eps);
+ y0 = y;
+ }
+
+
+ // These calculations and assignments are pretty much final
+ // if the chord is not in a beamed group, but if it is then
+ // they will be reworked by NotationGroup::applyBeam, which
+ // is called from NotationHLayout::layout, which is called
+ // after this. Any inaccuracies here for beamed groups
+ // should be stamped out there.
+
+ // el->event()->setMaybe<Bool>(STEM_UP, stemUp);
+ el->event()->setMaybe<Bool>(m_properties.VIEW_LOCAL_STEM_UP, stemUp);
+
+ bool primary =
+ ((stemmed && stemUp) ? (j == 0) : (j == chord.size() - 1));
+ el->event()->setMaybe<Bool>
+ (m_properties.CHORD_PRIMARY_NOTE, primary);
+
+ if (primary) {
+ el->event()->setMaybe<Int>
+ (m_properties.CHORD_MARK_COUNT, chord.getMarkCountForChord());
+ }
+
+ bool shifted = chord.isNoteHeadShifted(chord[j]);
+ el->event()->setMaybe<Bool>
+ (m_properties.NOTE_HEAD_SHIFTED, shifted);
+
+ el->event()->setMaybe<Bool>
+ (m_properties.NOTE_DOT_SHIFTED, false);
+ if (hasShifted && stemUp) {
+ long dots = 0;
+ (void)el->event()->get
+ <Int>(NOTE_DOTS, dots);
+ if (dots > 0) {
+ el->event()->setMaybe<Bool>
+ (m_properties.NOTE_DOT_SHIFTED, true);
+ }
+ }
+
+ el->event()->setMaybe<Bool>
+ (m_properties.NEEDS_EXTRA_SHIFT_SPACE,
+ hasNoteHeadShifted && !stemUp);
+
+ el->event()->setMaybe<Bool>
+ (m_properties.DRAW_FLAG, j == flaggedNote);
+
+ int stemLength = -1;
+ if (j != flaggedNote) {
+ stemLength = staff.getLayoutYForHeight(h[flaggedNote]) -
+ staff.getLayoutYForHeight(h[j]);
+ if (stemLength < 0)
+ stemLength = -stemLength;
+ // NOTATION_DEBUG << "Setting stem length to "
+ // << stemLength << endl;
+ } else {
+ int minStemLength = stemLength;
+ if (h[j] < -2 && stemUp) {
+ //!!! needs tuning, & applying for beamed stems too
+ minStemLength = staff.getLayoutYForHeight(h[j]) -
+ staff.getLayoutYForHeight(4);
+ } else if (h[j] > 10 && !stemUp) {
+ minStemLength = staff.getLayoutYForHeight(4) -
+ staff.getLayoutYForHeight(h[j]);
+ }
+ stemLength = std::max(minStemLength, stemLength);
+ }
+
+ el->event()->setMaybe<Int>
+ (m_properties.UNBEAMED_STEM_LENGTH, stemLength);
+ }
+
+
+ // #938545 (Broken notation: Duplicated note can float
+ // outside stave) -- Need to cope with the case where a
+ // note that's not a member of a chord (different stem
+ // direction &c) falls between notes that are members.
+ // Not optimal, as we can end up scanning the chord
+ // multiple times (we'll return to it after scanning the
+ // contained note). [We can't just iterate over all
+ // elements within the chord (as we can in hlayout)
+ // because we need them in height order.]
+
+ i = chord.getFirstElementNotInChord();
+ if (i == notes->end())
+ i = chord.getFinalElement();
+ else
+ --i;
+
+ } else {
+
+ if (el->event()->isa(Clef::EventType)) {
+
+ // clef pixmaps have the hotspot placed to coincide
+ // with the pitch of the clef -- so the alto clef
+ // should be "on" the middle line, the treble clef
+ // "on" the line below the middle, etc
+
+ el->setLayoutY(staff.getLayoutYForHeight
+ (Clef(*el->event()).getAxisHeight()));
+
+ } else if (el->event()->isa(Rosegarden::Key::EventType)) {
+
+ el->setLayoutY(staff.getLayoutYForHeight(12));
+
+ } else if (el->event()->isa(Text::EventType)) {
+
+ std::string type = Text::UnspecifiedType;
+ el->event()->get<String>(Text::TextTypePropertyName, type);
+
+ if (type == Text::Dynamic ||
+ type == Text::LocalDirection ||
+ type == Text::UnspecifiedType) {
+ el->setLayoutY(staff.getLayoutYForHeight(-7) + displacedY);
+ } else if (type == Text::Lyric) {
+ long verse = 0;
+ el->event()->get<Int>(Text::LyricVersePropertyName, verse);
+ el->setLayoutY(staff.getLayoutYForHeight(-10 - 3 * verse) + displacedY);
+ } else if (type == Text::Annotation) {
+ el->setLayoutY(staff.getLayoutYForHeight(-13) + displacedY);
+ } else {
+ el->setLayoutY(staff.getLayoutYForHeight(22) + displacedY);
+ }
+
+ } else if (el->event()->isa(Indication::EventType)) {
+
+ try {
+ std::string indicationType =
+ el->event()->get
+ <String>(Indication::IndicationTypePropertyName);
+
+ if (indicationType == Indication::Slur ||
+ indicationType == Indication::PhrasingSlur) {
+ getSlurList(staff).push_back(i);
+ }
+
+ if (indicationType == Indication::OttavaUp ||
+ indicationType == Indication::QuindicesimaUp) {
+ el->setLayoutY(staff.getLayoutYForHeight(15) + displacedY);
+ } else {
+ el->setLayoutY(staff.getLayoutYForHeight( -9) + displacedY);
+ }
+ } catch (...) {
+ el->setLayoutY(staff.getLayoutYForHeight( -9) + displacedY);
+ }
+
+ } else if (el->event()->isa(Guitar::Chord::EventType)) {
+
+ el->setLayoutY(staff.getLayoutYForHeight(22) + displacedY);
+ }
+ }
+ }
+
+ PRINT_ELAPSED("NotationVLayout::scanStaff");
+}
+
+void
+NotationVLayout::finishLayout(timeT, timeT)
+{
+ START_TIMING;
+
+ for (SlurListMap::iterator mi = m_slurs.begin();
+ mi != m_slurs.end(); ++mi) {
+
+ for (SlurList::iterator si = mi->second.begin();
+ si != mi->second.end(); ++si) {
+
+ NotationElementList::iterator i = *si;
+ NotationStaff &staff = dynamic_cast<NotationStaff &>(*(mi->first));
+
+ positionSlur(staff, i);
+ }
+ }
+
+ PRINT_ELAPSED("NotationVLayout::finishLayout");
+}
+
+void
+NotationVLayout::positionSlur(NotationStaff &staff,
+ NotationElementList::iterator i)
+{
+ NotationRules rules;
+
+ bool phrasing = ((*i)->event()->get
+ <String>(Indication::IndicationTypePropertyName)
+ == Indication::PhrasingSlur);
+
+ NotationElementList::iterator scooter = i;
+
+ timeT slurDuration = (*i)->event()->getDuration();
+ if (slurDuration == 0 && (*i)->event()->has("indicationduration")) {
+ slurDuration = (*i)->event()->get
+ <Int>("indicationduration"); // obs property
+ }
+ timeT endTime = (*i)->getViewAbsoluteTime() + slurDuration;
+
+ bool haveStart = false;
+
+ int startTopHeight = 4, endTopHeight = 4,
+ startBottomHeight = 4, endBottomHeight = 4,
+ maxTopHeight = 4, minBottomHeight = 4,
+ maxCount = 0, minCount = 0;
+
+ int startX = (int)(*i)->getLayoutX(), endX = startX + 10;
+ bool startStemUp = false, endStemUp = false;
+ long startMarks = 0, endMarks = 0;
+ bool startTied = false, endTied = false;
+ bool beamAbove = false, beamBelow = false;
+ bool dynamic = false;
+
+ std::vector<Event *> stemUpNotes, stemDownNotes;
+
+ // Scan the notes spanned by the slur, recording the top and
+ // bottom heights of the first and last chords, plus the presence
+ // of any troublesome beams and high or low notes in the body.
+
+ while (scooter != staff.getViewElementList()->end()) {
+
+ if ((*scooter)->getViewAbsoluteTime() >= endTime)
+ break;
+ Event *event = (*scooter)->event();
+
+ if (event->isa(Note::EventType)) {
+
+ long h = 0;
+ if (!event->get
+ <Int>(m_properties.HEIGHT_ON_STAFF, h)) {
+ KMessageBox::sorry
+ ((QWidget *)parent(), i18n("Spanned note at %1 has no HEIGHT_ON_STAFF property!\nThis is a bug (the program would previously have crashed by now)").arg((*scooter)->getViewAbsoluteTime()));
+ event->dump(std::cerr);
+ }
+
+ bool stemUp = rules.isStemUp(h);
+ event->get
+ <Bool>(m_properties.VIEW_LOCAL_STEM_UP, stemUp);
+
+ bool beamed = false;
+ event->get
+ <Bool>(m_properties.BEAMED, beamed);
+
+ bool primary = false;
+
+ if (event->get
+ <Bool>
+ (m_properties.CHORD_PRIMARY_NOTE, primary) && primary) {
+
+ NotationChord chord(*(staff.getViewElementList()), scooter,
+ m_notationQuantizer, m_properties);
+
+ if (beamed) {
+ if (stemUp)
+ beamAbove = true;
+ else
+ beamBelow = true;
+ }
+
+ if (!haveStart) {
+
+ startBottomHeight = chord.getLowestNoteHeight();
+ startTopHeight = chord.getHighestNoteHeight();
+ minBottomHeight = startBottomHeight;
+ maxTopHeight = startTopHeight;
+
+ startX = (int)(*scooter)->getLayoutX();
+ startStemUp = stemUp;
+ startMarks = chord.getMarkCountForChord();
+
+ bool tied = false;
+ if ((event->get
+ <Bool>(TIED_FORWARD, tied) && tied) ||
+ (event->get<Bool>(TIED_BACKWARD, tied) && tied)) {
+ startTied = true;
+ }
+
+ haveStart = true;
+
+ } else {
+ if (chord.getLowestNoteHeight() < minBottomHeight) {
+ minBottomHeight = chord.getLowestNoteHeight();
+ ++minCount;
+ }
+ if (chord.getHighestNoteHeight() > maxTopHeight) {
+ maxTopHeight = chord.getHighestNoteHeight();
+ ++maxCount;
+ }
+ }
+
+ endBottomHeight = chord.getLowestNoteHeight();
+ endTopHeight = chord.getHighestNoteHeight();
+ endX = (int)(*scooter)->getLayoutX();
+ endStemUp = stemUp;
+ endMarks = chord.getMarkCountForChord();
+
+ bool tied = false;
+ if ((event->get
+ <Bool>(TIED_FORWARD, tied) && tied) ||
+ (event->get<Bool>(TIED_BACKWARD, tied) && tied)) {
+ endTied = true;
+ }
+ }
+
+ if (!beamed) {
+ if (stemUp)
+ stemUpNotes.push_back(event);
+ else
+ stemDownNotes.push_back(event);
+ }
+
+ } else if (event->isa(Indication::EventType)) {
+
+ try {
+ std::string indicationType =
+ event->get
+ <String>(Indication::IndicationTypePropertyName);
+
+ if (indicationType == Indication::Crescendo ||
+ indicationType == Indication::Decrescendo)
+ dynamic = true;
+ } catch (...) { }
+ }
+
+ ++scooter;
+ }
+
+ bool above = true;
+
+ if ((*i)->event()->has(NotationProperties::SLUR_ABOVE) &&
+ (*i)->event()->isPersistent<Bool>(NotationProperties::SLUR_ABOVE)) {
+
+ (*i)->event()->get
+ <Bool>(NotationProperties::SLUR_ABOVE, above);
+
+ } else if (phrasing) {
+
+ int score = 0; // for "above"
+
+ if (dynamic)
+ score += 2;
+
+ if (startStemUp == endStemUp) {
+ if (startStemUp)
+ score -= 2;
+ else
+ score += 2;
+ } else if (beamBelow != beamAbove) {
+ if (beamAbove)
+ score -= 2;
+ else
+ score += 2;
+ }
+
+ if (maxTopHeight < 6)
+ score += 1;
+ else if (minBottomHeight > 2)
+ score -= 1;
+
+ if (stemUpNotes.size() != stemDownNotes.size()) {
+ if (stemUpNotes.size() < stemDownNotes.size())
+ score += 1;
+ else
+ score -= 1;
+ }
+
+ above = (score >= 0);
+
+ } else {
+
+ if (startStemUp == endStemUp) {
+ above = !startStemUp;
+ } else if (beamBelow) {
+ above = true;
+ } else if (beamAbove) {
+ above = false;
+ } else if (stemUpNotes.size() != stemDownNotes.size()) {
+ above = (stemUpNotes.size() < stemDownNotes.size());
+ } else {
+ above = ((startTopHeight - 4) + (endTopHeight - 4) +
+ (4 - startBottomHeight) + (4 - endBottomHeight) <= 8);
+ }
+ }
+
+ // now choose the actual y-coord of the slur based on the side
+ // we've decided to put it on
+
+ int startHeight, endHeight;
+ int startOffset = 2, endOffset = 2;
+
+ if (above) {
+
+ if (!startStemUp)
+ startOffset += startMarks * 2;
+ else
+ startOffset += 5;
+
+ if (!endStemUp)
+ endOffset += startMarks * 2;
+ else
+ endOffset += 5;
+
+ startHeight = startTopHeight + startOffset;
+ endHeight = endTopHeight + endOffset;
+
+ bool maxRelevant = ((maxTopHeight != endTopHeight) || (maxCount > 1));
+ if (maxRelevant) {
+ int midHeight = (startHeight + endHeight) / 2;
+ if (maxTopHeight > midHeight - 1) {
+ startHeight += maxTopHeight - midHeight + 1;
+ endHeight += maxTopHeight - midHeight + 1;
+ }
+ }
+
+ } else {
+
+ if (startStemUp)
+ startOffset += startMarks * 2;
+ else
+ startOffset += 5;
+
+ if (endStemUp)
+ endOffset += startMarks * 2;
+ else
+ endOffset += 5;
+
+ startHeight = startBottomHeight - startOffset;
+ endHeight = endBottomHeight - endOffset;
+
+ bool minRelevant = ((minBottomHeight != endBottomHeight) || (minCount > 1));
+ if (minRelevant) {
+ int midHeight = (startHeight + endHeight) / 2;
+ if (minBottomHeight < midHeight + 1) {
+ startHeight -= midHeight - minBottomHeight + 1;
+ endHeight -= midHeight - minBottomHeight + 1;
+ }
+ }
+ }
+
+ int y0 = staff.getLayoutYForHeight(startHeight),
+ y1 = staff.getLayoutYForHeight(endHeight);
+
+ int dy = y1 - y0;
+ int length = endX - startX;
+ int diff = staff.getLayoutYForHeight(0) - staff.getLayoutYForHeight(3);
+ if (length < diff*10)
+ diff /= 2;
+ if (length > diff*3)
+ length -= diff / 2;
+ startX += diff;
+
+ (*i)->event()->setMaybe<Bool>(NotationProperties::SLUR_ABOVE, above);
+ (*i)->event()->setMaybe<Int>(m_properties.SLUR_Y_DELTA, dy);
+ (*i)->event()->setMaybe<Int>(m_properties.SLUR_LENGTH, length);
+
+ double displacedX = 0.0, displacedY = 0.0;
+
+ long dxRaw = 0;
+ (*i)->event()->get<Int>(DISPLACED_X, dxRaw);
+ displacedX = double(dxRaw * m_npf->getNoteBodyWidth()) / 1000.0;
+
+ long dyRaw = 0;
+ (*i)->event()->get<Int>(DISPLACED_Y, dyRaw);
+ displacedY = double(dyRaw * m_npf->getLineSpacing()) / 1000.0;
+
+ (*i)->setLayoutX(startX + displacedX);
+ (*i)->setLayoutY(y0 + displacedY);
+}
+
+}
diff --git a/src/gui/editors/notation/NotationVLayout.h b/src/gui/editors/notation/NotationVLayout.h
new file mode 100644
index 0000000..83a16c1
--- /dev/null
+++ b/src/gui/editors/notation/NotationVLayout.h
@@ -0,0 +1,122 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTATIONVLAYOUT_H_
+#define _RG_NOTATIONVLAYOUT_H_
+
+#include "base/FastVector.h"
+#include "base/LayoutEngine.h"
+#include "gui/general/ProgressReporter.h"
+#include <map>
+#include "base/Event.h"
+
+#include "NotationElement.h"
+
+
+class SlurList;
+class QObject;
+
+
+namespace Rosegarden
+{
+
+class Staff;
+class Quantizer;
+class Composition;
+class NotePixmapFactory;
+class NotationStaff;
+class NotationProperties;
+class Composition;
+
+
+/**
+ * Vertical notation layout
+ *
+ * computes the Y coordinate of notation elements
+ */
+
+class NotationVLayout : public ProgressReporter,
+ public VerticalLayoutEngine
+{
+public:
+ NotationVLayout(Composition *c, NotePixmapFactory *npf,
+ const NotationProperties &properties,
+ QObject* parent, const char* name = 0);
+
+ virtual ~NotationVLayout();
+
+ void setNotePixmapFactory(NotePixmapFactory *npf) {
+ m_npf = npf;
+ }
+
+ /**
+ * Resets internal data stores for all staffs
+ */
+ virtual void reset();
+
+ /**
+ * Resets internal data stores for a specific staff
+ */
+ virtual void resetStaff(Staff &,
+ timeT = 0,
+ timeT = 0);
+
+ /**
+ * Lay out a single staff.
+ */
+ virtual void scanStaff(Staff &,
+ timeT = 0,
+ timeT = 0);
+
+ /**
+ * Do any layout dependent on more than one staff. As it
+ * happens, we have none, but we do have some layout that
+ * depends on the final results from the horizontal layout
+ * (for slurs), so we should do that here
+ */
+ virtual void finishLayout(timeT = 0,
+ timeT = 0);
+
+private:
+ void positionSlur(NotationStaff &staff, NotationElementList::iterator i);
+
+ typedef FastVector<NotationElementList::iterator> SlurList;
+ typedef std::map<Staff *, SlurList> SlurListMap;
+
+ //--------------- Data members ---------------------------------
+
+ SlurListMap m_slurs;
+ SlurList &getSlurList(Staff &);
+
+ Composition *m_composition;
+ NotePixmapFactory *m_npf;
+ const Quantizer *m_notationQuantizer;
+ const NotationProperties &m_properties;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationView.cpp b/src/gui/editors/notation/NotationView.cpp
new file mode 100644
index 0000000..66cb4b3
--- /dev/null
+++ b/src/gui/editors/notation/NotationView.cpp
@@ -0,0 +1,7552 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NotationView.h"
+#include <list>
+#include <qlayout.h>
+#include "misc/Debug.h"
+#include <kapplication.h>
+
+#include "gui/editors/segment/TrackEditor.h"
+#include "gui/editors/segment/TrackButtons.h"
+#include "base/BaseProperties.h"
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Strings.h"
+#include "base/AnalysisTypes.h"
+#include "base/Clipboard.h"
+#include "base/Composition.h"
+#include "base/CompositionTimeSliceAdapter.h"
+#include "base/Configuration.h"
+#include "base/Device.h"
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/MidiTypes.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/PropertyName.h"
+#include "base/NotationQuantizer.h"
+#include "base/RealTime.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/Staff.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "ClefInserter.h"
+#include "commands/edit/AddDotCommand.h"
+#include "commands/edit/ClearTriggersCommand.h"
+#include "commands/edit/CollapseNotesCommand.h"
+#include "commands/edit/CopyCommand.h"
+#include "commands/edit/CutAndCloseCommand.h"
+#include "commands/edit/CutCommand.h"
+#include "commands/edit/EraseCommand.h"
+#include "commands/edit/EventEditCommand.h"
+#include "commands/edit/EventQuantizeCommand.h"
+#include "commands/edit/InsertTriggerNoteCommand.h"
+#include "commands/edit/PasteEventsCommand.h"
+#include "commands/edit/SetLyricsCommand.h"
+#include "commands/edit/SetNoteTypeCommand.h"
+#include "commands/edit/SetTriggerCommand.h"
+#include "commands/edit/TransposeCommand.h"
+#include "commands/notation/AddFingeringMarkCommand.h"
+#include "commands/notation/AddIndicationCommand.h"
+#include "commands/notation/AddMarkCommand.h"
+#include "commands/notation/AddSlashesCommand.h"
+#include "commands/notation/AddTextMarkCommand.h"
+#include "commands/notation/AutoBeamCommand.h"
+#include "commands/notation/BeamCommand.h"
+#include "commands/notation/BreakCommand.h"
+#include "commands/notation/ChangeSlurPositionCommand.h"
+#include "commands/notation/ChangeTiePositionCommand.h"
+#include "commands/notation/ChangeStemsCommand.h"
+#include "commands/notation/ChangeStyleCommand.h"
+#include "commands/notation/ClefInsertionCommand.h"
+#include "commands/notation/CollapseRestsCommand.h"
+#include "commands/notation/DeCounterpointCommand.h"
+#include "commands/notation/EraseEventCommand.h"
+#include "commands/notation/FixNotationQuantizeCommand.h"
+#include "commands/notation/IncrementDisplacementsCommand.h"
+#include "commands/notation/InterpretCommand.h"
+#include "commands/notation/KeyInsertionCommand.h"
+#include "commands/notation/MakeAccidentalsCautionaryCommand.h"
+#include "commands/notation/MakeChordCommand.h"
+#include "commands/notation/MakeNotesViableCommand.h"
+#include "commands/notation/MultiKeyInsertionCommand.h"
+#include "commands/notation/NormalizeRestsCommand.h"
+#include "commands/notation/RemoveFingeringMarksCommand.h"
+#include "commands/notation/RemoveMarksCommand.h"
+#include "commands/notation/RemoveNotationQuantizeCommand.h"
+#include "commands/notation/ResetDisplacementsCommand.h"
+#include "commands/notation/RespellCommand.h"
+#include "commands/notation/RestoreSlursCommand.h"
+#include "commands/notation/RestoreTiesCommand.h"
+#include "commands/notation/RestoreStemsCommand.h"
+#include "commands/notation/SetVisibilityCommand.h"
+#include "commands/notation/SustainInsertionCommand.h"
+#include "commands/notation/TextInsertionCommand.h"
+#include "commands/notation/TieNotesCommand.h"
+#include "commands/notation/TupletCommand.h"
+#include "commands/notation/UntieNotesCommand.h"
+#include "commands/notation/UnTupletCommand.h"
+#include "commands/segment/PasteToTriggerSegmentCommand.h"
+#include "commands/segment/SegmentSyncCommand.h"
+#include "commands/segment/SegmentTransposeCommand.h"
+#include "commands/segment/RenameTrackCommand.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "document/io/LilyPondExporter.h"
+#include "GuitarChordInserter.h"
+#include "gui/application/SetWaitCursor.h"
+#include "gui/application/RosegardenGUIView.h"
+#include "gui/dialogs/ClefDialog.h"
+#include "gui/dialogs/EventEditDialog.h"
+#include "gui/dialogs/InterpretDialog.h"
+#include "gui/dialogs/IntervalDialog.h"
+#include "gui/dialogs/KeySignatureDialog.h"
+#include "gui/dialogs/LilyPondOptionsDialog.h"
+#include "gui/dialogs/LyricEditDialog.h"
+#include "gui/dialogs/MakeOrnamentDialog.h"
+#include "gui/dialogs/PasteNotationDialog.h"
+#include "gui/dialogs/QuantizeDialog.h"
+#include "gui/dialogs/SimpleEventEditDialog.h"
+#include "gui/dialogs/TextEventDialog.h"
+#include "gui/dialogs/TupletDialog.h"
+#include "gui/dialogs/UseOrnamentDialog.h"
+#include "gui/rulers/StandardRuler.h"
+#include "gui/general/ActiveItem.h"
+#include "gui/general/ClefIndex.h"
+#include "gui/general/EditViewBase.h"
+#include "gui/general/EditView.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/LinedStaff.h"
+#include "gui/general/LinedStaffManager.h"
+#include "gui/general/ProgressReporter.h"
+#include "gui/general/PresetHandlerDialog.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "gui/kdeext/KTmpStatusMsg.h"
+#include "gui/kdeext/QCanvasSimpleSprite.h"
+#include "gui/rulers/ChordNameRuler.h"
+#include "gui/rulers/RawNoteRuler.h"
+#include "gui/rulers/TempoRuler.h"
+#include "gui/rulers/LoopRuler.h"
+#include "gui/studio/StudioControl.h"
+#include "gui/dialogs/EventFilterDialog.h"
+#include "gui/widgets/ProgressBar.h"
+#include "gui/widgets/ProgressDialog.h"
+#include "gui/widgets/ScrollBoxDialog.h"
+#include "gui/widgets/ScrollBox.h"
+#include "gui/widgets/QDeferScrollView.h"
+#include "NotationCanvasView.h"
+#include "NotationElement.h"
+#include "NotationEraser.h"
+#include "NotationHLayout.h"
+#include "NotationProperties.h"
+#include "NotationSelector.h"
+#include "NotationStaff.h"
+#include "NotationStrings.h"
+#include "NotationToolBox.h"
+#include "NotationVLayout.h"
+#include "NoteFontFactory.h"
+#include "NoteInserter.h"
+#include "NotePixmapFactory.h"
+#include "NoteStyleFactory.h"
+#include "NoteStyle.h"
+#include "RestInserter.h"
+#include "sound/MappedEvent.h"
+#include "TextInserter.h"
+#include "HeadersGroup.h"
+#include <kaction.h>
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <klineeditdlg.h>
+#include <kmessagebox.h>
+#include <kprinter.h>
+#include <kprocess.h>
+#include <kprogress.h>
+#include <kstatusbar.h>
+#include <kstdaction.h>
+#include <ktempfile.h>
+#include <ktoolbar.h>
+#include <kxmlguiclient.h>
+#include <qbrush.h>
+#include <qcanvas.h>
+#include <qcursor.h>
+#include <qdialog.h>
+#include <qevent.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qhbox.h>
+#include <qiconset.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qpaintdevicemetrics.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qprinter.h>
+#include <qrect.h>
+#include <qregexp.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qtimer.h>
+#include <qwidget.h>
+#include <qvalidator.h>
+#include <algorithm>
+#include <qpushbutton.h>
+#include <qtooltip.h>
+
+
+namespace Rosegarden
+{
+
+class NoteActionData
+{
+public:
+ NoteActionData();
+ NoteActionData(const QString& _title,
+ QString _actionName,
+ QString _pixmapName,
+ int _keycode,
+ bool _rest,
+ Note::Type _noteType,
+ int _dots);
+
+ QString title;
+ QString actionName;
+ QString pixmapName;
+ int keycode;
+ bool rest;
+ Note::Type noteType;
+ int dots;
+};
+
+NoteActionData::NoteActionData()
+ : title(0),
+ actionName(0),
+ pixmapName(0),
+ keycode(0),
+ rest(false),
+ noteType(0),
+ dots(0)
+{
+}
+
+NoteActionData::NoteActionData(const QString& _title,
+ QString _actionName,
+ QString _pixmapName,
+ int _keycode,
+ bool _rest,
+ Note::Type _noteType,
+ int _dots)
+ : title(_title),
+ actionName(_actionName),
+ pixmapName(_pixmapName),
+ keycode(_keycode),
+ rest(_rest),
+ noteType(_noteType),
+ dots(_dots)
+{
+}
+
+
+class NoteChangeActionData
+{
+public:
+ NoteChangeActionData();
+ NoteChangeActionData(const QString &_title,
+ QString _actionName,
+ QString _pixmapName,
+ int _keycode,
+ bool _notationOnly,
+ Note::Type _noteType);
+
+ QString title;
+ QString actionName;
+ QString pixmapName;
+ int keycode;
+ bool notationOnly;
+ Note::Type noteType;
+};
+
+NoteChangeActionData::NoteChangeActionData()
+ : title(0),
+ actionName(0),
+ pixmapName(0),
+ keycode(0),
+ notationOnly(false),
+ noteType(0)
+{
+}
+
+NoteChangeActionData::NoteChangeActionData(const QString& _title,
+ QString _actionName,
+ QString _pixmapName,
+ int _keycode,
+ bool _notationOnly,
+ Note::Type _noteType)
+ : title(_title),
+ actionName(_actionName),
+ pixmapName(_pixmapName),
+ keycode(_keycode),
+ notationOnly(_notationOnly),
+ noteType(_noteType)
+{
+}
+
+
+class MarkActionData
+{
+public:
+ MarkActionData() :
+ title(0),
+ actionName(0),
+ keycode(0) { }
+
+ MarkActionData(const QString &_title,
+ QString _actionName,
+ int _keycode,
+ Mark _mark) :
+ title(_title),
+ actionName(_actionName),
+ keycode(_keycode),
+ mark(_mark) { }
+
+ QString title;
+ QString actionName;
+ int keycode;
+ Mark mark;
+};
+
+
+NotationView::NotationView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ QWidget *parent,
+ bool showProgressive) :
+ EditView(doc, segments, 2, parent, "notationview"),
+ m_properties(getViewLocalPropertyPrefix()),
+ m_selectionCounter(0),
+ m_insertModeLabel(0),
+ m_annotationsLabel(0),
+ m_lilyPondDirectivesLabel(0),
+ m_progressBar(0),
+ m_currentNotePixmap(0),
+ m_hoveredOverNoteName(0),
+ m_hoveredOverAbsoluteTime(0),
+ m_currentStaff( -1),
+ m_lastFinishingStaff( -1),
+ m_title(0),
+ m_subtitle(0),
+ m_composer(0),
+ m_copyright(0),
+ m_insertionTime(0),
+ m_deferredCursorMove(NoCursorMoveNeeded),
+ m_lastNoteAction("crotchet"),
+ m_fontName(NoteFontFactory::getDefaultFontName()),
+ m_fontSize(NoteFontFactory::getDefaultSize(m_fontName)),
+ m_pageMode(LinedStaff::LinearMode),
+ m_leftGutter(20),
+ m_notePixmapFactory(new NotePixmapFactory(m_fontName, m_fontSize)),
+ m_hlayout(new NotationHLayout(&doc->getComposition(), m_notePixmapFactory,
+ m_properties, this)),
+ m_vlayout(new NotationVLayout(&doc->getComposition(), m_notePixmapFactory,
+ m_properties, this)),
+ m_chordNameRuler(0),
+ m_tempoRuler(0),
+ m_rawNoteRuler(0),
+ m_annotationsVisible(false),
+ m_lilyPondDirectivesVisible(false),
+ m_selectDefaultNote(0),
+ m_fontCombo(0),
+ m_fontSizeCombo(0),
+ m_spacingCombo(0),
+ m_fontSizeActionMenu(0),
+ m_pannerDialog(new ScrollBoxDialog(this, ScrollBox::FixHeight)),
+ m_renderTimer(0),
+ m_playTracking(true),
+ m_progressDisplayer(PROGRESS_NONE),
+ m_inhibitRefresh(true),
+ m_ok(false),
+ m_printMode(false),
+ m_printSize(8), // set in positionStaffs
+ m_showHeadersGroup(0),
+ m_headersGroupView(0),
+ m_headersGroup(0),
+ m_headersTopFrame(0),
+ m_showHeadersMenuEntry(0)
+{
+ initActionDataMaps(); // does something only the 1st time it's called
+
+ m_toolBox = new NotationToolBox(this);
+
+ assert(segments.size() > 0);
+ NOTATION_DEBUG << "NotationView ctor" << endl;
+
+
+ // Initialise the display-related defaults that will be needed
+ // by both the actions and the layout toolbar
+
+ m_config->setGroup(NotationViewConfigGroup);
+
+ m_showHeadersGroup = m_config->readNumEntry("shownotationheader",
+ HeadersGroup::DefaultShowMode);
+
+ m_fontName = qstrtostr(m_config->readEntry
+ ("notefont",
+ strtoqstr(NoteFontFactory::getDefaultFontName())));
+
+ try
+ {
+ (void)NoteFontFactory::getFont
+ (m_fontName,
+ NoteFontFactory::getDefaultSize(m_fontName));
+ } catch (Exception e)
+ {
+ m_fontName = NoteFontFactory::getDefaultFontName();
+ }
+
+ m_fontSize = m_config->readUnsignedNumEntry
+ ((segments.size() > 1 ? "multistaffnotesize" : "singlestaffnotesize"),
+ NoteFontFactory::getDefaultSize(m_fontName));
+
+ int defaultSpacing = m_config->readNumEntry("spacing", 100);
+ m_hlayout->setSpacing(defaultSpacing);
+
+ int defaultProportion = m_config->readNumEntry("proportion", 60);
+ m_hlayout->setProportion(defaultProportion);
+
+ delete m_notePixmapFactory;
+ m_notePixmapFactory = new NotePixmapFactory(m_fontName, m_fontSize);
+ m_hlayout->setNotePixmapFactory(m_notePixmapFactory);
+ m_vlayout->setNotePixmapFactory(m_notePixmapFactory);
+
+ setupActions();
+ // setupAddControlRulerMenu(); - too early for notation, moved to end of ctor.
+ initLayoutToolbar();
+ initStatusBar();
+
+ setBackgroundMode(PaletteBase);
+
+ QCanvas *tCanvas = new QCanvas(this);
+ tCanvas->resize(width() * 2, height() * 2);
+
+ setCanvasView(new NotationCanvasView(*this, tCanvas, getCentralWidget()));
+
+ updateViewCaption();
+
+ m_chordNameRuler = new ChordNameRuler
+ (m_hlayout, doc, segments, m_leftGutter, 20, getCentralWidget());
+ addRuler(m_chordNameRuler);
+ if (showProgressive)
+ m_chordNameRuler->show();
+
+ m_tempoRuler = new TempoRuler
+ (m_hlayout, doc, this, m_leftGutter, 24, false, getCentralWidget());
+ addRuler(m_tempoRuler);
+ m_tempoRuler->hide();
+ static_cast<TempoRuler *>(m_tempoRuler)->connectSignals();
+
+ m_rawNoteRuler = new RawNoteRuler
+ (m_hlayout, segments[0], m_leftGutter, 20, getCentralWidget());
+ addRuler(m_rawNoteRuler);
+ m_rawNoteRuler->show();
+
+ // All toolbars should be created before this is called
+ setAutoSaveSettings("NotationView", true);
+
+ // All rulers must have been created before this is called,
+ // or the program will crash
+ readOptions();
+
+
+ setBottomStandardRuler(new StandardRuler(getDocument(), m_hlayout, m_leftGutter, 25,
+ true, getBottomWidget()));
+
+ for (unsigned int i = 0; i < segments.size(); ++i)
+ {
+ m_staffs.push_back(new NotationStaff
+ (canvas(), segments[i], 0, // snap
+ i, this,
+ m_fontName, m_fontSize));
+ }
+
+
+ // HeadersGroup ctor must not be called before m_staffs initialization
+ m_headersGroupView = new QDeferScrollView(getCentralWidget());
+ QWidget * vport = m_headersGroupView->viewport();
+ m_headersGroup = new HeadersGroup(vport, this, &doc->getComposition());
+ m_headersGroupView->setVScrollBarMode(QScrollView::AlwaysOff);
+ m_headersGroupView->setHScrollBarMode(QScrollView::AlwaysOff);
+ m_headersGroupView->setFixedWidth(m_headersGroupView->contentsWidth());
+ m_canvasView->setLeftFixedWidget(m_headersGroupView);
+
+ // Add a close button just above the track headers.
+ // The grid layout is only here to maintain the button in a
+ // right place
+ m_headersTopFrame = new QFrame(getCentralWidget());
+ QGridLayout * headersTopGrid
+ = new QGridLayout(m_headersTopFrame, 2, 2);
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QCanvasPixmap pixmap(pixmapDir + "/misc/close.xpm");
+ QPushButton * hideHeadersButton
+ = new QPushButton(m_headersTopFrame);
+ headersTopGrid->addWidget(hideHeadersButton, 1, 1,
+ Qt::AlignRight | Qt::AlignBottom);
+ hideHeadersButton->setIconSet(QIconSet(pixmap));
+ hideHeadersButton->setFlat(true);
+ QToolTip::add(hideHeadersButton, i18n("Close track headers"));
+ headersTopGrid->setMargin(4);
+ setTopStandardRuler(new StandardRuler(getDocument(),
+ m_hlayout, m_leftGutter, 25,
+ false, getCentralWidget()), m_headersTopFrame);
+
+ m_topStandardRuler->getLoopRuler()->setBackgroundColor
+ (GUIPalette::getColour(GUIPalette::InsertCursorRuler));
+
+ connect(m_topStandardRuler->getLoopRuler(), SIGNAL(startMouseMove(int)),
+ m_canvasView, SLOT(startAutoScroll(int)));
+ connect(m_topStandardRuler->getLoopRuler(), SIGNAL(stopMouseMove()),
+ m_canvasView, SLOT(stopAutoScroll()));
+
+ connect(m_bottomStandardRuler->getLoopRuler(), SIGNAL(startMouseMove(int)),
+ m_canvasView, SLOT(startAutoScroll(int)));
+ connect(m_bottomStandardRuler->getLoopRuler(), SIGNAL(stopMouseMove()),
+ m_canvasView, SLOT(stopAutoScroll()));
+
+ // Following connection have to be done before calling setPageMode())
+ connect(m_headersGroup, SIGNAL(headersResized(int)),
+ this, SLOT(slotHeadersWidthChanged(int)));
+
+
+ //
+ // layout
+ //
+ ProgressDialog* progressDlg = 0;
+
+ if (showProgressive)
+ {
+ show();
+ ProgressDialog::processEvents();
+
+ NOTATION_DEBUG << "NotationView : setting up progress dialog" << endl;
+
+ progressDlg = new ProgressDialog(i18n("Starting..."),
+ 100, this);
+ progressDlg->setAutoClose(false);
+ progressDlg->setAutoReset(true);
+ progressDlg->setMinimumDuration(1000);
+ setupProgress(progressDlg);
+
+ m_progressDisplayer = PROGRESS_DIALOG;
+ }
+
+ m_chordNameRuler->setStudio(&getDocument()->getStudio());
+
+ m_currentStaff = 0;
+ m_staffs[0]->setCurrent(true);
+
+ m_config->setGroup(NotationViewConfigGroup);
+ int layoutMode = m_config->readNumEntry("layoutmode", 0);
+
+ try
+ {
+
+ LinedStaff::PageMode mode = LinedStaff::LinearMode;
+ if (layoutMode == 1)
+ mode = LinedStaff::ContinuousPageMode;
+ else if (layoutMode == 2)
+ mode = LinedStaff::MultiPageMode;
+
+ setPageMode(mode);
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->getSegment().getRefreshStatus
+ (m_segmentsRefreshStatusIds[i]).setNeedsRefresh(false);
+ }
+
+ m_ok = true;
+
+ } catch (ProgressReporter::Cancelled c)
+ {
+ // when cancelled, m_ok is false -- checked by calling method
+ NOTATION_DEBUG << "NotationView ctor : layout Cancelled" << endl;
+ }
+
+ NOTATION_DEBUG << "NotationView ctor : m_ok = " << m_ok << endl;
+
+ delete progressDlg;
+
+ // at this point we can return if operation was cancelled
+ if (!isOK())
+ {
+ setOutOfCtor();
+ return ;
+ }
+
+
+ // otherwise, carry on
+ setupDefaultProgress();
+
+ //
+ // Connect signals
+ //
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(renderRequired(double, double)),
+ this, SLOT(slotCheckRendered(double, double)));
+
+ m_topStandardRuler->connectRulerToDocPointer(doc);
+ m_bottomStandardRuler->connectRulerToDocPointer(doc);
+
+ // Disconnect the default connection for this signal from the
+ // top ruler, and connect our own instead
+
+ QObject::disconnect
+ (m_topStandardRuler->getLoopRuler(),
+ SIGNAL(setPointerPosition(timeT)), 0, 0);
+
+ QObject::connect
+ (m_topStandardRuler->getLoopRuler(),
+ SIGNAL(setPointerPosition(timeT)),
+ this, SLOT(slotSetInsertCursorPosition(timeT)));
+
+ QObject::connect
+ (m_topStandardRuler,
+ SIGNAL(dragPointerToPosition(timeT)),
+ this, SLOT(slotSetInsertCursorPosition(timeT)));
+
+ connect(m_bottomStandardRuler, SIGNAL(dragPointerToPosition(timeT)),
+ this, SLOT(slotSetPointerPosition(timeT)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(itemPressed(int, int, QMouseEvent*, NotationElement*)),
+ this, SLOT (slotItemPressed(int, int, QMouseEvent*, NotationElement*)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(activeItemPressed(QMouseEvent*, QCanvasItem*)),
+ this, SLOT (slotActiveItemPressed(QMouseEvent*, QCanvasItem*)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(nonNotationItemPressed(QMouseEvent*, QCanvasItem*)),
+ this, SLOT (slotNonNotationItemPressed(QMouseEvent*, QCanvasItem*)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(textItemPressed(QMouseEvent*, QCanvasItem*)),
+ this, SLOT (slotTextItemPressed(QMouseEvent*, QCanvasItem*)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(mouseMoved(QMouseEvent*)),
+ this, SLOT (slotMouseMoved(QMouseEvent*)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(mouseReleased(QMouseEvent*)),
+ this, SLOT (slotMouseReleased(QMouseEvent*)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(hoveredOverNoteChanged(const QString&)),
+ this, SLOT (slotHoveredOverNoteChanged(const QString&)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(hoveredOverAbsoluteTimeChanged(unsigned int)),
+ this, SLOT (slotHoveredOverAbsoluteTimeChanged(unsigned int)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(zoomIn()), this, SLOT(slotZoomIn()));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(zoomOut()), this, SLOT(slotZoomOut()));
+
+ QObject::connect
+ (m_pannerDialog->scrollbox(), SIGNAL(valueChanged(const QPoint &)),
+ getCanvasView(), SLOT(slotSetScrollPos(const QPoint &)));
+
+ QObject::connect
+ (getCanvasView()->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ m_pannerDialog->scrollbox(), SLOT(setViewX(int)));
+
+ QObject::connect
+ (getCanvasView()->verticalScrollBar(), SIGNAL(valueChanged(int)),
+ m_pannerDialog->scrollbox(), SLOT(setViewY(int)));
+
+ QObject::connect
+ (doc, SIGNAL(pointerPositionChanged(timeT)),
+ this, SLOT(slotSetPointerPosition(timeT)));
+
+ //
+ // Connect vertical scrollbars between canvas and notation header
+ QObject::connect
+ (getCanvasView()->verticalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(slotVerticalScrollHeadersGroup(int)));
+
+ QObject::connect
+ (getCanvasView()->verticalScrollBar(), SIGNAL(sliderMoved(int)),
+ this, SLOT(slotVerticalScrollHeadersGroup(int)));
+
+ QObject::connect
+ (m_headersGroupView, SIGNAL(gotWheelEvent(QWheelEvent*)),
+ getCanvasView(), SLOT(slotExternalWheelEvent(QWheelEvent*)));
+
+ // Ensure notation header keeps the right bottom margin when user
+ // toggles the canvas view bottom rulers
+ connect(getCanvasView(), SIGNAL(bottomWidgetHeightChanged(int)),
+ this, SLOT(slotCanvasBottomWidgetHeightChanged(int)));
+
+ // Signal canvas horizontal scroll to notation header
+ QObject::connect
+ (getCanvasView(), SIGNAL(contentsMoving(int, int)),
+ this, SLOT(slotUpdateHeaders(int, int)));
+
+ // Connect the close notation headers button
+ QObject::connect(hideHeadersButton, SIGNAL(clicked()),
+ this, SLOT(slotHideHeadersGroup()));
+
+ stateChanged("have_selection", KXMLGUIClient::StateReverse);
+ stateChanged("have_notes_in_selection", KXMLGUIClient::StateReverse);
+ stateChanged("have_rests_in_selection", KXMLGUIClient::StateReverse);
+ stateChanged("have_multiple_staffs",
+ (m_staffs.size() > 1 ? KXMLGUIClient::StateNoReverse :
+ KXMLGUIClient::StateReverse));
+ stateChanged("rest_insert_tool_current", KXMLGUIClient::StateReverse);
+ slotTestClipboard();
+
+ if (getSegmentsOnlyRestsAndClefs())
+ {
+ m_selectDefaultNote->activate();
+ stateChanged("note_insert_tool_current",
+ KXMLGUIClient::StateNoReverse);
+ } else
+ {
+ actionCollection()->action("select")->activate();
+ stateChanged("note_insert_tool_current",
+ KXMLGUIClient::StateReverse);
+ }
+
+ timeT start = doc->getComposition().getLoopStart();
+ timeT end = doc->getComposition().getLoopEnd();
+ m_topStandardRuler->getLoopRuler()->slotSetLoopMarker(start, end);
+ m_bottomStandardRuler->getLoopRuler()->slotSetLoopMarker(start, end);
+
+ slotSetInsertCursorPosition(0);
+ slotSetPointerPosition(doc->getComposition().getPosition());
+ setCurrentSelection(0, false, true);
+ slotUpdateInsertModeStatus();
+ m_chordNameRuler->repaint();
+ m_tempoRuler->repaint();
+ m_rawNoteRuler->repaint();
+ m_inhibitRefresh = false;
+
+ // slotCheckRendered(0, getCanvasView()->visibleWidth());
+ // getCanvasView()->repaintContents();
+ updateView();
+
+ QObject::connect
+ (this, SIGNAL(renderComplete()),
+ getCanvasView(), SLOT(slotRenderComplete()));
+
+ if (parent)
+ {
+ const TrackButtons * trackLabels =
+ ((RosegardenGUIView*)parent)->getTrackEditor()->getTrackButtons();
+ QObject::connect
+ (trackLabels, SIGNAL(nameChanged()),
+ this, SLOT(slotUpdateStaffName()));
+ }
+
+ setConfigDialogPageIndex(3);
+ setOutOfCtor();
+
+ // Property and Control Rulers
+ //
+ if (getCurrentSegment()->getViewFeatures())
+ slotShowVelocityControlRuler();
+ setupControllerTabs();
+
+ setupAddControlRulerMenu();
+ setRewFFwdToAutoRepeat();
+
+ slotCompositionStateUpdate();
+
+ NOTATION_DEBUG << "NotationView ctor exiting" << endl;
+}
+
+NotationView::NotationView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ QWidget *parent,
+ NotationView *referenceView)
+ : EditView(doc, segments, 1, 0, "printview"),
+ m_properties(getViewLocalPropertyPrefix()),
+ m_selectionCounter(0),
+ m_currentNotePixmap(0),
+ m_hoveredOverNoteName(0),
+ m_hoveredOverAbsoluteTime(0),
+ m_lastFinishingStaff( -1),
+ m_title(0),
+ m_subtitle(0),
+ m_composer(0),
+ m_copyright(0),
+ m_insertionTime(0),
+ m_deferredCursorMove(NoCursorMoveNeeded),
+ m_lastNoteAction("crotchet"),
+ m_fontName(NoteFontFactory::getDefaultFontName()),
+ m_fontSize(NoteFontFactory::getDefaultSize(m_fontName)),
+ m_pageMode(LinedStaff::LinearMode),
+ m_leftGutter(0),
+ m_notePixmapFactory(new NotePixmapFactory(m_fontName, m_fontSize)),
+ m_hlayout(new NotationHLayout(&doc->getComposition(), m_notePixmapFactory,
+ m_properties, this)),
+ m_vlayout(new NotationVLayout(&doc->getComposition(), m_notePixmapFactory,
+ m_properties, this)),
+ m_chordNameRuler(0),
+ m_tempoRuler(0),
+ m_rawNoteRuler(0),
+ m_annotationsVisible(false),
+ m_lilyPondDirectivesVisible(false),
+ m_selectDefaultNote(0),
+ m_fontCombo(0),
+ m_fontSizeCombo(0),
+ m_spacingCombo(0),
+ m_fontSizeActionMenu(0),
+ m_pannerDialog(0),
+ m_renderTimer(0),
+ m_playTracking(false),
+ m_progressDisplayer(PROGRESS_NONE),
+ m_inhibitRefresh(true),
+ m_ok(false),
+ m_printMode(true),
+ m_printSize(8), // set in positionStaffs
+ m_showHeadersGroup(0),
+ m_headersGroupView(0),
+ m_headersGroup(0),
+ m_headersTopFrame(0),
+ m_showHeadersMenuEntry(0)
+{
+ assert(segments.size() > 0);
+ NOTATION_DEBUG << "NotationView print ctor" << endl;
+
+
+ // Initialise the display-related defaults that will be needed
+ // by both the actions and the layout toolbar
+
+ m_config->setGroup(NotationViewConfigGroup);
+
+ if (referenceView)
+ {
+ m_fontName = referenceView->m_fontName;
+ } else
+ {
+ m_fontName = qstrtostr(m_config->readEntry
+ ("notefont",
+ strtoqstr(NoteFontFactory::getDefaultFontName())));
+ }
+
+
+ // Force largest font size
+ std::vector<int> sizes = NoteFontFactory::getAllSizes(m_fontName);
+ m_fontSize = sizes[sizes.size() - 1];
+
+ if (referenceView)
+ {
+ m_hlayout->setSpacing(referenceView->m_hlayout->getSpacing());
+ m_hlayout->setProportion(referenceView->m_hlayout->getProportion());
+ } else
+ {
+ int defaultSpacing = m_config->readNumEntry("spacing", 100);
+ m_hlayout->setSpacing(defaultSpacing);
+ int defaultProportion = m_config->readNumEntry("proportion", 60);
+ m_hlayout->setProportion(defaultProportion);
+ }
+
+ delete m_notePixmapFactory;
+ m_notePixmapFactory = new NotePixmapFactory(m_fontName, m_fontSize);
+ m_hlayout->setNotePixmapFactory(m_notePixmapFactory);
+ m_vlayout->setNotePixmapFactory(m_notePixmapFactory);
+
+ setBackgroundMode(PaletteBase);
+ m_config->setGroup(NotationViewConfigGroup);
+
+ QCanvas *tCanvas = new QCanvas(this);
+ tCanvas->resize(width() * 2, height() * 2); //!!!
+
+ setCanvasView(new NotationCanvasView(*this, tCanvas, getCentralWidget()));
+ canvas()->retune(128); // tune for larger canvas
+
+ for (unsigned int i = 0; i < segments.size(); ++i)
+ {
+ m_staffs.push_back(new NotationStaff(canvas(), segments[i], 0, // snap
+ i, this,
+ m_fontName, m_fontSize));
+ }
+
+ m_currentStaff = 0;
+ m_staffs[0]->setCurrent(true);
+
+ ProgressDialog* progressDlg = 0;
+
+ if (parent)
+ {
+
+ ProgressDialog::processEvents();
+
+ NOTATION_DEBUG << "NotationView : setting up progress dialog" << endl;
+
+ progressDlg = new ProgressDialog(i18n("Preparing to print..."),
+ 100, parent);
+ progressDlg->setAutoClose(false);
+ progressDlg->setAutoReset(true);
+ progressDlg->setMinimumDuration(1000);
+ setupProgress(progressDlg);
+
+ m_progressDisplayer = PROGRESS_DIALOG;
+ }
+
+ try
+ {
+
+ setPageMode(LinedStaff::MultiPageMode); // also positions and renders the staffs!
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->getSegment().getRefreshStatus
+ (m_segmentsRefreshStatusIds[i]).setNeedsRefresh(false);
+ }
+
+ m_ok = true;
+
+ } catch (ProgressReporter::Cancelled c)
+ {
+ // when cancelled, m_ok is false -- checked by calling method
+ NOTATION_DEBUG << "NotationView ctor : layout Cancelled" << endl;
+ }
+
+ NOTATION_DEBUG << "NotationView ctor : m_ok = " << m_ok << endl;
+
+ delete progressDlg;
+
+ if (!isOK())
+ {
+ setOutOfCtor();
+ return ; // In case more code is added there later
+ }
+
+ setOutOfCtor(); // keep this as last call in the ctor
+}
+
+NotationView::~NotationView()
+{
+ NOTATION_DEBUG << "-> ~NotationView()" << endl;
+
+ if (!m_printMode && m_ok)
+ slotSaveOptions();
+
+ delete m_chordNameRuler;
+
+ delete m_renderTimer;
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ for (Segment::iterator j = m_staffs[i]->getSegment().begin();
+ j != m_staffs[i]->getSegment().end(); ++j) {
+ removeViewLocalProperties(*j);
+ }
+ delete m_staffs[i]; // this will erase all "notes" canvas items
+ }
+
+ PixmapArrayGC::deleteAll();
+ Profiles::getInstance()->dump();
+
+ NOTATION_DEBUG << "<- ~NotationView()" << endl;
+}
+
+void
+NotationView::removeViewLocalProperties(Event *e)
+{
+ Event::PropertyNames names(e->getPropertyNames());
+ std::string prefix(getViewLocalPropertyPrefix());
+
+ for (Event::PropertyNames::iterator i = names.begin();
+ i != names.end(); ++i) {
+ if (i->getName().substr(0, prefix.size()) == prefix) {
+ e->unset(*i);
+ }
+ }
+}
+
+const NotationProperties &
+NotationView::getProperties() const
+{
+ return m_properties;
+}
+
+void NotationView::positionStaffs()
+{
+ NOTATION_DEBUG << "NotationView::positionStaffs" << endl;
+
+ m_config->setGroup(NotationViewConfigGroup);
+ m_printSize = m_config->readUnsignedNumEntry("printingnotesize", 5);
+
+ int minTrack = 0, maxTrack = 0;
+ bool haveMinTrack = false;
+ typedef std::map<int, int> TrackIntMap;
+ TrackIntMap trackHeights;
+ TrackIntMap trackCoords;
+
+ int pageWidth, pageHeight, leftMargin, topMargin;
+ pageWidth = getPageWidth();
+ pageHeight = getPageHeight();
+ leftMargin = 0, topMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+
+ int accumulatedHeight;
+ int rowsPerPage = 1;
+ int legerLines = 8;
+ if (m_pageMode != LinedStaff::LinearMode)
+ legerLines = 7;
+ int rowGapPercent = (m_staffs.size() > 1 ? 40 : 10);
+ int aimFor = -1;
+
+ bool done = false;
+
+ int titleHeight = 0;
+
+ if (m_title)
+ delete m_title;
+ if (m_subtitle)
+ delete m_subtitle;
+ if (m_composer)
+ delete m_composer;
+ if (m_copyright)
+ delete m_copyright;
+ m_title = m_subtitle = m_composer = m_copyright = 0;
+
+ if (m_pageMode == LinedStaff::MultiPageMode) {
+
+ const Configuration &metadata =
+ getDocument()->getComposition().getMetadata();
+
+ QFont defaultFont(NotePixmapFactory::defaultSerifFontFamily);
+ m_config->setGroup(NotationViewConfigGroup);
+ QFont font = m_config->readFontEntry("textfont", &defaultFont);
+ font.setPixelSize(m_fontSize * 5);
+ QFontMetrics metrics(font);
+
+ if (metadata.has(CompositionMetadataKeys::Title)) {
+ QString title(strtoqstr(metadata.get<String>
+ (CompositionMetadataKeys::Title)));
+ m_title = new QCanvasText(title, font, canvas());
+ m_title->setX(m_leftGutter + pageWidth / 2 - metrics.width(title) / 2);
+ m_title->setY(20 + topMargin / 4 + metrics.ascent());
+ m_title->show();
+ titleHeight += metrics.height() * 3 / 2 + topMargin / 4;
+ }
+
+ font.setPixelSize(m_fontSize * 3);
+ metrics = QFontMetrics(font);
+
+ if (metadata.has(CompositionMetadataKeys::Subtitle)) {
+ QString subtitle(strtoqstr(metadata.get<String>
+ (CompositionMetadataKeys::Subtitle)));
+ m_subtitle = new QCanvasText(subtitle, font, canvas());
+ m_subtitle->setX(m_leftGutter + pageWidth / 2 - metrics.width(subtitle) / 2);
+ m_subtitle->setY(20 + titleHeight + metrics.ascent());
+ m_subtitle->show();
+ titleHeight += metrics.height() * 3 / 2;
+ }
+
+ if (metadata.has(CompositionMetadataKeys::Composer)) {
+ QString composer(strtoqstr(metadata.get<String>
+ (CompositionMetadataKeys::Composer)));
+ m_composer = new QCanvasText(composer, font, canvas());
+ m_composer->setX(m_leftGutter + pageWidth - metrics.width(composer) - leftMargin);
+ m_composer->setY(20 + titleHeight + metrics.ascent());
+ m_composer->show();
+ titleHeight += metrics.height() * 3 / 2;
+ }
+
+ font.setPixelSize(m_fontSize * 2);
+ metrics = QFontMetrics(font);
+
+ if (metadata.has(CompositionMetadataKeys::Copyright)) {
+ QString copyright(strtoqstr(metadata.get<String>
+ (CompositionMetadataKeys::Copyright)));
+ m_copyright = new QCanvasText(copyright, font, canvas());
+ m_copyright->setX(m_leftGutter + leftMargin);
+ m_copyright->setY(20 + pageHeight - topMargin - metrics.descent());
+ m_copyright->show();
+ }
+ }
+
+ while (1) {
+
+ accumulatedHeight = 0;
+ int maxTrackHeight = 0;
+
+ trackHeights.clear();
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+
+ m_staffs[i]->setLegerLineCount(legerLines);
+
+ int height = m_staffs[i]->getHeightOfRow();
+ TrackId trackId = m_staffs[i]->getSegment().getTrack();
+ Track *track =
+ m_staffs[i]->getSegment().getComposition()->
+ getTrackById(trackId);
+
+ if (!track)
+ continue; // This Should Not Happen, My Friend
+
+ int trackPosition = track->getPosition();
+
+ TrackIntMap::iterator hi = trackHeights.find(trackPosition);
+ if (hi == trackHeights.end()) {
+ trackHeights.insert(TrackIntMap::value_type
+ (trackPosition, height));
+ } else if (height > hi->second) {
+ hi->second = height;
+ }
+
+ if (height > maxTrackHeight)
+ maxTrackHeight = height;
+
+ if (trackPosition < minTrack || !haveMinTrack) {
+ minTrack = trackPosition;
+ haveMinTrack = true;
+ }
+ if (trackPosition > maxTrack) {
+ maxTrack = trackPosition;
+ }
+ }
+
+ for (int i = minTrack; i <= maxTrack; ++i) {
+ TrackIntMap::iterator hi = trackHeights.find(i);
+ if (hi != trackHeights.end()) {
+ trackCoords[i] = accumulatedHeight;
+ accumulatedHeight += hi->second;
+ }
+ }
+
+ accumulatedHeight += maxTrackHeight * rowGapPercent / 100;
+
+ if (done)
+ break;
+
+ if (m_pageMode != LinedStaff::MultiPageMode) {
+
+ rowsPerPage = 0;
+ done = true;
+ break;
+
+ } else {
+
+ // Check how well all this stuff actually fits on the
+ // page. If things don't fit as well as we'd like, modify
+ // at most one parameter so as to save some space, then
+ // loop around again and see if it worked. This iterative
+ // approach is inefficient but the time spent here is
+ // neglible in context, and it's a simple way to code it.
+
+ int staffPageHeight = pageHeight - topMargin * 2 - titleHeight;
+ rowsPerPage = staffPageHeight / accumulatedHeight;
+
+ if (rowsPerPage < 1) {
+
+ if (legerLines > 5)
+ --legerLines;
+ else if (rowGapPercent > 20)
+ rowGapPercent -= 10;
+ else if (legerLines > 4)
+ --legerLines;
+ else if (rowGapPercent > 0)
+ rowGapPercent -= 10;
+ else if (legerLines > 3)
+ --legerLines;
+ else if (m_printSize > 3)
+ --m_printSize;
+ else { // just accept that we'll have to overflow
+ rowsPerPage = 1;
+ done = true;
+ }
+
+ } else {
+
+ if (aimFor == rowsPerPage) {
+
+ titleHeight +=
+ (staffPageHeight - (rowsPerPage * accumulatedHeight)) / 2;
+
+ done = true;
+
+ } else {
+
+ if (aimFor == -1)
+ aimFor = rowsPerPage + 1;
+
+ // we can perhaps accommodate another row, with care
+ if (legerLines > 5)
+ --legerLines;
+ else if (rowGapPercent > 20)
+ rowGapPercent -= 10;
+ else if (legerLines > 3)
+ --legerLines;
+ else if (rowGapPercent > 0)
+ rowGapPercent -= 10;
+ else { // no, we can't
+ rowGapPercent = 0;
+ legerLines = 8;
+ done = true;
+ }
+ }
+ }
+ }
+ }
+
+ m_hlayout->setPageWidth(pageWidth - leftMargin * 2);
+
+ int topGutter = 0;
+
+ if (m_pageMode == LinedStaff::MultiPageMode) {
+
+ topGutter = 20;
+
+ } else if (m_pageMode == LinedStaff::ContinuousPageMode) {
+
+ // fewer leger lines above staff than in linear mode --
+ // compensate for this on the top staff
+ topGutter = m_notePixmapFactory->getLineSpacing() * 2;
+ }
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+
+ TrackId trackId = m_staffs[i]->getSegment().getTrack();
+ Track *track =
+ m_staffs[i]->getSegment().getComposition()->
+ getTrackById(trackId);
+
+ if (!track)
+ continue; // Once Again, My Friend, You Should Never See Me Here
+
+ int trackPosition = track->getPosition();
+
+ m_staffs[i]->setTitleHeight(titleHeight);
+ m_staffs[i]->setRowSpacing(accumulatedHeight);
+
+ if (trackPosition < maxTrack) {
+ m_staffs[i]->setConnectingLineLength(trackHeights[trackPosition]);
+ }
+
+ if (trackPosition == minTrack &&
+ m_pageMode != LinedStaff::LinearMode) {
+ m_staffs[i]->setBarNumbersEvery(5);
+ } else {
+ m_staffs[i]->setBarNumbersEvery(0);
+ }
+
+ m_staffs[i]->setX(m_leftGutter);
+ m_staffs[i]->setY(topGutter + trackCoords[trackPosition] + topMargin);
+ m_staffs[i]->setPageWidth(pageWidth - leftMargin * 2);
+ m_staffs[i]->setRowsPerPage(rowsPerPage);
+ m_staffs[i]->setPageMode(m_pageMode);
+ m_staffs[i]->setMargin(leftMargin);
+
+ NOTATION_DEBUG << "NotationView::positionStaffs: set staff's page width to "
+ << (pageWidth - leftMargin * 2) << endl;
+
+ }
+
+
+ if (!m_printMode) {
+ // Destroy then recreate all track headers
+ hideHeadersGroup();
+ m_headersGroup->removeAllHeaders();
+ if (m_pageMode == LinedStaff::LinearMode) {
+ for (int i = minTrack; i <= maxTrack; ++i) {
+ TrackIntMap::iterator hi = trackHeights.find(i);
+ if (hi != trackHeights.end()) {
+ TrackId trackId = getDocument()->getComposition()
+ .getTrackByPosition(i)->getId();
+ m_headersGroup->addHeader(trackId, trackHeights[i],
+ trackCoords[i], getCanvasLeftX());
+ }
+ }
+
+ m_headersGroup->completeToHeight(canvas()->height());
+
+ m_headersGroupView->addChild(m_headersGroup);
+
+ getCanvasView()->updateLeftWidgetGeometry();
+
+ if ( (m_showHeadersGroup == HeadersGroup::ShowAlways)
+ || ( (m_showHeadersGroup == HeadersGroup::ShowWhenNeeded)
+ && (m_headersGroup->getUsedHeight()
+ > getCanvasView()->visibleHeight()))) {
+ m_headersGroup->slotUpdateAllHeaders(getCanvasLeftX(), 0, true);
+ showHeadersGroup();
+
+ // Disable menu entry when headers are shown
+ m_showHeadersMenuEntry->setEnabled(false);
+ } else {
+ // Enable menu entry when headers are hidden
+ m_showHeadersMenuEntry->setEnabled(true);
+ }
+ } else {
+ // Disable menu entry when not in linear mode
+ m_showHeadersMenuEntry->setEnabled(false);
+ }
+ }
+}
+
+void NotationView::slotCanvasBottomWidgetHeightChanged(int newHeight)
+{
+ getCanvasView()->updateLeftWidgetGeometry();
+}
+
+void NotationView::positionPages()
+{
+ if (m_printMode)
+ return ;
+
+ QPixmap background;
+ QPixmap deskBackground;
+ bool haveBackground = false;
+
+ m_config->setGroup(NotationViewConfigGroup);
+ if (m_config->readBoolEntry("backgroundtextures", true)) {
+ QString pixmapDir =
+ KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ if (background.load(QString("%1/misc/bg-paper-cream.xpm").
+ arg(pixmapDir))) {
+ haveBackground = true;
+ }
+ // we're happy to ignore errors from this one:
+ deskBackground.load(QString("%1/misc/bg-desktop.xpm").arg(pixmapDir));
+ }
+
+ int pageWidth = getPageWidth();
+ int pageHeight = getPageHeight();
+ int leftMargin = 0, topMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+ int maxPageCount = 1;
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ int pageCount = m_staffs[i]->getPageCount();
+ if (pageCount > maxPageCount)
+ maxPageCount = pageCount;
+ }
+
+ for (unsigned int i = 0; i < m_pages.size(); ++i) {
+ delete m_pages[i];
+ delete m_pageNumbers[i];
+ }
+ m_pages.clear();
+ m_pageNumbers.clear();
+
+ if (m_pageMode != LinedStaff::MultiPageMode) {
+ if (haveBackground) {
+ canvas()->setBackgroundPixmap(background);
+ getCanvasView()->setBackgroundMode(Qt::FixedPixmap);
+ getCanvasView()->setPaletteBackgroundPixmap(background);
+ getCanvasView()->setErasePixmap(background);
+ }
+ } else {
+ if (haveBackground) {
+ canvas()->setBackgroundPixmap(deskBackground);
+ getCanvasView()->setBackgroundMode(Qt::FixedPixmap);
+ getCanvasView()->setPaletteBackgroundPixmap(background);
+ getCanvasView()->setErasePixmap(background);
+ }
+
+ QFont pageNumberFont;
+ pageNumberFont.setPixelSize(m_fontSize * 2);
+ QFontMetrics pageNumberMetrics(pageNumberFont);
+
+ for (int page = 0; page < maxPageCount; ++page) {
+
+ int x = m_leftGutter + pageWidth * page + leftMargin / 4;
+ int y = 20;
+ int w = pageWidth - leftMargin / 2;
+ int h = pageHeight;
+
+ QString str = QString("%1").arg(page + 1);
+ QCanvasText *text = new QCanvasText(str, pageNumberFont, canvas());
+ text->setX(m_leftGutter + pageWidth * page + pageWidth - pageNumberMetrics.width(str) - leftMargin);
+ text->setY(y + h - pageNumberMetrics.descent() - topMargin);
+ text->setZ( -999);
+ text->show();
+ m_pageNumbers.push_back(text);
+
+ QCanvasRectangle *rect = new QCanvasRectangle(x, y, w, h, canvas());
+ if (haveBackground)
+ rect->setBrush(QBrush(Qt::white, background));
+ rect->setPen(Qt::black);
+ rect->setZ( -1000);
+ rect->show();
+ m_pages.push_back(rect);
+ }
+
+ updateThumbnails(false);
+ }
+
+ m_config->setGroup(NotationViewConfigGroup);
+}
+
+void NotationView::slotUpdateStaffName()
+{
+ LinedStaff *staff = getLinedStaff(m_currentStaff);
+ staff->drawStaffName();
+ m_headersGroup->slotUpdateAllHeaders(getCanvasLeftX(), 0, true);
+}
+
+void NotationView::slotSaveOptions()
+{
+ m_config->setGroup(NotationViewConfigGroup);
+
+ m_config->writeEntry("Show Chord Name Ruler", getToggleAction("show_chords_ruler")->isChecked());
+ m_config->writeEntry("Show Raw Note Ruler", getToggleAction("show_raw_note_ruler")->isChecked());
+ m_config->writeEntry("Show Tempo Ruler", getToggleAction("show_tempo_ruler")->isChecked());
+ m_config->writeEntry("Show Annotations", m_annotationsVisible);
+ m_config->writeEntry("Show LilyPond Directives", m_lilyPondDirectivesVisible);
+
+ m_config->sync();
+}
+
+void NotationView::setOneToolbar(const char *actionName,
+ const char *toolbarName)
+{
+ KToggleAction *action = getToggleAction(actionName);
+ if (!action) {
+ std::cerr << "WARNING: No such action as " << actionName << std::endl;
+ return ;
+ }
+ QWidget *toolbar = toolBar(toolbarName);
+ if (!toolbar) {
+ std::cerr << "WARNING: No such toolbar as " << toolbarName << std::endl;
+ return ;
+ }
+ action->setChecked(!toolbar->isHidden());
+}
+
+void NotationView::readOptions()
+{
+ EditView::readOptions();
+
+ setOneToolbar("show_tools_toolbar", "Tools Toolbar");
+ setOneToolbar("show_notes_toolbar", "Notes Toolbar");
+ setOneToolbar("show_rests_toolbar", "Rests Toolbar");
+ setOneToolbar("show_clefs_toolbar", "Clefs Toolbar");
+ setOneToolbar("show_group_toolbar", "Group Toolbar");
+ setOneToolbar("show_marks_toolbar", "Marks Toolbar");
+ setOneToolbar("show_layout_toolbar", "Layout Toolbar");
+ setOneToolbar("show_transport_toolbar", "Transport Toolbar");
+ setOneToolbar("show_accidentals_toolbar", "Accidentals Toolbar");
+ setOneToolbar("show_meta_toolbar", "Meta Toolbar");
+
+ m_config->setGroup(NotationViewConfigGroup);
+
+ bool opt;
+
+ opt = m_config->readBoolEntry("Show Chord Name Ruler", false);
+ getToggleAction("show_chords_ruler")->setChecked(opt);
+ slotToggleChordsRuler();
+
+ opt = m_config->readBoolEntry("Show Raw Note Ruler", true);
+ getToggleAction("show_raw_note_ruler")->setChecked(opt);
+ slotToggleRawNoteRuler();
+
+ opt = m_config->readBoolEntry("Show Tempo Ruler", true);
+ getToggleAction("show_tempo_ruler")->setChecked(opt);
+ slotToggleTempoRuler();
+
+ opt = m_config->readBoolEntry("Show Annotations", true);
+ m_annotationsVisible = opt;
+ getToggleAction("show_annotations")->setChecked(opt);
+ slotUpdateAnnotationsStatus();
+ // slotToggleAnnotations();
+
+ opt = m_config->readBoolEntry("Show LilyPond Directives", true);
+ m_lilyPondDirectivesVisible = opt;
+ getToggleAction("show_lilypond_directives")->setChecked(opt);
+ slotUpdateLilyPondDirectivesStatus();
+}
+
+void NotationView::setupActions()
+{
+ KStdAction::print(this, SLOT(slotFilePrint()), actionCollection());
+ KStdAction::printPreview(this, SLOT(slotFilePrintPreview()),
+ actionCollection());
+
+ new KAction(i18n("Print &with LilyPond..."), 0, 0, this,
+ SLOT(slotPrintLilyPond()), actionCollection(),
+ "file_print_lilypond");
+
+ new KAction(i18n("Preview with Lil&yPond..."), 0, 0, this,
+ SLOT(slotPreviewLilyPond()), actionCollection(),
+ "file_preview_lilypond");
+
+ EditViewBase::setupActions("notation.rc");
+ EditView::setupActions();
+
+ KRadioAction* noteAction = 0;
+
+ // View menu stuff
+
+ KActionMenu *fontActionMenu =
+ new KActionMenu(i18n("Note &Font"), this, "note_font_actionmenu");
+
+ std::set
+ <std::string> fs(NoteFontFactory::getFontNames());
+ std::vector<std::string> f(fs.begin(), fs.end());
+ std::sort(f.begin(), f.end());
+
+ for (std::vector<std::string>::iterator i = f.begin(); i != f.end(); ++i) {
+
+ QString fontQName(strtoqstr(*i));
+
+ KToggleAction *fontAction =
+ new KToggleAction
+ (fontQName, 0, this, SLOT(slotChangeFontFromAction()),
+ actionCollection(), "note_font_" + fontQName);
+
+ fontAction->setChecked(*i == m_fontName);
+ fontActionMenu->insert(fontAction);
+ }
+
+ actionCollection()->insert(fontActionMenu);
+
+ m_fontSizeActionMenu =
+ new KActionMenu(i18n("Si&ze"), this, "note_font_size_actionmenu");
+ setupFontSizeMenu();
+
+ actionCollection()->insert(m_fontSizeActionMenu);
+
+ m_showHeadersMenuEntry
+ = new KAction(i18n("Show Track Headers"), 0, this,
+ SLOT(slotShowHeadersGroup()),
+ actionCollection(), "show_track_headers");
+
+ KActionMenu *spacingActionMenu =
+ new KActionMenu(i18n("S&pacing"), this, "stretch_actionmenu");
+
+ int defaultSpacing = m_hlayout->getSpacing();
+ std::vector<int> spacings = NotationHLayout::getAvailableSpacings();
+
+ for (std::vector<int>::iterator i = spacings.begin();
+ i != spacings.end(); ++i) {
+
+ KToggleAction *spacingAction =
+ new KToggleAction
+ (QString("%1%").arg(*i), 0, this,
+ SLOT(slotChangeSpacingFromAction()),
+ actionCollection(), QString("spacing_%1").arg(*i));
+
+ spacingAction->setExclusiveGroup("spacing");
+ spacingAction->setChecked(*i == defaultSpacing);
+ spacingActionMenu->insert(spacingAction);
+ }
+
+ actionCollection()->insert(spacingActionMenu);
+
+ KActionMenu *proportionActionMenu =
+ new KActionMenu(i18n("Du&ration Factor"), this, "proportion_actionmenu");
+
+ int defaultProportion = m_hlayout->getProportion();
+ std::vector<int> proportions = NotationHLayout::getAvailableProportions();
+
+ for (std::vector<int>::iterator i = proportions.begin();
+ i != proportions.end(); ++i) {
+
+ QString name = QString("%1%").arg(*i);
+ if (*i == 0)
+ name = i18n("None");
+
+ KToggleAction *proportionAction =
+ new KToggleAction
+ (name, 0, this,
+ SLOT(slotChangeProportionFromAction()),
+ actionCollection(), QString("proportion_%1").arg(*i));
+
+ proportionAction->setExclusiveGroup("proportion");
+ proportionAction->setChecked(*i == defaultProportion);
+ proportionActionMenu->insert(proportionAction);
+ }
+
+ actionCollection()->insert(proportionActionMenu);
+
+ KActionMenu *styleActionMenu =
+ new KActionMenu(i18n("Note &Style"), this, "note_style_actionmenu");
+
+ std::vector<NoteStyleName> styles
+ (NoteStyleFactory::getAvailableStyleNames());
+
+ for (std::vector<NoteStyleName>::iterator i = styles.begin();
+ i != styles.end(); ++i) {
+
+ QString styleQName(strtoqstr(*i));
+
+ KAction *styleAction =
+ new KAction
+ (styleQName, 0, this, SLOT(slotSetStyleFromAction()),
+ actionCollection(), "style_" + styleQName);
+
+ styleActionMenu->insert(styleAction);
+ }
+
+ actionCollection()->insert(styleActionMenu);
+
+ KActionMenu *ornamentActionMenu =
+ new KActionMenu(i18n("Use Ornament"), this, "ornament_actionmenu");
+
+
+
+ new KAction
+ (i18n("Insert Rest"), Key_P, this, SLOT(slotInsertRest()),
+ actionCollection(), QString("insert_rest"));
+
+ new KAction
+ (i18n("Switch from Note to Rest"), Key_T, this,
+ SLOT(slotSwitchFromNoteToRest()),
+ actionCollection(), QString("switch_from_note_to_rest"));
+
+ new KAction
+ (i18n("Switch from Rest to Note"), Key_Y, this,
+ SLOT(slotSwitchFromRestToNote()),
+ actionCollection(), QString("switch_from_rest_to_note"));
+
+
+ // setup Notes menu & toolbar
+ QIconSet icon;
+
+ for (NoteActionDataMap::Iterator actionDataIter = m_noteActionDataMap->begin();
+ actionDataIter != m_noteActionDataMap->end();
+ ++actionDataIter) {
+
+ NoteActionData noteActionData = **actionDataIter;
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ (noteActionData.pixmapName)));
+ noteAction = new KRadioAction(noteActionData.title,
+ icon,
+ noteActionData.keycode,
+ this,
+ SLOT(slotNoteAction()),
+ actionCollection(),
+ noteActionData.actionName);
+ noteAction->setExclusiveGroup("notes");
+
+ if (noteActionData.noteType == Note::Crotchet &&
+ noteActionData.dots == 0 && !noteActionData.rest) {
+ m_selectDefaultNote = noteAction;
+ }
+ }
+
+ // Note duration change actions
+ for (NoteChangeActionDataMap::Iterator actionDataIter = m_noteChangeActionDataMap->begin();
+ actionDataIter != m_noteChangeActionDataMap->end();
+ ++actionDataIter) {
+
+ NoteChangeActionData data = **actionDataIter;
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ (data.pixmapName)));
+
+ KAction *action = new KAction(data.title,
+ icon,
+ data.keycode,
+ this,
+ SLOT(slotNoteChangeAction()),
+ actionCollection(),
+ data.actionName);
+ }
+
+ //
+ // Accidentals
+ //
+ static QString actionsAccidental[][4] =
+ {
+ { i18n("No accidental"), "1slotNoAccidental()", "no_accidental", "accidental-none" },
+ { i18n("Follow previous accidental"), "1slotFollowAccidental()", "follow_accidental", "accidental-follow" },
+ { i18n("Sharp"), "1slotSharp()", "sharp_accidental", "accidental-sharp" },
+ { i18n("Flat"), "1slotFlat()", "flat_accidental", "accidental-flat" },
+ { i18n("Natural"), "1slotNatural()", "natural_accidental", "accidental-natural" },
+ { i18n("Double sharp"), "1slotDoubleSharp()", "double_sharp_accidental", "accidental-doublesharp" },
+ { i18n("Double flat"), "1slotDoubleFlat()", "double_flat_accidental", "accidental-doubleflat" }
+ };
+
+ for (unsigned int i = 0;
+ i < sizeof(actionsAccidental) / sizeof(actionsAccidental[0]); ++i) {
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ (actionsAccidental[i][3])));
+ noteAction = new KRadioAction(actionsAccidental[i][0], icon, 0, this,
+ actionsAccidental[i][1],
+ actionCollection(), actionsAccidental[i][2]);
+ noteAction->setExclusiveGroup("accidentals");
+ }
+
+
+ //
+ // Clefs
+ //
+
+ // Treble
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-treble")));
+ noteAction = new KRadioAction(i18n("&Treble Clef"), icon, 0, this,
+ SLOT(slotTrebleClef()),
+ actionCollection(), "treble_clef");
+ noteAction->setExclusiveGroup("notes");
+
+ // Alto
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-alto")));
+ noteAction = new KRadioAction(i18n("&Alto Clef"), icon, 0, this,
+ SLOT(slotAltoClef()),
+ actionCollection(), "alto_clef");
+ noteAction->setExclusiveGroup("notes");
+
+ // Tenor
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-tenor")));
+ noteAction = new KRadioAction(i18n("Te&nor Clef"), icon, 0, this,
+ SLOT(slotTenorClef()),
+ actionCollection(), "tenor_clef");
+ noteAction->setExclusiveGroup("notes");
+
+ // Bass
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-bass")));
+ noteAction = new KRadioAction(i18n("&Bass Clef"), icon, 0, this,
+ SLOT(slotBassClef()),
+ actionCollection(), "bass_clef");
+ noteAction->setExclusiveGroup("notes");
+
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("text")));
+ noteAction = new KRadioAction(i18n("&Text"), icon, Key_F8, this,
+ SLOT(slotText()),
+ actionCollection(), "text");
+ noteAction->setExclusiveGroup("notes");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("guitarchord")));
+ noteAction = new KRadioAction(i18n("&Guitar Chord"), icon, Key_F9, this,
+ SLOT(slotGuitarChord()),
+ actionCollection(), "guitarchord");
+ noteAction->setExclusiveGroup("notes");
+
+ /* icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("lilypond")));
+ noteAction = new KRadioAction(i18n("Lil&ypond Directive"), icon, Key_F9, this,
+ SLOT(slotLilyPondDirective()),
+ actionCollection(), "lilypond_directive");
+ noteAction->setExclusiveGroup("notes"); */
+
+
+ //
+ // Edition tools (eraser, selector...)
+ //
+ noteAction = new KRadioAction(i18n("&Erase"), "eraser", Key_F4,
+ this, SLOT(slotEraseSelected()),
+ actionCollection(), "erase");
+ noteAction->setExclusiveGroup("notes");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("select")));
+ noteAction = new KRadioAction(i18n("&Select and Edit"), icon, Key_F2,
+ this, SLOT(slotSelectSelected()),
+ actionCollection(), "select");
+ noteAction->setExclusiveGroup("notes");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("step_by_step")));
+ new KToggleAction(i18n("Ste&p Recording"), icon, 0, this,
+ SLOT(slotToggleStepByStep()), actionCollection(),
+ "toggle_step_by_step");
+
+
+ // Edit menu
+ new KAction(i18n("Select from Sta&rt"), 0, this,
+ SLOT(slotEditSelectFromStart()), actionCollection(),
+ "select_from_start");
+
+ new KAction(i18n("Select to &End"), 0, this,
+ SLOT(slotEditSelectToEnd()), actionCollection(),
+ "select_to_end");
+
+ new KAction(i18n("Select Whole St&aff"), Key_A + CTRL, this,
+ SLOT(slotEditSelectWholeStaff()), actionCollection(),
+ "select_whole_staff");
+
+ new KAction(i18n("C&ut and Close"), CTRL + SHIFT + Key_X, this,
+ SLOT(slotEditCutAndClose()), actionCollection(),
+ "cut_and_close");
+
+ new KAction(i18n("Pa&ste..."), CTRL + SHIFT + Key_V, this,
+ SLOT(slotEditGeneralPaste()), actionCollection(),
+ "general_paste");
+
+ new KAction(i18n("De&lete"), Key_Delete, this,
+ SLOT(slotEditDelete()), actionCollection(),
+ "delete");
+
+ new KAction(i18n("Move to Staff Above"), 0, this,
+ SLOT(slotMoveEventsUpStaff()), actionCollection(),
+ "move_events_up_staff");
+
+ new KAction(i18n("Move to Staff Below"), 0, this,
+ SLOT(slotMoveEventsDownStaff()), actionCollection(),
+ "move_events_down_staff");
+
+ //
+ // Settings menu
+ //
+ int layoutMode = m_config->readNumEntry("layoutmode", 0);
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/linear-layout.xpm");
+ icon = QIconSet(pixmap);
+ KRadioAction *linearModeAction = new KRadioAction
+ (i18n("&Linear Layout"), icon, 0, this, SLOT(slotLinearMode()),
+ actionCollection(), "linear_mode");
+ linearModeAction->setExclusiveGroup("layoutMode");
+ if (layoutMode == 0)
+ linearModeAction->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/continuous-page-mode.xpm");
+ icon = QIconSet(pixmap);
+ KRadioAction *continuousPageModeAction = new KRadioAction
+ (i18n("&Continuous Page Layout"), icon, 0, this, SLOT(slotContinuousPageMode()),
+ actionCollection(), "continuous_page_mode");
+ continuousPageModeAction->setExclusiveGroup("layoutMode");
+ if (layoutMode == 1)
+ continuousPageModeAction->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/multi-page-mode.xpm");
+ icon = QIconSet(pixmap);
+ KRadioAction *multiPageModeAction = new KRadioAction
+ (i18n("&Multiple Page Layout"), icon, 0, this, SLOT(slotMultiPageMode()),
+ actionCollection(), "multi_page_mode");
+ multiPageModeAction->setExclusiveGroup("layoutMode");
+ if (layoutMode == 2)
+ multiPageModeAction->setChecked(true);
+
+ new KToggleAction(i18n("Show Ch&ord Name Ruler"), 0, this,
+ SLOT(slotToggleChordsRuler()),
+ actionCollection(), "show_chords_ruler");
+
+ new KToggleAction(i18n("Show Ra&w Note Ruler"), 0, this,
+ SLOT(slotToggleRawNoteRuler()),
+ actionCollection(), "show_raw_note_ruler");
+
+ new KToggleAction(i18n("Show &Tempo Ruler"), 0, this,
+ SLOT(slotToggleTempoRuler()),
+ actionCollection(), "show_tempo_ruler");
+
+ new KToggleAction(i18n("Show &Annotations"), 0, this,
+ SLOT(slotToggleAnnotations()),
+ actionCollection(), "show_annotations");
+
+ new KToggleAction(i18n("Show Lily&Pond Directives"), 0, this,
+ SLOT(slotToggleLilyPondDirectives()),
+ actionCollection(), "show_lilypond_directives");
+
+ new KAction(i18n("Open L&yric Editor"), 0, this, SLOT(slotEditLyrics()),
+ actionCollection(), "lyric_editor");
+
+ //
+ // Group menu
+ //
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-beam")));
+
+ new KAction(BeamCommand::getGlobalName(), icon, Key_B + CTRL, this,
+ SLOT(slotGroupBeam()), actionCollection(), "beam");
+
+ new KAction(AutoBeamCommand::getGlobalName(), 0, this,
+ SLOT(slotGroupAutoBeam()), actionCollection(), "auto_beam");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-unbeam")));
+
+ new KAction(BreakCommand::getGlobalName(), icon, Key_U + CTRL, this,
+ SLOT(slotGroupBreak()), actionCollection(), "break_group");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-simple-tuplet")));
+
+ new KAction(TupletCommand::getGlobalName(true), icon, Key_R + CTRL, this,
+ SLOT(slotGroupSimpleTuplet()), actionCollection(), "simple_tuplet");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-tuplet")));
+
+ new KAction(TupletCommand::getGlobalName(false), icon, Key_T + CTRL, this,
+ SLOT(slotGroupGeneralTuplet()), actionCollection(), "tuplet");
+
+ new KAction(UnTupletCommand::getGlobalName(), 0, this,
+ SLOT(slotGroupUnTuplet()), actionCollection(), "break_tuplets");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeToolbarPixmap("triplet")));
+ (new KToggleAction(i18n("Trip&let Insert Mode"), icon, Key_G,
+ this, SLOT(slotUpdateInsertModeStatus()),
+ actionCollection(), "triplet_mode"))->
+ setChecked(false);
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeToolbarPixmap("chord")));
+ (new KToggleAction(i18n("C&hord Insert Mode"), icon, Key_H,
+ this, SLOT(slotUpdateInsertModeStatus()),
+ actionCollection(), "chord_mode"))->
+ setChecked(false);
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeToolbarPixmap("group-grace")));
+ (new KToggleAction(i18n("Grace Insert Mode"), icon, 0,
+ this, SLOT(slotUpdateInsertModeStatus()),
+ actionCollection(), "grace_mode"))->
+ setChecked(false);
+/*!!!
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-grace")));
+
+ new KAction(GraceCommand::getGlobalName(), icon, 0, this,
+ SLOT(slotGroupGrace()), actionCollection(), "grace");
+
+ new KAction(UnGraceCommand::getGlobalName(), 0, this,
+ SLOT(slotGroupUnGrace()), actionCollection(), "ungrace");
+*/
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-slur")));
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::Slur), icon, Key_ParenRight, this,
+ SLOT(slotGroupSlur()), actionCollection(), "slur");
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::PhrasingSlur), 0, Key_ParenRight + CTRL, this,
+ SLOT(slotGroupPhrasingSlur()), actionCollection(), "phrasing_slur");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-glissando")));
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::Glissando), icon, 0, this,
+ SLOT(slotGroupGlissando()), actionCollection(), "glissando");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-crescendo")));
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::Crescendo), icon, Key_Less, this,
+ SLOT(slotGroupCrescendo()), actionCollection(), "crescendo");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-decrescendo")));
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::Decrescendo), icon, Key_Greater, this,
+ SLOT(slotGroupDecrescendo()), actionCollection(), "decrescendo");
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::QuindicesimaUp), 0, 0, this,
+ SLOT(slotGroupOctave2Up()), actionCollection(), "octave_2up");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-ottava")));
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::OttavaUp), icon, 0, this,
+ SLOT(slotGroupOctaveUp()), actionCollection(), "octave_up");
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::OttavaDown), 0, 0, this,
+ SLOT(slotGroupOctaveDown()), actionCollection(), "octave_down");
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::QuindicesimaDown), 0, 0, this,
+ SLOT(slotGroupOctave2Down()), actionCollection(), "octave_2down");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-chord")));
+ new KAction(MakeChordCommand::getGlobalName(), icon, 0, this,
+ SLOT(slotGroupMakeChord()), actionCollection(), "make_chord");
+
+ // setup Transforms menu
+ new KAction(NormalizeRestsCommand::getGlobalName(), Key_N + CTRL, this,
+ SLOT(slotTransformsNormalizeRests()), actionCollection(),
+ "normalize_rests");
+
+ new KAction(CollapseRestsCommand::getGlobalName(), 0, this,
+ SLOT(slotTransformsCollapseRests()), actionCollection(),
+ "collapse_rests_aggressively");
+
+ new KAction(CollapseNotesCommand::getGlobalName(), Key_Equal + CTRL, this,
+ SLOT(slotTransformsCollapseNotes()), actionCollection(),
+ "collapse_notes");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transforms-tie")));
+
+ new KAction(TieNotesCommand::getGlobalName(), icon, Key_AsciiTilde, this,
+ SLOT(slotTransformsTieNotes()), actionCollection(),
+ "tie_notes");
+
+ new KAction(UntieNotesCommand::getGlobalName(), 0, this,
+ SLOT(slotTransformsUntieNotes()), actionCollection(),
+ "untie_notes");
+
+ new KAction(MakeNotesViableCommand::getGlobalName(), 0, this,
+ SLOT(slotTransformsMakeNotesViable()), actionCollection(),
+ "make_notes_viable");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transforms-decounterpoint")));
+
+ new KAction(DeCounterpointCommand::getGlobalName(), icon, 0, this,
+ SLOT(slotTransformsDeCounterpoint()), actionCollection(),
+ "de_counterpoint");
+
+ new KAction(ChangeStemsCommand::getGlobalName(true),
+ 0, Key_PageUp + CTRL, this,
+ SLOT(slotTransformsStemsUp()), actionCollection(),
+ "stems_up");
+
+ new KAction(ChangeStemsCommand::getGlobalName(false),
+ 0, Key_PageDown + CTRL, this,
+ SLOT(slotTransformsStemsDown()), actionCollection(),
+ "stems_down");
+
+ new KAction(RestoreStemsCommand::getGlobalName(), 0, this,
+ SLOT(slotTransformsRestoreStems()), actionCollection(),
+ "restore_stems");
+
+ new KAction(ChangeSlurPositionCommand::getGlobalName(true),
+ 0, this,
+ SLOT(slotTransformsSlursAbove()), actionCollection(),
+ "slurs_above");
+
+ new KAction(ChangeSlurPositionCommand::getGlobalName(false),
+ 0, this,
+ SLOT(slotTransformsSlursBelow()), actionCollection(),
+ "slurs_below");
+
+ new KAction(RestoreSlursCommand::getGlobalName(), 0, this,
+ SLOT(slotTransformsRestoreSlurs()), actionCollection(),
+ "restore_slurs");
+
+ new KAction(ChangeTiePositionCommand::getGlobalName(true),
+ 0, this,
+ SLOT(slotTransformsTiesAbove()), actionCollection(),
+ "ties_above");
+
+ new KAction(ChangeTiePositionCommand::getGlobalName(false),
+ 0, this,
+ SLOT(slotTransformsTiesBelow()), actionCollection(),
+ "ties_below");
+
+ new KAction(RestoreTiesCommand::getGlobalName(), 0, this,
+ SLOT(slotTransformsRestoreTies()), actionCollection(),
+ "restore_ties");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("accmenu-doubleflat")));
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Set, Accidentals::DoubleFlat),
+ icon, 0, this,
+ SLOT(slotRespellDoubleFlat()), actionCollection(),
+ "respell_doubleflat");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("accmenu-flat")));
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Set, Accidentals::Flat),
+ icon, 0, this,
+ SLOT(slotRespellFlat()), actionCollection(),
+ "respell_flat");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("accmenu-natural")));
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Set, Accidentals::Natural),
+ icon, 0, this,
+ SLOT(slotRespellNatural()), actionCollection(),
+ "respell_natural");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("accmenu-sharp")));
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Set, Accidentals::Sharp),
+ icon, 0, this,
+ SLOT(slotRespellSharp()), actionCollection(),
+ "respell_sharp");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("accmenu-doublesharp")));
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Set, Accidentals::DoubleSharp),
+ icon, 0, this,
+ SLOT(slotRespellDoubleSharp()), actionCollection(),
+ "respell_doublesharp");
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Up, Accidentals::NoAccidental),
+ Key_Up + CTRL + SHIFT, this,
+ SLOT(slotRespellUp()), actionCollection(),
+ "respell_up");
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Down, Accidentals::NoAccidental),
+ Key_Down + CTRL + SHIFT, this,
+ SLOT(slotRespellDown()), actionCollection(),
+ "respell_down");
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Restore, Accidentals::NoAccidental),
+ 0, this,
+ SLOT(slotRespellRestore()), actionCollection(),
+ "respell_restore");
+
+ new KAction(MakeAccidentalsCautionaryCommand::getGlobalName(true),
+ 0, this,
+ SLOT(slotShowCautionary()), actionCollection(),
+ "show_cautionary");
+
+ new KAction(MakeAccidentalsCautionaryCommand::getGlobalName(false),
+ 0, this,
+ SLOT(slotCancelCautionary()), actionCollection(),
+ "cancel_cautionary");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("quantize")));
+
+ new KAction(EventQuantizeCommand::getGlobalName(), icon, Key_Equal, this,
+ SLOT(slotTransformsQuantize()), actionCollection(),
+ "quantize");
+
+ new KAction(FixNotationQuantizeCommand::getGlobalName(), 0,
+ this, SLOT(slotTransformsFixQuantization()), actionCollection(),
+ "fix_quantization");
+
+ new KAction(RemoveNotationQuantizeCommand::getGlobalName(), 0,
+ this, SLOT(slotTransformsRemoveQuantization()), actionCollection(),
+ "remove_quantization");
+
+ new KAction(InterpretCommand::getGlobalName(), 0,
+ this, SLOT(slotTransformsInterpret()), actionCollection(),
+ "interpret");
+
+ new KAction(i18n("&Dump selected events to stderr"), 0, this,
+ SLOT(slotDebugDump()), actionCollection(), "debug_dump");
+
+ for (MarkActionDataMap::Iterator i = m_markActionDataMap->begin();
+ i != m_markActionDataMap->end(); ++i) {
+
+ const MarkActionData &markActionData = **i;
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeMarkMenuPixmap(markActionData.mark)));
+
+ new KAction(markActionData.title,
+ icon,
+ markActionData.keycode,
+ this,
+ SLOT(slotAddMark()),
+ actionCollection(),
+ markActionData.actionName);
+ }
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("text-mark")));
+
+ new KAction(AddTextMarkCommand::getGlobalName(), icon, 0, this,
+ SLOT(slotMarksAddTextMark()), actionCollection(),
+ "add_text_mark");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName("0"), 0, Key_0 + ALT, this,
+ SLOT(slotMarksAddFingeringMarkFromAction()), actionCollection(),
+ "add_fingering_0");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName("1"), 0, Key_1 + ALT, this,
+ SLOT(slotMarksAddFingeringMarkFromAction()), actionCollection(),
+ "add_fingering_1");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName("2"), 0, Key_2 + ALT, this,
+ SLOT(slotMarksAddFingeringMarkFromAction()), actionCollection(),
+ "add_fingering_2");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName("3"), 0, Key_3 + ALT, this,
+ SLOT(slotMarksAddFingeringMarkFromAction()), actionCollection(),
+ "add_fingering_3");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName("4"), 0, Key_4 + ALT, this,
+ SLOT(slotMarksAddFingeringMarkFromAction()), actionCollection(),
+ "add_fingering_4");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName("5"), 0, Key_5 + ALT, this,
+ SLOT(slotMarksAddFingeringMarkFromAction()), actionCollection(),
+ "add_fingering_5");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName("+"), 0, Key_9 + ALT, this,
+ SLOT(slotMarksAddFingeringMarkFromAction()), actionCollection(),
+ "add_fingering_plus");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName(), 0, 0, this,
+ SLOT(slotMarksAddFingeringMark()), actionCollection(),
+ "add_fingering_mark");
+
+ new KAction(RemoveMarksCommand::getGlobalName(), 0, this,
+ SLOT(slotMarksRemoveMarks()), actionCollection(),
+ "remove_marks");
+
+ new KAction(RemoveFingeringMarksCommand::getGlobalName(), 0, this,
+ SLOT(slotMarksRemoveFingeringMarks()), actionCollection(),
+ "remove_fingering_marks");
+
+ new KAction(i18n("Ma&ke Ornament..."), 0, this,
+ SLOT(slotMakeOrnament()), actionCollection(),
+ "make_ornament");
+
+ new KAction(i18n("Trigger &Ornament..."), 0, this,
+ SLOT(slotUseOrnament()), actionCollection(),
+ "use_ornament");
+
+ new KAction(i18n("Remove Ornament..."), 0, this,
+ SLOT(slotRemoveOrnament()), actionCollection(),
+ "remove_ornament");
+
+ static QString slashTitles[] = {
+ i18n("&None"), "&1", "&2", "&3", "&4", "&5"
+ };
+ for (int i = 0; i <= 5; ++i) {
+ new KAction(slashTitles[i], 0, this,
+ SLOT(slotAddSlashes()), actionCollection(),
+ QString("slashes_%1").arg(i));
+ }
+
+ new KAction(ClefInsertionCommand::getGlobalName(), 0, this,
+ SLOT(slotEditAddClef()), actionCollection(),
+ "add_clef");
+
+ new KAction(KeyInsertionCommand::getGlobalName(), 0, this,
+ SLOT(slotEditAddKeySignature()), actionCollection(),
+ "add_key_signature");
+
+ new KAction(SustainInsertionCommand::getGlobalName(true), 0, this,
+ SLOT(slotEditAddSustainDown()), actionCollection(),
+ "add_sustain_down");
+
+ new KAction(SustainInsertionCommand::getGlobalName(false), 0, this,
+ SLOT(slotEditAddSustainUp()), actionCollection(),
+ "add_sustain_up");
+
+ new KAction(TransposeCommand::getDiatonicGlobalName(false), 0, this,
+ SLOT(slotEditTranspose()), actionCollection(),
+ "transpose_segment");
+
+ new KAction(i18n("Convert Notation For..."), 0, this,
+ SLOT(slotEditSwitchPreset()), actionCollection(),
+ "switch_preset");
+
+
+ // setup Settings menu
+ static QString actionsToolbars[][4] =
+ {
+ { i18n("Show T&ools Toolbar"), "1slotToggleToolsToolBar()", "show_tools_toolbar", "palette-tools" },
+ { i18n("Show &Notes Toolbar"), "1slotToggleNotesToolBar()", "show_notes_toolbar", "palette-notes" },
+ { i18n("Show &Rests Toolbar"), "1slotToggleRestsToolBar()", "show_rests_toolbar", "palette-rests" },
+ { i18n("Show &Accidentals Toolbar"), "1slotToggleAccidentalsToolBar()", "show_accidentals_toolbar", "palette-accidentals" },
+ { i18n("Show Cle&fs Toolbar"), "1slotToggleClefsToolBar()", "show_clefs_toolbar",
+ "palette-clefs" },
+ { i18n("Show &Marks Toolbar"), "1slotToggleMarksToolBar()", "show_marks_toolbar",
+ "palette-marks" },
+ { i18n("Show &Group Toolbar"), "1slotToggleGroupToolBar()", "show_group_toolbar",
+ "palette-group" },
+ { i18n("Show &Layout Toolbar"), "1slotToggleLayoutToolBar()", "show_layout_toolbar",
+ "palette-font" },
+ { i18n("Show Trans&port Toolbar"), "1slotToggleTransportToolBar()", "show_transport_toolbar",
+ "palette-transport" },
+ { i18n("Show M&eta Toolbar"), "1slotToggleMetaToolBar()", "show_meta_toolbar",
+ "palette-meta" }
+ };
+
+ for (unsigned int i = 0;
+ i < sizeof(actionsToolbars) / sizeof(actionsToolbars[0]); ++i) {
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap(actionsToolbars[i][3])));
+
+ new KToggleAction(actionsToolbars[i][0], icon, 0,
+ this, actionsToolbars[i][1],
+ actionCollection(), actionsToolbars[i][2]);
+ }
+
+ new KAction(i18n("Cursor &Back"), 0, Key_Left, this,
+ SLOT(slotStepBackward()), actionCollection(),
+ "cursor_back");
+
+ new KAction(i18n("Cursor &Forward"), 0, Key_Right, this,
+ SLOT(slotStepForward()), actionCollection(),
+ "cursor_forward");
+
+ new KAction(i18n("Cursor Ba&ck Bar"), 0, Key_Left + CTRL, this,
+ SLOT(slotJumpBackward()), actionCollection(),
+ "cursor_back_bar");
+
+ new KAction(i18n("Cursor For&ward Bar"), 0, Key_Right + CTRL, this,
+ SLOT(slotJumpForward()), actionCollection(),
+ "cursor_forward_bar");
+
+ new KAction(i18n("Cursor Back and Se&lect"), SHIFT + Key_Left, this,
+ SLOT(slotExtendSelectionBackward()), actionCollection(),
+ "extend_selection_backward");
+
+ new KAction(i18n("Cursor Forward and &Select"), SHIFT + Key_Right, this,
+ SLOT(slotExtendSelectionForward()), actionCollection(),
+ "extend_selection_forward");
+
+ new KAction(i18n("Cursor Back Bar and Select"), SHIFT + CTRL + Key_Left, this,
+ SLOT(slotExtendSelectionBackwardBar()), actionCollection(),
+ "extend_selection_backward_bar");
+
+ new KAction(i18n("Cursor Forward Bar and Select"), SHIFT + CTRL + Key_Right, this,
+ SLOT(slotExtendSelectionForwardBar()), actionCollection(),
+ "extend_selection_forward_bar");
+
+ /*!!! not here yet
+ new KAction(i18n("Move Selection Left"), Key_Minus, this,
+ SLOT(slotMoveSelectionLeft()), actionCollection(),
+ "move_selection_left");
+ */
+
+ new KAction(i18n("Cursor to St&art"), 0,
+ /* #1025717: conflicting meanings for ctrl+a - dupe with Select All
+ Key_A + CTRL, */ this,
+ SLOT(slotJumpToStart()), actionCollection(),
+ "cursor_start");
+
+ new KAction(i18n("Cursor to &End"), 0, Key_E + CTRL, this,
+ SLOT(slotJumpToEnd()), actionCollection(),
+ "cursor_end");
+
+ new KAction(i18n("Cursor &Up Staff"), 0, Key_Up + SHIFT, this,
+ SLOT(slotCurrentStaffUp()), actionCollection(),
+ "cursor_up_staff");
+
+ new KAction(i18n("Cursor &Down Staff"), 0, Key_Down + SHIFT, this,
+ SLOT(slotCurrentStaffDown()), actionCollection(),
+ "cursor_down_staff");
+
+ new KAction(i18n("Cursor Pre&vious Segment"), 0, Key_Prior + ALT, this,
+ SLOT(slotCurrentSegmentPrior()), actionCollection(),
+ "cursor_prior_segment");
+
+ new KAction(i18n("Cursor Ne&xt Segment"), 0, Key_Next + ALT, this,
+ SLOT(slotCurrentSegmentNext()), actionCollection(),
+ "cursor_next_segment");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-cursor-to-pointer")));
+ new KAction(i18n("Cursor to &Playback Pointer"), icon, 0, this,
+ SLOT(slotJumpCursorToPlayback()), actionCollection(),
+ "cursor_to_playback_pointer");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-play")));
+ KAction *play = new KAction(i18n("&Play"), icon, Key_Enter, this,
+ SIGNAL(play()), actionCollection(), "play");
+ // Alternative shortcut for Play
+ KShortcut playShortcut = play->shortcut();
+ playShortcut.append( KKey(Key_Return + CTRL) );
+ play->setShortcut(playShortcut);
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-stop")));
+ new KAction(i18n("&Stop"), icon, Key_Insert, this,
+ SIGNAL(stop()), actionCollection(), "stop");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-rewind")));
+ new KAction(i18n("Re&wind"), icon, Key_End, this,
+ SIGNAL(rewindPlayback()), actionCollection(),
+ "playback_pointer_back_bar");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-ffwd")));
+ new KAction(i18n("&Fast Forward"), icon, Key_PageDown, this,
+ SIGNAL(fastForwardPlayback()), actionCollection(),
+ "playback_pointer_forward_bar");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-rewind-end")));
+ new KAction(i18n("Rewind to &Beginning"), icon, 0, this,
+ SIGNAL(rewindPlaybackToBeginning()), actionCollection(),
+ "playback_pointer_start");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-ffwd-end")));
+ new KAction(i18n("Fast Forward to &End"), icon, 0, this,
+ SIGNAL(fastForwardPlaybackToEnd()), actionCollection(),
+ "playback_pointer_end");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-pointer-to-cursor")));
+ new KAction(i18n("Playback Pointer to &Cursor"), icon, 0, this,
+ SLOT(slotJumpPlaybackToCursor()), actionCollection(),
+ "playback_pointer_to_cursor");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-solo")));
+ new KToggleAction(i18n("&Solo"), icon, 0, this,
+ SLOT(slotToggleSolo()), actionCollection(),
+ "toggle_solo");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-tracking")));
+ (new KToggleAction(i18n("Scro&ll to Follow Playback"), icon, Key_Pause, this,
+ SLOT(slotToggleTracking()), actionCollection(),
+ "toggle_tracking"))->setChecked(m_playTracking);
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-panic")));
+ new KAction(i18n("Panic"), icon, Key_P + CTRL + ALT, this,
+ SIGNAL(panic()), actionCollection(), "panic");
+
+ new KAction(i18n("Set Loop to Selection"), Key_Semicolon + CTRL, this,
+ SLOT(slotPreviewSelection()), actionCollection(),
+ "preview_selection");
+
+ new KAction(i18n("Clear L&oop"), Key_Colon + CTRL, this,
+ SLOT(slotClearLoop()), actionCollection(),
+ "clear_loop");
+
+ new KAction(i18n("Clear Selection"), Key_Escape, this,
+ SLOT(slotClearSelection()), actionCollection(),
+ "clear_selection");
+
+ // QString pixmapDir =
+ // KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ // icon = QIconSet(QCanvasPixmap(pixmapDir + "/toolbar/eventfilter.xpm"));
+ new KAction(i18n("&Filter Selection"), "filter", Key_F + CTRL, this,
+ SLOT(slotFilterSelection()), actionCollection(),
+ "filter_selection");
+
+ new KAction(i18n("Push &Left"), 0, this,
+ SLOT(slotFinePositionLeft()), actionCollection(),
+ "fine_position_left");
+
+ new KAction(i18n("Push &Right"), 0, this,
+ SLOT(slotFinePositionRight()), actionCollection(),
+ "fine_position_right");
+
+ new KAction(i18n("Push &Up"), 0, this,
+ SLOT(slotFinePositionUp()), actionCollection(),
+ "fine_position_up");
+
+ new KAction(i18n("Push &Down"), 0, this,
+ SLOT(slotFinePositionDown()), actionCollection(),
+ "fine_position_down");
+
+ new KAction(i18n("&Restore Positions"), 0, this,
+ SLOT(slotFinePositionRestore()), actionCollection(),
+ "fine_position_restore");
+
+ new KAction(i18n("Make &Invisible"), 0, this,
+ SLOT(slotMakeInvisible()), actionCollection(),
+ "make_invisible");
+
+ new KAction(i18n("Make &Visible"), 0, this,
+ SLOT(slotMakeVisible()), actionCollection(),
+ "make_visible");
+
+ new KAction(i18n("Toggle Dot"), Key_Period, this,
+ SLOT(slotToggleDot()), actionCollection(),
+ "toggle_dot");
+
+ new KAction(i18n("Add Dot"), Key_Period + CTRL, this,
+ SLOT(slotAddDot()), actionCollection(),
+ "add_dot");
+
+ new KAction(i18n("Add Dot"), Key_Period + CTRL + ALT, this,
+ SLOT(slotAddDotNotationOnly()), actionCollection(),
+ "add_notation_dot");
+
+ createGUI(getRCFileName(), false);
+}
+
+bool
+NotationView::isInChordMode()
+{
+ return ((KToggleAction *)actionCollection()->action("chord_mode"))->
+ isChecked();
+}
+
+bool
+NotationView::isInTripletMode()
+{
+ return ((KToggleAction *)actionCollection()->action("triplet_mode"))->
+ isChecked();
+}
+
+bool
+NotationView::isInGraceMode()
+{
+ return ((KToggleAction *)actionCollection()->action("grace_mode"))->
+ isChecked();
+}
+
+void
+NotationView::setupFontSizeMenu(std::string oldFontName)
+{
+ if (oldFontName != "") {
+
+ std::vector<int> sizes = NoteFontFactory::getScreenSizes(oldFontName);
+
+ for (unsigned int i = 0; i < sizes.size(); ++i) {
+ KAction *action =
+ actionCollection()->action
+ (QString("note_font_size_%1").arg(sizes[i]));
+ m_fontSizeActionMenu->remove
+ (action);
+
+ // Don't delete -- that could cause a crash when this
+ // function is called from the action itself. Instead
+ // we reuse and reinsert existing actions below.
+ }
+ }
+
+ std::vector<int> sizes = NoteFontFactory::getScreenSizes(m_fontName);
+
+ for (unsigned int i = 0; i < sizes.size(); ++i) {
+
+ QString actionName = QString("note_font_size_%1").arg(sizes[i]);
+
+ KToggleAction *sizeAction = dynamic_cast<KToggleAction *>
+ (actionCollection()->action(actionName));
+
+ if (!sizeAction) {
+ sizeAction =
+ new KToggleAction(i18n("1 pixel", "%n pixels", sizes[i]),
+ 0, this,
+ SLOT(slotChangeFontSizeFromAction()),
+ actionCollection(), actionName);
+ }
+
+ sizeAction->setChecked(sizes[i] == m_fontSize);
+ m_fontSizeActionMenu->insert(sizeAction);
+ }
+}
+
+LinedStaff *
+NotationView::getLinedStaff(int i)
+{
+ return getNotationStaff(i);
+}
+
+LinedStaff *
+NotationView::getLinedStaff(const Segment &segment)
+{
+ return getNotationStaff(segment);
+}
+
+NotationStaff *
+NotationView::getNotationStaff(const Segment &segment)
+{
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ if (&(m_staffs[i]->getSegment()) == &segment)
+ return m_staffs[i];
+ }
+ return 0;
+}
+
+bool NotationView::isCurrentStaff(int i)
+{
+ return getCurrentSegment() == &(m_staffs[i]->getSegment());
+}
+
+void NotationView::initLayoutToolbar()
+{
+ KToolBar *layoutToolbar = toolBar("Layout Toolbar");
+
+ if (!layoutToolbar) {
+ std::cerr
+ << "NotationView::initLayoutToolbar() : layout toolbar not found"
+ << std::endl;
+ return ;
+ }
+
+ new QLabel(i18n(" Font: "), layoutToolbar, "font label");
+
+ //
+ // font combo
+ //
+ m_fontCombo = new KComboBox(layoutToolbar);
+ m_fontCombo->setEditable(false);
+
+ std::set
+ <std::string> fs(NoteFontFactory::getFontNames());
+ std::vector<std::string> f(fs.begin(), fs.end());
+ std::sort(f.begin(), f.end());
+
+ bool foundFont = false;
+
+ for (std::vector<std::string>::iterator i = f.begin(); i != f.end(); ++i) {
+
+ QString fontQName(strtoqstr(*i));
+
+ m_fontCombo->insertItem(fontQName);
+ if (fontQName.lower() == strtoqstr(m_fontName).lower()) {
+ m_fontCombo->setCurrentItem(m_fontCombo->count() - 1);
+ foundFont = true;
+ }
+ }
+
+ if (!foundFont) {
+ KMessageBox::sorry
+ (this, i18n("Unknown font \"%1\", using default").arg
+ (strtoqstr(m_fontName)));
+ m_fontName = NoteFontFactory::getDefaultFontName();
+ }
+
+ connect(m_fontCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotChangeFont(const QString &)));
+
+ new QLabel(i18n(" Size: "), layoutToolbar, "size label");
+
+ QString value;
+
+ //
+ // font size combo
+ //
+ std::vector<int> sizes = NoteFontFactory::getScreenSizes(m_fontName);
+ m_fontSizeCombo = new KComboBox(layoutToolbar, "font size combo");
+
+ for (std::vector<int>::iterator i = sizes.begin(); i != sizes.end(); ++i) {
+
+ value.setNum(*i);
+ m_fontSizeCombo->insertItem(value);
+ }
+ // set combo's current value to default
+ value.setNum(m_fontSize);
+ m_fontSizeCombo->setCurrentText(value);
+
+ connect(m_fontSizeCombo, SIGNAL(activated(const QString&)),
+ this, SLOT(slotChangeFontSizeFromStringValue(const QString&)));
+
+ new QLabel(i18n(" Spacing: "), layoutToolbar, "spacing label");
+
+ //
+ // spacing combo
+ //
+ int defaultSpacing = m_hlayout->getSpacing();
+ std::vector<int> spacings = NotationHLayout::getAvailableSpacings();
+
+ m_spacingCombo = new KComboBox(layoutToolbar, "spacing combo");
+ for (std::vector<int>::iterator i = spacings.begin(); i != spacings.end(); ++i) {
+
+ value.setNum(*i);
+ value += "%";
+ m_spacingCombo->insertItem(value);
+ }
+ // set combo's current value to default
+ value.setNum(defaultSpacing);
+ value += "%";
+ m_spacingCombo->setCurrentText(value);
+
+ connect(m_spacingCombo, SIGNAL(activated(const QString&)),
+ this, SLOT(slotChangeSpacingFromStringValue(const QString&)));
+}
+
+void NotationView::initStatusBar()
+{
+ KStatusBar* sb = statusBar();
+
+ m_hoveredOverNoteName = new QLabel(sb);
+ m_hoveredOverNoteName->setMinimumWidth(32);
+
+ m_hoveredOverAbsoluteTime = new QLabel(sb);
+ m_hoveredOverAbsoluteTime->setMinimumWidth(160);
+
+ sb->addWidget(m_hoveredOverAbsoluteTime);
+ sb->addWidget(m_hoveredOverNoteName);
+
+ QHBox *hbox = new QHBox(sb);
+ m_currentNotePixmap = new QLabel(hbox);
+ m_currentNotePixmap->setMinimumWidth(20);
+ m_insertModeLabel = new QLabel(hbox);
+ m_annotationsLabel = new QLabel(hbox);
+ m_lilyPondDirectivesLabel = new QLabel(hbox);
+ sb->addWidget(hbox);
+
+ sb->insertItem(KTmpStatusMsg::getDefaultMsg(),
+ KTmpStatusMsg::getDefaultId(), 1);
+ sb->setItemAlignment(KTmpStatusMsg::getDefaultId(),
+ AlignLeft | AlignVCenter);
+
+ m_selectionCounter = new QLabel(sb);
+ sb->addWidget(m_selectionCounter);
+
+ m_progressBar = new ProgressBar(100, true, sb);
+ m_progressBar->setMinimumWidth(100);
+ sb->addWidget(m_progressBar);
+}
+
+QSize NotationView::getViewSize()
+{
+ return canvas()->size();
+}
+
+void NotationView::setViewSize(QSize s)
+{
+ canvas()->resize(s.width(), s.height());
+
+ if ( (m_pageMode == LinedStaff::LinearMode)
+ && (m_showHeadersGroup != HeadersGroup::ShowNever)) {
+ m_headersGroup->completeToHeight(s.height());
+ }
+}
+
+void
+NotationView::setPageMode(LinedStaff::PageMode pageMode)
+{
+ m_pageMode = pageMode;
+
+ if (pageMode != LinedStaff::LinearMode) {
+ if (m_topStandardRuler)
+ m_topStandardRuler->hide();
+ if (m_bottomStandardRuler)
+ m_bottomStandardRuler->hide();
+ if (m_chordNameRuler)
+ m_chordNameRuler->hide();
+ if (m_rawNoteRuler)
+ m_rawNoteRuler->hide();
+ if (m_tempoRuler)
+ m_tempoRuler->hide();
+ hideHeadersGroup();
+ } else {
+ if (m_topStandardRuler)
+ m_topStandardRuler->show();
+ if (m_bottomStandardRuler)
+ m_bottomStandardRuler->show();
+ if (m_chordNameRuler && getToggleAction("show_chords_ruler")->isChecked())
+ m_chordNameRuler->show();
+ if (m_rawNoteRuler && getToggleAction("show_raw_note_ruler")->isChecked())
+ m_rawNoteRuler->show();
+ if (m_tempoRuler && getToggleAction("show_tempo_ruler")->isChecked())
+ m_tempoRuler->show();
+ showHeadersGroup();
+ }
+
+ stateChanged("linear_mode",
+ (pageMode == LinedStaff::LinearMode ? KXMLGUIClient::StateNoReverse :
+ KXMLGUIClient::StateReverse));
+
+ int pageWidth = getPageWidth();
+ int topMargin = 0, leftMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+
+ m_hlayout->setPageMode(pageMode != LinedStaff::LinearMode);
+ m_hlayout->setPageWidth(pageWidth - leftMargin * 2);
+
+ NOTATION_DEBUG << "NotationView::setPageMode: set layout's page width to "
+ << (pageWidth - leftMargin * 2) << endl;
+
+ positionStaffs();
+
+ bool layoutApplied = applyLayout();
+ if (!layoutApplied)
+ KMessageBox::sorry(0, "Couldn't apply layout");
+ else {
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->markChanged();
+ }
+ }
+
+ if (!m_printMode) {
+ // Layout is done : Time related to left of canvas should now
+ // correctly be determined and track headers contents be drawn.
+ m_headersGroup->slotUpdateAllHeaders(0, 0, true);
+ }
+
+ positionPages();
+
+ if (!m_printMode) {
+ updateView();
+ slotSetInsertCursorPosition(getInsertionTime(), false, false);
+ slotSetPointerPosition(getDocument()->getComposition().getPosition(), false);
+ }
+
+ Profiles::getInstance()->dump();
+}
+
+int
+NotationView::getPageWidth()
+{
+ if (m_pageMode != LinedStaff::MultiPageMode) {
+
+ if (isInPrintMode() && getCanvasView() && getCanvasView()->canvas())
+ return getCanvasView()->canvas()->width();
+
+ if (getCanvasView()) {
+ return
+ getCanvasView()->width() -
+ getCanvasView()->verticalScrollBar()->width() -
+ m_leftGutter - 10;
+ }
+
+ return width() - 50;
+
+ } else {
+
+ //!!! For the moment we use A4 for this calculation
+
+ double printSizeMm = 25.4 * ((double)m_printSize / 72.0);
+ double mmPerPixel = printSizeMm / (double)m_notePixmapFactory->getSize();
+ return (int)(210.0 / mmPerPixel);
+ }
+}
+
+int
+NotationView::getPageHeight()
+{
+ if (m_pageMode != LinedStaff::MultiPageMode) {
+
+ if (isInPrintMode() && getCanvasView() && getCanvasView()->canvas())
+ return getCanvasView()->canvas()->height();
+
+ if (getCanvasView()) {
+ return getCanvasView()->height();
+ }
+
+ return (height() > 200 ? height() - 100 : height());
+
+ } else {
+
+ //!!! For the moment we use A4 for this calculation
+
+ double printSizeMm = 25.4 * ((double)m_printSize / 72.0);
+ double mmPerPixel = printSizeMm / (double)m_notePixmapFactory->getSize();
+ return (int)(297.0 / mmPerPixel);
+ }
+}
+
+void
+NotationView::getPageMargins(int &left, int &top)
+{
+ if (m_pageMode != LinedStaff::MultiPageMode) {
+
+ left = 0;
+ top = 0;
+
+ } else {
+
+ //!!! For the moment we use A4 for this calculation
+
+ double printSizeMm = 25.4 * ((double)m_printSize / 72.0);
+ double mmPerPixel = printSizeMm / (double)m_notePixmapFactory->getSize();
+ left = (int)(20.0 / mmPerPixel);
+ top = (int)(15.0 / mmPerPixel);
+ }
+}
+
+void
+NotationView::scrollToTime(timeT t)
+{
+
+ double notationViewLayoutCoord = m_hlayout->getXForTime(t);
+
+ // Doesn't appear to matter which staff we use
+ //!!! actually it probably does matter, if they don't have the same extents
+ double notationViewCanvasCoord =
+ getLinedStaff(0)->getCanvasCoordsForLayoutCoords
+ (notationViewLayoutCoord, 0).first;
+
+ // HK: I could have sworn I saw a hard-coded scroll happen somewhere
+ // (i.e. a default extra scroll to make up for the staff not beginning on
+ // the left edge) but now I can't find it.
+ getCanvasView()->slotScrollHorizSmallSteps
+ (int(notationViewCanvasCoord)); // + DEFAULT_STAFF_OFFSET);
+}
+
+RulerScale*
+NotationView::getHLayout()
+{
+ return m_hlayout;
+}
+
+void
+NotationView::paintEvent(QPaintEvent *e)
+{
+ m_inPaintEvent = true;
+
+ // This is duplicated here from EditViewBase, because (a) we need
+ // to know about the segment being removed before we try to check
+ // the staff names etc., and (b) it's not safe to call close()
+ // from EditViewBase::paintEvent if we're then going to try to do
+ // some more work afterwards in this function
+
+ if (isCompositionModified()) {
+
+ // Check if one of the segments we display has been removed
+ // from the composition.
+ //
+ // For the moment we'll have to close the view if any of the
+ // segments we handle has been deleted.
+
+ for (unsigned int i = 0; i < m_segments.size(); ++i) {
+
+ if (!m_segments[i]->getComposition()) {
+ // oops, I think we've been deleted
+ close();
+ return ;
+ }
+ }
+ }
+
+ int topMargin = 0, leftMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+
+ if (m_pageMode == LinedStaff::ContinuousPageMode) {
+ // relayout if the window width changes significantly in continuous page mode
+ int diff = int(getPageWidth() - leftMargin * 2 - m_hlayout->getPageWidth());
+ if (diff < -10 || diff > 10) {
+ setPageMode(m_pageMode);
+ refreshSegment(0, 0, 0);
+ }
+
+ } else if (m_pageMode == LinedStaff::LinearMode) {
+ // resize canvas again if the window height has changed significantly
+ if (getCanvasView() && getCanvasView()->canvas()) {
+ int diff = int(getPageHeight() - getCanvasView()->canvas()->height());
+ if (diff > 10) {
+ readjustCanvasSize();
+ }
+ }
+ }
+
+ // check for staff name changes
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ if (!m_staffs[i]->isStaffNameUpToDate()) {
+ refreshSegment(0);
+ break;
+ }
+ }
+
+ m_inPaintEvent = false;
+
+ EditView::paintEvent(e);
+
+ m_inPaintEvent = false;
+
+ // now deal with any backlog of insertable notes that appeared
+ // during paint (because it's not safe to modify a segment from
+ // within a sub-event-loop in a processEvents call from a paint)
+ if (!m_pendingInsertableNotes.empty()) {
+ std::vector<std::pair<int, int> > notes = m_pendingInsertableNotes;
+ m_pendingInsertableNotes.clear();
+ for (unsigned int i = 0; i < notes.size(); ++i) {
+ slotInsertableNoteEventReceived(notes[i].first, notes[i].second, true);
+ }
+ }
+
+ slotSetOperationNameAndStatus(i18n(" Ready."));
+}
+
+bool NotationView::applyLayout(int staffNo, timeT startTime, timeT endTime)
+{
+ slotSetOperationNameAndStatus(i18n("Laying out score..."));
+ ProgressDialog::processEvents();
+
+ m_hlayout->setStaffCount(m_staffs.size());
+
+ Profiler profiler("NotationView::applyLayout");
+ unsigned int i;
+
+ for (i = 0; i < m_staffs.size(); ++i) {
+
+ if (staffNo >= 0 && (int)i != staffNo)
+ continue;
+
+ slotSetOperationNameAndStatus(i18n("Laying out staff %1...").arg(i + 1));
+ ProgressDialog::processEvents();
+
+ m_hlayout->resetStaff(*m_staffs[i], startTime, endTime);
+ m_vlayout->resetStaff(*m_staffs[i], startTime, endTime);
+ m_hlayout->scanStaff(*m_staffs[i], startTime, endTime);
+ m_vlayout->scanStaff(*m_staffs[i], startTime, endTime);
+ }
+
+ slotSetOperationNameAndStatus(i18n("Reconciling staffs..."));
+ ProgressDialog::processEvents();
+
+ m_hlayout->finishLayout(startTime, endTime);
+ m_vlayout->finishLayout(startTime, endTime);
+
+ // find the last finishing staff for future use
+
+ timeT lastFinishingStaffEndTime = 0;
+ bool haveEndTime = false;
+ m_lastFinishingStaff = -1;
+
+ timeT firstStartingStaffStartTime = 0;
+ bool haveStartTime = false;
+ int firstStartingStaff = -1;
+
+ for (i = 0; i < m_staffs.size(); ++i) {
+
+ timeT thisStartTime = m_staffs[i]->getSegment().getStartTime();
+ if (thisStartTime < firstStartingStaffStartTime || !haveStartTime) {
+ firstStartingStaffStartTime = thisStartTime;
+ haveStartTime = true;
+ firstStartingStaff = i;
+ }
+
+ timeT thisEndTime = m_staffs[i]->getSegment().getEndTime();
+ if (thisEndTime > lastFinishingStaffEndTime || !haveEndTime) {
+ lastFinishingStaffEndTime = thisEndTime;
+ haveEndTime = true;
+ m_lastFinishingStaff = i;
+ }
+ }
+
+ readjustCanvasSize();
+ if (m_topStandardRuler) {
+ m_topStandardRuler->update();
+ }
+ if (m_bottomStandardRuler) {
+ m_bottomStandardRuler->update();
+ }
+ if (m_tempoRuler && m_tempoRuler->isVisible()) {
+ m_tempoRuler->update();
+ }
+ if (m_rawNoteRuler && m_rawNoteRuler->isVisible()) {
+ m_rawNoteRuler->update();
+ }
+
+ return true;
+}
+
+void NotationView::setCurrentSelectedNote(const char *pixmapName,
+ bool rest, Note::Type n, int dots)
+{
+ NoteInserter* inserter = 0;
+
+ if (rest)
+ inserter = dynamic_cast<NoteInserter*>(m_toolBox->getTool(RestInserter::ToolName));
+ else
+ inserter = dynamic_cast<NoteInserter*>(m_toolBox->getTool(NoteInserter::ToolName));
+
+ inserter->slotSetNote(n);
+ inserter->slotSetDots(dots);
+
+ setTool(inserter);
+
+ m_currentNotePixmap->setPixmap
+ (NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeToolbarPixmap(pixmapName, true)));
+
+ emit changeCurrentNote(rest, n);
+}
+
+void NotationView::setCurrentSelectedNote(const NoteActionData &noteAction)
+{
+ setCurrentSelectedNote(noteAction.pixmapName,
+ noteAction.rest,
+ noteAction.noteType,
+ noteAction.dots);
+}
+
+void NotationView::setCurrentSelection(EventSelection* s, bool preview,
+ bool redrawNow)
+{
+ //!!! rather too much here shared with matrixview -- could much of
+ // this be in editview?
+
+ if (m_currentEventSelection == s)
+ return ;
+ NOTATION_DEBUG << "XXX " << endl;
+
+ EventSelection *oldSelection = m_currentEventSelection;
+ m_currentEventSelection = s;
+
+ // positionElements is overkill here, but we hope it's not too
+ // much overkill (if that's not a contradiction)
+
+ timeT startA, endA, startB, endB;
+
+ if (oldSelection) {
+ startA = oldSelection->getStartTime();
+ endA = oldSelection->getEndTime();
+ startB = s ? s->getStartTime() : startA;
+ endB = s ? s->getEndTime() : endA;
+ } else {
+ // we know they can't both be null -- first thing we tested above
+ startA = startB = s->getStartTime();
+ endA = endB = s->getEndTime();
+ }
+
+ // refreshSegment takes start==end to mean refresh everything
+ if (startA == endA)
+ ++endA;
+ if (startB == endB)
+ ++endB;
+
+ bool updateRequired = true;
+
+ // play previews if appropriate -- also permits an optimisation
+ // for the case where the selection is unchanged (quite likely
+ // when sweeping)
+
+ if (s && preview) {
+
+ bool foundNewEvent = false;
+
+ for (EventSelection::eventcontainer::iterator i =
+ s->getSegmentEvents().begin();
+ i != s->getSegmentEvents().end(); ++i) {
+
+ if (oldSelection && oldSelection->getSegment() == s->getSegment()
+ && oldSelection->contains(*i))
+ continue;
+
+ foundNewEvent = true;
+
+ long pitch;
+ if (!(*i)->get
+ <Int>(BaseProperties::PITCH,
+ pitch)) continue;
+
+ long velocity = -1;
+ (void)(*i)->get
+ <Int>(BaseProperties::VELOCITY,
+ velocity);
+
+ if (!((*i)->has(BaseProperties::TIED_BACKWARD) &&
+ (*i)->get
+ <Bool>
+ (BaseProperties::TIED_BACKWARD)))
+ playNote(s->getSegment(), pitch, velocity);
+ }
+
+ if (!foundNewEvent) {
+ if (oldSelection &&
+ oldSelection->getSegment() == s->getSegment() &&
+ oldSelection->getSegmentEvents().size() ==
+ s->getSegmentEvents().size())
+ updateRequired = false;
+ }
+ }
+
+ if (updateRequired) {
+
+ if (!s || !oldSelection ||
+ (endA >= startB && endB >= startA &&
+ oldSelection->getSegment() == s->getSegment())) {
+
+ // the regions overlap: use their union and just do one refresh
+
+ Segment &segment(s ? s->getSegment() :
+ oldSelection->getSegment());
+
+ if (redrawNow) {
+ // recolour the events now
+ getLinedStaff(segment)->positionElements(std::min(startA, startB),
+ std::max(endA, endB));
+ } else {
+ // mark refresh status and then request a repaint
+ segment.getRefreshStatus
+ (m_segmentsRefreshStatusIds
+ [getLinedStaff(segment)->getId()]).
+ push(std::min(startA, startB), std::max(endA, endB));
+ }
+
+ } else {
+ // do two refreshes, one for each -- here we know neither is null
+
+ if (redrawNow) {
+ // recolour the events now
+ getLinedStaff(oldSelection->getSegment())->positionElements(startA,
+ endA);
+
+ getLinedStaff(s->getSegment())->positionElements(startB, endB);
+ } else {
+ // mark refresh status and then request a repaint
+
+ oldSelection->getSegment().getRefreshStatus
+ (m_segmentsRefreshStatusIds
+ [getLinedStaff(oldSelection->getSegment())->getId()]).
+ push(startA, endA);
+
+ s->getSegment().getRefreshStatus
+ (m_segmentsRefreshStatusIds
+ [getLinedStaff(s->getSegment())->getId()]).
+ push(startB, endB);
+ }
+ }
+
+ if (s) {
+ // make the staff containing the selection current
+ int staffId = getLinedStaff(s->getSegment())->getId();
+ if (staffId != m_currentStaff)
+ slotSetCurrentStaff(staffId);
+ }
+ }
+
+ delete oldSelection;
+
+ statusBar()->changeItem(KTmpStatusMsg::getDefaultMsg(),
+ KTmpStatusMsg::getDefaultId());
+
+ if (s) {
+ int eventsSelected = s->getSegmentEvents().size();
+ m_selectionCounter->setText
+ (i18n(" 1 event selected ",
+ " %n events selected ", eventsSelected));
+ } else {
+ m_selectionCounter->setText(i18n(" No selection "));
+ }
+ m_selectionCounter->update();
+
+ setMenuStates();
+
+ if (redrawNow)
+ updateView();
+ else
+ update();
+
+ NOTATION_DEBUG << "XXX " << endl;
+}
+
+void NotationView::setSingleSelectedEvent(int staffNo, Event *event,
+ bool preview, bool redrawNow)
+{
+ setSingleSelectedEvent(getStaff(staffNo)->getSegment(), event,
+ preview, redrawNow);
+}
+
+void NotationView::setSingleSelectedEvent(Segment &segment, Event *event,
+ bool preview, bool redrawNow)
+{
+ EventSelection *selection = new EventSelection(segment);
+ selection->addEvent(event);
+ setCurrentSelection(selection, preview, redrawNow);
+}
+
+bool NotationView::canPreviewAnotherNote()
+{
+ static time_t lastCutOff = 0;
+ static int sinceLastCutOff = 0;
+
+ time_t now = time(0);
+ ++sinceLastCutOff;
+
+ if ((now - lastCutOff) > 0) {
+ sinceLastCutOff = 0;
+ lastCutOff = now;
+ NOTATION_DEBUG << "NotationView::canPreviewAnotherNote: reset" << endl;
+ } else {
+ if (sinceLastCutOff >= 20) {
+ // don't permit more than 20 notes per second or so, to
+ // avoid gungeing up the sound drivers
+ NOTATION_DEBUG << "Rejecting preview (too busy)" << endl;
+ return false;
+ }
+ NOTATION_DEBUG << "NotationView::canPreviewAnotherNote: ok" << endl;
+ }
+
+ return true;
+}
+
+void NotationView::playNote(Segment &s, int pitch, int velocity)
+{
+ Composition &comp = getDocument()->getComposition();
+ Studio &studio = getDocument()->getStudio();
+ Track *track = comp.getTrackById(s.getTrack());
+
+ Instrument *ins =
+ studio.getInstrumentById(track->getInstrument());
+
+ // check for null instrument
+ //
+ if (ins == 0)
+ return ;
+
+ if (!canPreviewAnotherNote())
+ return ;
+
+ if (velocity < 0)
+ velocity = MidiMaxValue;
+
+ MappedEvent mE(ins->getId(),
+ MappedEvent::MidiNoteOneShot,
+ pitch + s.getTranspose(),
+ velocity,
+ RealTime::zeroTime,
+ RealTime(0, 250000000),
+ RealTime::zeroTime);
+
+ StudioControl::sendMappedEvent(mE);
+}
+
+void NotationView::showPreviewNote(int staffNo, double layoutX,
+ int pitch, int height,
+ const Note &note, bool grace,
+ int velocity)
+{
+ m_staffs[staffNo]->showPreviewNote(layoutX, height, note, grace);
+ playNote(m_staffs[staffNo]->getSegment(), pitch, velocity);
+}
+
+void NotationView::clearPreviewNote()
+{
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->clearPreviewNote();
+ }
+}
+
+void NotationView::setNotePixmapFactory(NotePixmapFactory* f)
+{
+ delete m_notePixmapFactory;
+ m_notePixmapFactory = f;
+ if (m_hlayout)
+ m_hlayout->setNotePixmapFactory(m_notePixmapFactory);
+ if (m_vlayout)
+ m_vlayout->setNotePixmapFactory(m_notePixmapFactory);
+}
+
+Segment *
+NotationView::getCurrentSegment()
+{
+ Staff *staff = getCurrentStaff();
+ return (staff ? &staff->getSegment() : 0);
+}
+
+bool
+NotationView::hasSegment(Segment *segment)
+{
+ for (unsigned int i = 0; i < m_segments.size(); ++i) {
+ if (segment == m_segments[i]) return true;
+ }
+ return false;
+}
+
+
+LinedStaff *
+NotationView::getCurrentLinedStaff()
+{
+ return getLinedStaff(m_currentStaff);
+}
+
+LinedStaff *
+NotationView::getStaffAbove()
+{
+ if (m_staffs.size() < 2) return 0;
+
+ Composition *composition =
+ m_staffs[m_currentStaff]->getSegment().getComposition();
+
+ Track *track = composition->
+ getTrackById(m_staffs[m_currentStaff]->getSegment().getTrack());
+ if (!track) return 0;
+
+ int position = track->getPosition();
+ Track *newTrack = 0;
+
+ while ((newTrack = composition->getTrackByPosition(--position))) {
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ if (m_staffs[i]->getSegment().getTrack() == newTrack->getId()) {
+ return m_staffs[i];
+ }
+ }
+ }
+
+ return 0;
+}
+
+LinedStaff *
+NotationView::getStaffBelow()
+{
+ if (m_staffs.size() < 2) return 0;
+
+ Composition *composition =
+ m_staffs[m_currentStaff]->getSegment().getComposition();
+
+ Track *track = composition->
+ getTrackById(m_staffs[m_currentStaff]->getSegment().getTrack());
+ if (!track) return 0;
+
+ int position = track->getPosition();
+ Track *newTrack = 0;
+
+ while ((newTrack = composition->getTrackByPosition(++position))) {
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ if (m_staffs[i]->getSegment().getTrack() == newTrack->getId()) {
+ return m_staffs[i];
+ }
+ }
+ }
+
+ return 0;
+}
+
+timeT
+NotationView::getInsertionTime()
+{
+ return m_insertionTime;
+}
+
+timeT
+NotationView::getInsertionTime(Clef &clef,
+ Rosegarden::Key &key)
+{
+ // This fuss is solely to recover the clef and key: we already
+ // set m_insertionTime to the right value when we first placed
+ // the insert cursor. We could get clef and key directly from
+ // the segment but the staff has a more efficient lookup
+
+ LinedStaff *staff = m_staffs[m_currentStaff];
+ double layoutX = staff->getLayoutXOfInsertCursor();
+ if (layoutX < 0) layoutX = 0;
+ Event *clefEvt = 0, *keyEvt = 0;
+ (void)staff->getElementUnderLayoutX(layoutX, clefEvt, keyEvt);
+
+ if (clefEvt) clef = Clef(*clefEvt);
+ else clef = Clef();
+
+ if (keyEvt) key = Rosegarden::Key(*keyEvt);
+ else key = Rosegarden::Key();
+
+ return m_insertionTime;
+}
+
+LinedStaff*
+NotationView::getStaffForCanvasCoords(int x, int y) const
+{
+ // (i) Do not change staff, if mouse was clicked within the current staff.
+ LinedStaff *s = m_staffs[m_currentStaff];
+ if (s->containsCanvasCoords(x, y)) {
+ LinedStaff::LinedStaffCoords coords =
+ s->getLayoutCoordsForCanvasCoords(x, y);
+
+ timeT t = m_hlayout->getTimeForX(coords.first);
+ // In order to find the correct starting and ending bar of the segment,
+ // make infinitesimal shifts (+1 and -1) towards its center.
+ timeT t0 = getDocument()->getComposition().getBarStartForTime(m_staffs[m_currentStaff]->getSegment().getStartTime()+1);
+ timeT t1 = getDocument()->getComposition().getBarEndForTime(m_staffs[m_currentStaff]->getSegment().getEndTime()-1);
+ if (t >= t0 && t < t1) {
+ return m_staffs[m_currentStaff];
+ }
+ }
+ // (ii) Find staff under cursor, if clicked outside the current staff.
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+
+ LinedStaff *s = m_staffs[i];
+
+ if (s->containsCanvasCoords(x, y)) {
+
+ LinedStaff::LinedStaffCoords coords =
+ s->getLayoutCoordsForCanvasCoords(x, y);
+
+ timeT t = m_hlayout->getTimeForX(coords.first);
+ // In order to find the correct starting and ending bar of the segment,
+ // make infinitesimal shifts (+1 and -1) towards its center.
+ timeT t0 = getDocument()->getComposition().getBarStartForTime(m_staffs[i]->getSegment().getStartTime()+1);
+ timeT t1 = getDocument()->getComposition().getBarEndForTime(m_staffs[i]->getSegment().getEndTime()-1);
+ if (t >= t0 && t < t1) {
+ return m_staffs[i];
+ }
+ }
+ }
+
+ return 0;
+}
+
+void NotationView::updateView()
+{
+ slotCheckRendered
+ (getCanvasView()->contentsX(),
+ getCanvasView()->contentsX() + getCanvasView()->visibleWidth());
+ canvas()->update();
+}
+
+void NotationView::print(bool previewOnly)
+{
+ if (m_staffs.size() == 0) {
+ KMessageBox::error(0, "Nothing to print");
+ return ;
+ }
+
+ Profiler profiler("NotationView::print");
+
+ // We need to be in multi-page mode at this point
+
+ int pageWidth = getPageWidth();
+ int pageHeight = getPageHeight();
+ int leftMargin = 0, topMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+ int maxPageCount = 1;
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ int pageCount = m_staffs[i]->getPageCount();
+ NOTATION_DEBUG << "NotationView::print(): staff " << i << " reports " << pageCount << " pages " << endl;
+ if (pageCount > maxPageCount)
+ maxPageCount = pageCount;
+ }
+
+ KPrinter printer(true, QPrinter::HighResolution);
+
+ printer.setPageSelection(KPrinter::ApplicationSide);
+ printer.setMinMax(1, maxPageCount + 1);
+
+ if (previewOnly)
+ printer.setPreviewOnly(true);
+ else if (!printer.setup((QWidget *)parent()))
+ return ;
+
+ QPaintDeviceMetrics pdm(&printer);
+ QPainter printpainter(&printer);
+
+ // Ideally we should aim to retain the aspect ratio and to move the
+ // staffs so as to be centred after scaling. But because we haven't
+ // got around to the latter, let's lose the former too and just
+ // expand to fit.
+
+ // Retain aspect ratio when scaling
+ double ratioX = (double)pdm.width() / (double)(pageWidth - leftMargin * 2),
+ ratioY = (double)pdm.height() / (double)(pageHeight - topMargin * 2);
+ double ratio = std::min(ratioX, ratioY);
+ printpainter.scale(ratio, ratio);
+
+ // printpainter.scale((double)pdm.width() / (double)(pageWidth - leftMargin*2),
+ // (double)pdm.height() / (double)(pageHeight - topMargin*2));
+ printpainter.translate( -leftMargin, -topMargin);
+
+ QValueList<int> pages = printer.pageList();
+
+ for (QValueList<int>::Iterator pli = pages.begin();
+ pli != pages.end(); ) { // incremented just below
+
+ int page = *pli - 1;
+ ++pli;
+ if (page < 0 || page >= maxPageCount)
+ continue;
+
+ NOTATION_DEBUG << "Printing page " << page << endl;
+
+ QRect pageRect(m_leftGutter + leftMargin + pageWidth * page,
+ topMargin,
+ pageWidth - leftMargin,
+ pageHeight - topMargin);
+
+ for (size_t i = 0; i < m_staffs.size(); ++i) {
+
+ LinedStaff *staff = m_staffs[i];
+
+ LinedStaff::LinedStaffCoords cc0 = staff->getLayoutCoordsForCanvasCoords
+ (pageRect.x(), pageRect.y());
+
+ LinedStaff::LinedStaffCoords cc1 = staff->getLayoutCoordsForCanvasCoords
+ (pageRect.x() + pageRect.width(), pageRect.y() + pageRect.height());
+
+ timeT t0 = m_hlayout->getTimeForX(cc0.first);
+ timeT t1 = m_hlayout->getTimeForX(cc1.first);
+
+ m_staffs[i]->setPrintPainter(&printpainter);
+ m_staffs[i]->checkRendered(t0, t1);
+ }
+
+ // Supplying doublebuffer==true to this method appears to
+ // slow down printing considerably but without it we get
+ // all sorts of horrible artifacts (possibly related to
+ // mishandling of pixmap masks?) in qt-3.0. Let's permit
+ // it as a "hidden" option.
+
+ m_config->setGroup(NotationViewConfigGroup);
+
+ NOTATION_DEBUG << "NotationView::print: calling QCanvas::drawArea" << endl;
+
+ {
+ Profiler profiler("NotationView::print(QCanvas::drawArea)");
+
+ if (m_config->readBoolEntry("forcedoublebufferprinting", false)) {
+ getCanvasView()->canvas()->drawArea(pageRect, &printpainter, true);
+ } else {
+#if QT_VERSION >= 0x030100
+ getCanvasView()->canvas()->drawArea(pageRect, &printpainter, false);
+#else
+
+ getCanvasView()->canvas()->drawArea(pageRect, &printpainter, true);
+#endif
+
+ }
+
+ }
+
+ NOTATION_DEBUG << "NotationView::print: QCanvas::drawArea done" << endl;
+
+ for (size_t i = 0; i < m_staffs.size(); ++i) {
+
+ LinedStaff *staff = m_staffs[i];
+
+ LinedStaff::LinedStaffCoords cc0 = staff->getLayoutCoordsForCanvasCoords
+ (pageRect.x(), pageRect.y());
+
+ LinedStaff::LinedStaffCoords cc1 = staff->getLayoutCoordsForCanvasCoords
+ (pageRect.x() + pageRect.width(), pageRect.y() + pageRect.height());
+
+ timeT t0 = m_hlayout->getTimeForX(cc0.first);
+ timeT t1 = m_hlayout->getTimeForX(cc1.first);
+
+ m_staffs[i]->renderPrintable(t0, t1);
+ }
+
+ printpainter.translate( -pageWidth, 0);
+
+ if (pli != pages.end() && *pli - 1 < maxPageCount)
+ printer.newPage();
+
+ for (size_t i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->markChanged(); // recover any memory used for this page
+ PixmapArrayGC::deleteAll();
+ }
+ }
+
+ for (size_t i = 0; i < m_staffs.size(); ++i) {
+ for (Segment::iterator j = m_staffs[i]->getSegment().begin();
+ j != m_staffs[i]->getSegment().end(); ++j) {
+ removeViewLocalProperties(*j);
+ }
+ delete m_staffs[i];
+ }
+ m_staffs.clear();
+
+ printpainter.end();
+
+ Profiles::getInstance()->dump();
+}
+
+void
+NotationView::updateThumbnails(bool complete)
+{
+ if (m_pageMode != LinedStaff::MultiPageMode)
+ return ;
+
+ int pageWidth = getPageWidth();
+ int pageHeight = getPageHeight();
+ int leftMargin = 0, topMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+ int maxPageCount = 1;
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ int pageCount = m_staffs[i]->getPageCount();
+ if (pageCount > maxPageCount)
+ maxPageCount = pageCount;
+ }
+
+ int thumbScale = 20;
+ QPixmap thumbnail(canvas()->width() / thumbScale,
+ canvas()->height() / thumbScale);
+ thumbnail.fill(Qt::white);
+ QPainter thumbPainter(&thumbnail);
+
+ if (complete) {
+
+ thumbPainter.scale(1.0 / double(thumbScale), 1.0 / double(thumbScale));
+ thumbPainter.setPen(Qt::black);
+ thumbPainter.setBrush(Qt::white);
+
+ /*
+ QCanvas *canvas = getCanvasView()->canvas();
+ canvas->drawArea(QRect(0, 0, canvas->width(), canvas->height()),
+ &thumbPainter, false);
+ */
+ // hide small texts, as we get a crash in Xft when trying to
+ // render them at this scale
+ if (m_title)
+ m_title->hide();
+ if (m_subtitle)
+ m_subtitle->hide();
+ if (m_composer)
+ m_composer->hide();
+ if (m_copyright)
+ m_copyright->hide();
+
+ for (size_t page = 0; page < static_cast<size_t>(maxPageCount); ++page) {
+
+ bool havePageNumber = ((m_pageNumbers.size() > page) &&
+ (m_pageNumbers[page] != 0));
+ if (havePageNumber)
+ m_pageNumbers[page]->hide();
+
+ QRect pageRect(m_leftGutter + leftMargin * 2 + pageWidth * page,
+ topMargin * 2,
+ pageWidth - leftMargin*3,
+ pageHeight - topMargin*3);
+
+ QCanvas *canvas = getCanvasView()->canvas();
+ canvas->drawArea(pageRect, &thumbPainter, false);
+
+ if (havePageNumber)
+ m_pageNumbers[page]->show();
+ }
+
+ if (m_title)
+ m_title->show();
+ if (m_subtitle)
+ m_subtitle->show();
+ if (m_composer)
+ m_composer->show();
+ if (m_copyright)
+ m_copyright->show();
+
+ } else {
+
+ thumbPainter.setPen(Qt::black);
+
+ for (int page = 0; page < maxPageCount; ++page) {
+
+ int x = m_leftGutter + pageWidth * page + leftMargin / 4;
+ int y = 20;
+ int w = pageWidth - leftMargin / 2;
+ int h = pageHeight;
+
+ QString str = QString("%1").arg(page + 1);
+
+ thumbPainter.drawRect(x / thumbScale, y / thumbScale,
+ w / thumbScale, h / thumbScale);
+
+ int tx = (x + w / 2) / thumbScale, ty = (y + h / 2) / thumbScale;
+ tx -= thumbPainter.fontMetrics().width(str) / 2;
+ thumbPainter.drawText(tx, ty, str);
+ }
+ }
+
+ thumbPainter.end();
+ if (m_pannerDialog)
+ m_pannerDialog->scrollbox()->setThumbnail(thumbnail);
+}
+
+void NotationView::refreshSegment(Segment *segment,
+ timeT startTime, timeT endTime)
+{
+ NOTATION_DEBUG << "*** " << endl;
+
+ if (m_inhibitRefresh)
+ return ;
+ NOTATION_DEBUG << "NotationView::refreshSegment(" << segment << "," << startTime << "," << endTime << ")" << endl;
+ Profiler foo("NotationView::refreshSegment");
+
+ emit usedSelection();
+
+ if (segment) {
+ LinedStaff *staff = getLinedStaff(*segment);
+ if (staff)
+ applyLayout(staff->getId(), startTime, endTime);
+ } else {
+ applyLayout( -1, startTime, endTime);
+ }
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+
+ Segment *ssegment = &m_staffs[i]->getSegment();
+ bool thisStaff = (ssegment == segment || segment == 0);
+ m_staffs[i]->markChanged(startTime, endTime, !thisStaff);
+ }
+
+ PixmapArrayGC::deleteAll();
+
+ statusBar()->changeItem(KTmpStatusMsg::getDefaultMsg(),
+ KTmpStatusMsg::getDefaultId());
+
+ Event::dumpStats(std::cerr);
+ if (m_deferredCursorMove == NoCursorMoveNeeded) {
+ slotSetInsertCursorPosition(getInsertionTime(), false, false);
+ } else {
+ doDeferredCursorMove();
+ }
+ slotSetPointerPosition(getDocument()->getComposition().getPosition(), false);
+
+ if (m_currentEventSelection &&
+ m_currentEventSelection->getSegmentEvents().size() == 0) {
+ delete m_currentEventSelection;
+ m_currentEventSelection = 0;
+ //!!!??? was that the right thing to do?
+ }
+
+ setMenuStates();
+ slotSetOperationNameAndStatus(i18n(" Ready."));
+ NOTATION_DEBUG << "*** " << endl;
+}
+
+void NotationView::setMenuStates()
+{
+ // 1. set selection-related states
+
+ // Clear states first, then enter only those ones that apply
+ // (so as to avoid ever clearing one after entering another, in
+ // case the two overlap at all)
+ stateChanged("have_selection", KXMLGUIClient::StateReverse);
+ stateChanged("have_notes_in_selection", KXMLGUIClient::StateReverse);
+ stateChanged("have_rests_in_selection", KXMLGUIClient::StateReverse);
+
+ if (m_currentEventSelection) {
+
+ NOTATION_DEBUG << "NotationView::setMenuStates: Have selection; it's " << m_currentEventSelection << " covering range from " << m_currentEventSelection->getStartTime() << " to " << m_currentEventSelection->getEndTime() << " (" << m_currentEventSelection->getSegmentEvents().size() << " events)" << endl;
+
+ stateChanged("have_selection", KXMLGUIClient::StateNoReverse);
+ if (m_currentEventSelection->contains
+ (Note::EventType)) {
+ stateChanged("have_notes_in_selection",
+ KXMLGUIClient::StateNoReverse);
+ }
+ if (m_currentEventSelection->contains
+ (Note::EventRestType)) {
+ stateChanged("have_rests_in_selection",
+ KXMLGUIClient::StateNoReverse);
+ }
+ }
+
+ // 2. set inserter-related states
+
+ // #1372863 -- RestInserter is a subclass of NoteInserter, so we
+ // need to test dynamic_cast<RestInserter *> before
+ // dynamic_cast<NoteInserter *> (which will succeed for both)
+
+ if (dynamic_cast<RestInserter *>(m_tool)) {
+ NOTATION_DEBUG << "Have rest inserter " << endl;
+ stateChanged("note_insert_tool_current", StateReverse);
+ stateChanged("rest_insert_tool_current", StateNoReverse);
+ } else if (dynamic_cast<NoteInserter *>(m_tool)) {
+ NOTATION_DEBUG << "Have note inserter " << endl;
+ stateChanged("note_insert_tool_current", StateNoReverse);
+ stateChanged("rest_insert_tool_current", StateReverse);
+ } else {
+ NOTATION_DEBUG << "Have neither inserter " << endl;
+ stateChanged("note_insert_tool_current", StateReverse);
+ stateChanged("rest_insert_tool_current", StateReverse);
+ }
+}
+
+#define UPDATE_PROGRESS(n) \
+ progressCount += (n); \
+ if (progressTotal > 0) { \
+ emit setProgress(progressCount * 100 / progressTotal); \
+ ProgressDialog::processEvents(); \
+ }
+
+void NotationView::readjustCanvasSize()
+{
+ Profiler profiler("NotationView::readjustCanvasSize");
+
+ double maxWidth = 0.0;
+ int maxHeight = 0;
+
+ slotSetOperationNameAndStatus(i18n("Sizing and allocating canvas..."));
+ ProgressDialog::processEvents();
+
+ int progressTotal = m_staffs.size() + 2;
+ int progressCount = 0;
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+
+ LinedStaff &staff = *m_staffs[i];
+
+ staff.sizeStaff(*m_hlayout);
+ UPDATE_PROGRESS(1);
+
+ if (staff.getTotalWidth() + staff.getX() > maxWidth) {
+ maxWidth = staff.getTotalWidth() + staff.getX() + 1;
+ }
+
+ if (staff.getTotalHeight() + staff.getY() > maxHeight) {
+ maxHeight = staff.getTotalHeight() + staff.getY() + 1;
+ }
+ }
+
+ int topMargin = 0, leftMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+
+ int pageWidth = getPageWidth();
+ int pageHeight = getPageHeight();
+
+ NOTATION_DEBUG << "NotationView::readjustCanvasSize: maxHeight is "
+ << maxHeight << ", page height is " << pageHeight << endl
+ << " - maxWidth is " << maxWidth << ", page width is " << pageWidth << endl;
+
+
+ if (m_pageMode == LinedStaff::LinearMode) {
+ maxWidth = ((maxWidth / pageWidth) + 1) * pageWidth;
+ if (maxHeight < pageHeight)
+ maxHeight = pageHeight;
+ } else {
+ if (maxWidth < pageWidth)
+ maxWidth = pageWidth;
+ if (maxHeight < pageHeight + topMargin*2)
+ maxHeight = pageHeight + topMargin * 2;
+ }
+
+ // now get the EditView to do the biz
+ readjustViewSize(QSize(int(maxWidth), maxHeight), true);
+
+ UPDATE_PROGRESS(2);
+
+ if (m_pannerDialog) {
+
+ if (m_pageMode != LinedStaff::MultiPageMode) {
+ m_pannerDialog->hide();
+
+ } else {
+
+ m_pannerDialog->show();
+
+ m_pannerDialog->setPageSize
+ (QSize(canvas()->width(),
+ canvas()->height()));
+
+ m_pannerDialog->scrollbox()->setViewSize
+ (QSize(getCanvasView()->width(),
+ getCanvasView()->height()));
+ }
+ }
+
+ // Give a correct vertical alignment to track headers
+ if ((m_pageMode == LinedStaff::LinearMode) && m_showHeadersGroup) {
+ m_headersGroupView->setContentsPos(0, getCanvasView()->contentsY());
+ }
+}
+
+void NotationView::slotNoteAction()
+{
+ const QObject* sigSender = sender();
+
+ NoteActionDataMap::Iterator noteAct =
+ m_noteActionDataMap->find(sigSender->name());
+
+ if (noteAct != m_noteActionDataMap->end()) {
+ m_lastNoteAction = sigSender->name();
+ setCurrentSelectedNote(**noteAct);
+ setMenuStates();
+ } else {
+ std::cerr << "NotationView::slotNoteAction() : couldn't find NoteActionData named '"
+ << sigSender->name() << "'\n";
+ }
+}
+
+void NotationView::slotLastNoteAction()
+{
+ KAction *action = actionCollection()->action(m_lastNoteAction);
+ if (!action)
+ action = actionCollection()->action("crotchet");
+
+ if (action) {
+ action->activate();
+ } else {
+ std::cerr << "NotationView::slotNoteAction() : couldn't find action named '"
+ << m_lastNoteAction << "' or 'crotchet'\n";
+ }
+}
+
+void NotationView::slotAddMark()
+{
+ const QObject *s = sender();
+ if (!m_currentEventSelection)
+ return ;
+
+ MarkActionDataMap::Iterator i = m_markActionDataMap->find(s->name());
+
+ if (i != m_markActionDataMap->end()) {
+ addCommandToHistory(new AddMarkCommand
+ ((**i).mark, *m_currentEventSelection));
+ }
+}
+
+void NotationView::slotNoteChangeAction()
+{
+ const QObject* sigSender = sender();
+
+ NoteChangeActionDataMap::Iterator noteAct =
+ m_noteChangeActionDataMap->find(sigSender->name());
+
+ if (noteAct != m_noteChangeActionDataMap->end()) {
+ slotSetNoteDurations((**noteAct).noteType, (**noteAct).notationOnly);
+ } else {
+ std::cerr << "NotationView::slotNoteChangeAction() : couldn't find NoteChangeAction named '"
+ << sigSender->name() << "'\n";
+ }
+}
+
+void NotationView::initActionDataMaps()
+{
+ static bool called = false;
+ static int keys[] =
+ { Key_0, Key_3, Key_6, Key_8, Key_4, Key_2, Key_1, Key_5 };
+
+ if (called)
+ return ;
+ called = true;
+
+ m_noteActionDataMap = new NoteActionDataMap;
+
+ for (int rest = 0; rest < 2; ++rest) {
+ for (int dots = 0; dots < 2; ++dots) {
+ for (int type = Note::Longest; type >= Note::Shortest; --type) {
+ if (dots && (type == Note::Longest))
+ continue;
+
+ QString refName
+ (NotationStrings::getReferenceName(Note(type, dots), rest == 1));
+
+ QString shortName(refName);
+ shortName.replace(QRegExp("-"), "_");
+
+ QString titleName
+ (NotationStrings::getNoteName(Note(type, dots)));
+
+ titleName = titleName.left(1).upper() +
+ titleName.right(titleName.length() - 1);
+
+ if (rest) {
+ titleName.replace(QRegExp(i18n("note")), i18n("rest"));
+ }
+
+ int keycode = keys[type - Note::Shortest];
+ if (dots) // keycode += CTRL; -- used below for note change action
+ keycode = 0;
+ if (rest) // keycode += SHIFT; -- can't do shift+numbers
+ keycode = 0;
+
+ m_noteActionDataMap->insert
+ (shortName, new NoteActionData
+ (titleName, shortName, refName, keycode,
+ rest > 0, type, dots));
+ }
+ }
+ }
+
+ m_noteChangeActionDataMap = new NoteChangeActionDataMap;
+
+ for (int notationOnly = 0; notationOnly <= 1; ++notationOnly) {
+ for (int type = Note::Longest; type >= Note::Shortest; --type) {
+
+ QString refName
+ (NotationStrings::getReferenceName(Note(type, 0), false));
+
+ QString shortName(QString("change_%1%2")
+ .arg(notationOnly ? "notation_" : "").arg(refName));
+ shortName.replace(QRegExp("-"), "_");
+
+ QString titleName
+ (NotationStrings::getNoteName(Note(type, 0)));
+
+ titleName = titleName.left(1).upper() +
+ titleName.right(titleName.length() - 1);
+
+ int keycode = keys[type - Note::Shortest];
+ keycode += CTRL;
+ if (notationOnly)
+ keycode += ALT;
+
+ m_noteChangeActionDataMap->insert
+ (shortName, new NoteChangeActionData
+ (titleName, shortName, refName, keycode,
+ notationOnly ? true : false, type));
+ }
+ }
+
+ m_markActionDataMap = new MarkActionDataMap;
+
+ std::vector<Mark> marks = Marks::getStandardMarks();
+ for (unsigned int i = 0; i < marks.size(); ++i) {
+
+ Mark mark = marks[i];
+ QString markName(strtoqstr(mark));
+ QString actionName = QString("add_%1").arg(markName);
+
+ m_markActionDataMap->insert
+ (actionName, new MarkActionData
+ (AddMarkCommand::getGlobalName(mark),
+ actionName, 0, mark));
+ }
+
+}
+
+void NotationView::setupProgress(KProgress* bar)
+{
+ if (bar) {
+ NOTATION_DEBUG << "NotationView::setupProgress(bar)\n";
+
+ connect(m_hlayout, SIGNAL(setProgress(int)),
+ bar, SLOT(setValue(int)));
+
+ connect(m_hlayout, SIGNAL(incrementProgress(int)),
+ bar, SLOT(advance(int)));
+
+ connect(this, SIGNAL(setProgress(int)),
+ bar, SLOT(setValue(int)));
+
+ connect(this, SIGNAL(incrementProgress(int)),
+ bar, SLOT(advance(int)));
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ connect(m_staffs[i], SIGNAL(setProgress(int)),
+ bar, SLOT(setValue(int)));
+
+ connect(m_staffs[i], SIGNAL(incrementProgress(int)),
+ bar, SLOT(advance(int)));
+ }
+ }
+
+}
+
+void NotationView::setupProgress(ProgressDialog* dialog)
+{
+ NOTATION_DEBUG << "NotationView::setupProgress(dialog)" << endl;
+ disconnectProgress();
+
+ if (dialog) {
+ setupProgress(dialog->progressBar());
+
+ connect(dialog, SIGNAL(cancelClicked()),
+ m_hlayout, SLOT(slotCancel()));
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ connect(m_staffs[i], SIGNAL(setOperationName(QString)),
+ this, SLOT(slotSetOperationNameAndStatus(QString)));
+
+ connect(dialog, SIGNAL(cancelClicked()),
+ m_staffs[i], SLOT(slotCancel()));
+ }
+
+ connect(this, SIGNAL(setOperationName(QString)),
+ dialog, SLOT(slotSetOperationName(QString)));
+ m_progressDisplayer = PROGRESS_DIALOG;
+ }
+
+}
+
+void NotationView::slotSetOperationNameAndStatus(QString name)
+{
+ emit setOperationName(name);
+ statusBar()->changeItem(QString(" %1").arg(name),
+ KTmpStatusMsg::getDefaultId());
+}
+
+void NotationView::disconnectProgress()
+{
+ NOTATION_DEBUG << "NotationView::disconnectProgress()" << endl;
+
+ m_hlayout->disconnect();
+ disconnect(SIGNAL(setProgress(int)));
+ disconnect(SIGNAL(incrementProgress(int)));
+ disconnect(SIGNAL(setOperationName(QString)));
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->disconnect();
+ }
+}
+
+void NotationView::setupDefaultProgress()
+{
+ if (m_progressDisplayer != PROGRESS_BAR) {
+ NOTATION_DEBUG << "NotationView::setupDefaultProgress()" << endl;
+ disconnectProgress();
+ setupProgress(m_progressBar);
+ m_progressDisplayer = PROGRESS_BAR;
+ }
+}
+
+void NotationView::updateViewCaption()
+{
+ if (m_segments.size() == 1) {
+
+ TrackId trackId = m_segments[0]->getTrack();
+ Track *track =
+ m_segments[0]->getComposition()->getTrackById(trackId);
+
+ int trackPosition = -1;
+ if (track)
+ trackPosition = track->getPosition();
+ // std::cout << std::endl << std::endl << std::endl << "DEBUG TITLE BAR: " << getDocument()->getTitle() << std::endl << std::endl << std::endl;
+ setCaption(i18n("%1 - Segment Track #%2 - Notation")
+ .arg(getDocument()->getTitle())
+ .arg(trackPosition + 1));
+
+ } else if (m_segments.size() == getDocument()->getComposition().getNbSegments()) {
+
+ setCaption(i18n("%1 - All Segments - Notation")
+ .arg(getDocument()->getTitle()));
+
+ } else {
+
+ setCaption(i18n("%1 - Segment - Notation", "%1 - %n Segments - Notation", m_segments.size())
+ .arg(getDocument()->getTitle()));
+
+ }
+}
+
+NotationView::NoteActionDataMap* NotationView::m_noteActionDataMap = 0;
+
+NotationView::NoteChangeActionDataMap* NotationView::m_noteChangeActionDataMap = 0;
+
+NotationView::MarkActionDataMap* NotationView::m_markActionDataMap = 0;
+
+
+/// SLOTS
+
+
+void
+NotationView::slotUpdateInsertModeStatus()
+{
+ QString tripletMessage = i18n("Triplet");
+ QString chordMessage = i18n("Chord");
+ QString graceMessage = i18n("Grace");
+ QString message;
+
+ if (isInTripletMode()) {
+ message = i18n("%1 %2").arg(message).arg(tripletMessage);
+ }
+
+ if (isInChordMode()) {
+ message = i18n("%1 %2").arg(message).arg(chordMessage);
+ }
+
+ if (isInGraceMode()) {
+ message = i18n("%1 %2").arg(message).arg(graceMessage);
+ }
+
+ m_insertModeLabel->setText(message);
+}
+
+void
+NotationView::slotUpdateAnnotationsStatus()
+{
+ if (!areAnnotationsVisible()) {
+ for (int i = 0; i < getStaffCount(); ++i) {
+ Segment &s = getStaff(i)->getSegment();
+ for (Segment::iterator j = s.begin(); j != s.end(); ++j) {
+ if ((*j)->isa(Text::EventType) &&
+ ((*j)->get<String>(Text::TextTypePropertyName)
+ == Text::Annotation)) {
+ m_annotationsLabel->setText(i18n("Hidden annotations"));
+ return ;
+ }
+ }
+ }
+ }
+ m_annotationsLabel->setText("");
+ getToggleAction("show_annotations")->setChecked(areAnnotationsVisible());
+}
+
+void
+NotationView::slotUpdateLilyPondDirectivesStatus()
+{
+ if (!areLilyPondDirectivesVisible()) {
+ for (int i = 0; i < getStaffCount(); ++i) {
+ Segment &s = getStaff(i)->getSegment();
+ for (Segment::iterator j = s.begin(); j != s.end(); ++j) {
+ if ((*j)->isa(Text::EventType) &&
+ ((*j)->get
+ <String>
+ (Text::TextTypePropertyName)
+ == Text::LilyPondDirective)) {
+ m_lilyPondDirectivesLabel->setText(i18n("Hidden LilyPond directives"));
+ return ;
+ }
+ }
+ }
+ }
+ m_lilyPondDirectivesLabel->setText("");
+ getToggleAction("show_lilypond_directives")->setChecked(areLilyPondDirectivesVisible());
+}
+
+void
+NotationView::slotChangeSpacingFromStringValue(const QString& spacingT)
+{
+ // spacingT has a '%' at the end
+ //
+ int spacing = spacingT.left(spacingT.length() - 1).toInt();
+ slotChangeSpacing(spacing);
+}
+
+void
+NotationView::slotChangeSpacingFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ if (name.left(8) == "spacing_") {
+ int spacing = name.right(name.length() - 8).toInt();
+
+ if (spacing > 0)
+ slotChangeSpacing(spacing);
+
+ } else {
+ KMessageBox::sorry
+ (this, i18n("Unknown spacing action %1").arg(name));
+ }
+}
+
+void
+NotationView::slotChangeSpacing(int spacing)
+{
+ if (m_hlayout->getSpacing() == spacing)
+ return ;
+
+ m_hlayout->setSpacing(spacing);
+
+ // m_spacingSlider->setSize(spacing);
+
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action(QString("spacing_%1").arg(spacing)));
+ if (action)
+ action->setChecked(true);
+ else {
+ std::cerr
+ << "WARNING: Expected action \"spacing_" << spacing
+ << "\" to be a KToggleAction, but it isn't (or doesn't exist)"
+ << std::endl;
+ }
+
+ positionStaffs();
+ applyLayout();
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->markChanged();
+ }
+
+ positionPages();
+ updateControlRulers(true);
+ updateView();
+}
+
+void
+NotationView::slotChangeProportionFromIndex(int n)
+{
+ std::vector<int> proportions = m_hlayout->getAvailableProportions();
+ if (n >= (int)proportions.size())
+ n = proportions.size() - 1;
+ slotChangeProportion(proportions[n]);
+}
+
+void
+NotationView::slotChangeProportionFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ if (name.left(11) == "proportion_") {
+ int proportion = name.right(name.length() - 11).toInt();
+ slotChangeProportion(proportion);
+
+ } else {
+ KMessageBox::sorry
+ (this, i18n("Unknown proportion action %1").arg(name));
+ }
+}
+
+void
+NotationView::slotChangeProportion(int proportion)
+{
+ if (m_hlayout->getProportion() == proportion)
+ return ;
+
+ m_hlayout->setProportion(proportion);
+
+ // m_proportionSlider->setSize(proportion);
+
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action(QString("proportion_%1").arg(proportion)));
+ if (action)
+ action->setChecked(true);
+ else {
+ std::cerr
+ << "WARNING: Expected action \"proportion_" << proportion
+ << "\" to be a KToggleAction, but it isn't (or doesn't exist)"
+ << std::endl;
+ }
+
+ positionStaffs();
+ applyLayout();
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->markChanged();
+ }
+
+ positionPages();
+ updateControlRulers(true);
+ updateView();
+}
+
+void
+NotationView::slotChangeFontFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+ if (name.left(10) == "note_font_") {
+ name = name.right(name.length() - 10);
+ slotChangeFont(name);
+ } else {
+ KMessageBox::sorry
+ (this, i18n("Unknown font action %1").arg(name));
+ }
+}
+
+void
+NotationView::slotChangeFontSizeFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ if (name.left(15) == "note_font_size_") {
+ name = name.right(name.length() - 15);
+ bool ok = false;
+ int size = name.toInt(&ok);
+ if (ok)
+ slotChangeFont(m_fontName, size);
+ else {
+ KMessageBox::sorry
+ (this, i18n("Unknown font size %1").arg(name));
+ }
+ } else {
+ KMessageBox::sorry
+ (this, i18n("Unknown font size action %1").arg(name));
+ }
+}
+
+void
+NotationView::slotChangeFont(const QString &newName)
+{
+ NOTATION_DEBUG << "changeFont: " << newName << endl;
+ slotChangeFont(std::string(newName.utf8()));
+}
+
+void
+NotationView::slotChangeFont(std::string newName)
+{
+ int newSize = m_fontSize;
+
+ if (!NoteFontFactory::isAvailableInSize(newName, newSize)) {
+
+ int defaultSize = NoteFontFactory::getDefaultSize(newName);
+ newSize = m_config->readUnsignedNumEntry
+ ((getStaffCount() > 1 ?
+ "multistaffnotesize" : "singlestaffnotesize"), defaultSize);
+
+ if (!NoteFontFactory::isAvailableInSize(newName, newSize)) {
+ newSize = defaultSize;
+ }
+ }
+
+ slotChangeFont(newName, newSize);
+}
+
+void
+NotationView::slotChangeFontSize(int newSize)
+{
+ slotChangeFont(m_fontName, newSize);
+}
+
+void
+NotationView::slotChangeFontSizeFromStringValue(const QString& sizeT)
+{
+ int size = sizeT.toInt();
+ slotChangeFont(m_fontName, size);
+}
+
+void
+NotationView::slotZoomIn()
+{
+ std::vector<int> sizes = NoteFontFactory::getScreenSizes(m_fontName);
+ for (int i = 0; i + 1 < sizes.size(); ++i) {
+ if (sizes[i] == m_fontSize) {
+ slotChangeFontSize(sizes[i + 1]);
+ return ;
+ }
+ }
+}
+
+void
+NotationView::slotZoomOut()
+{
+ std::vector<int> sizes = NoteFontFactory::getScreenSizes(m_fontName);
+ for (int i = 1; i < sizes.size(); ++i) {
+ if (sizes[i] == m_fontSize) {
+ slotChangeFontSize(sizes[i - 1]);
+ return ;
+ }
+ }
+}
+
+void
+NotationView::slotChangeFont(std::string newName, int newSize)
+{
+ if (newName == m_fontName && newSize == m_fontSize)
+ return ;
+
+ NotePixmapFactory* npf = 0;
+
+ try {
+ npf = new NotePixmapFactory(newName, newSize);
+ } catch (...) {
+ return ;
+ }
+
+ bool changedFont = (newName != m_fontName || newSize != m_fontSize);
+
+ std::string oldName = m_fontName;
+ m_fontName = newName;
+ m_fontSize = newSize;
+ setNotePixmapFactory(npf);
+
+ // update the various GUI elements
+
+ std::set<std::string> fs(NoteFontFactory::getFontNames());
+ std::vector<std::string> f(fs.begin(), fs.end());
+ std::sort(f.begin(), f.end());
+
+ for (unsigned int i = 0; i < f.size(); ++i) {
+ bool thisOne = (f[i] == m_fontName);
+ if (thisOne)
+ m_fontCombo->setCurrentItem(i);
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("note_font_" + strtoqstr(f[i])));
+ NOTATION_DEBUG << "inspecting " << f[i] << (action ? ", have action" : ", no action") << endl;
+ if (action)
+ action->setChecked(thisOne);
+ else {
+ std::cerr
+ << "WARNING: Expected action \"note_font_" << f[i]
+ << "\" to be a KToggleAction, but it isn't (or doesn't exist)"
+ << std::endl;
+ }
+ }
+
+ NOTATION_DEBUG << "about to reinitialise sizes" << endl;
+
+ std::vector<int> sizes = NoteFontFactory::getScreenSizes(m_fontName);
+ m_fontSizeCombo->clear();
+ QString value;
+ for (std::vector<int>::iterator i = sizes.begin(); i != sizes.end(); ++i) {
+ value.setNum(*i);
+ m_fontSizeCombo->insertItem(value);
+ }
+ value.setNum(m_fontSize);
+ m_fontSizeCombo->setCurrentText(value);
+
+ setupFontSizeMenu(oldName);
+
+ if (!changedFont)
+ return ; // might have been called to initialise menus etc
+
+ NOTATION_DEBUG << "about to change font" << endl;
+
+ if (m_pageMode == LinedStaff::MultiPageMode) {
+
+ int pageWidth = getPageWidth();
+ int topMargin = 0, leftMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+
+ m_hlayout->setPageWidth(pageWidth - leftMargin * 2);
+ }
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->changeFont(m_fontName, m_fontSize);
+ }
+
+ NOTATION_DEBUG << "about to position staffs" << endl;
+
+ positionStaffs();
+
+ bool layoutApplied = applyLayout();
+ if (!layoutApplied)
+ KMessageBox::sorry(0, "Couldn't apply layout");
+ else {
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->markChanged();
+ }
+ }
+
+ positionPages();
+ updateControlRulers(true);
+ updateView();
+}
+
+void
+NotationView::slotFilePrint()
+{
+ KTmpStatusMsg msg(i18n("Printing..."), this);
+
+ SetWaitCursor waitCursor;
+ NotationView printingView(getDocument(), m_segments,
+ (QWidget *)parent(), this);
+
+ if (!printingView.isOK()) {
+ NOTATION_DEBUG << "Print : operation cancelled\n";
+ return ;
+ }
+
+ printingView.print();
+}
+
+void
+NotationView::slotFilePrintPreview()
+{
+ KTmpStatusMsg msg(i18n("Previewing..."), this);
+
+ SetWaitCursor waitCursor;
+ NotationView printingView(getDocument(), m_segments,
+ (QWidget *)parent(), this);
+
+ if (!printingView.isOK()) {
+ NOTATION_DEBUG << "Print preview : operation cancelled\n";
+ return ;
+ }
+
+ printingView.print(true);
+}
+
+std::map<KProcess *, KTempFile *> NotationView::m_lilyTempFileMap;
+
+void NotationView::slotPrintLilyPond()
+{
+ KTmpStatusMsg msg(i18n("Printing LilyPond file..."), this);
+ KTempFile *file = new KTempFile(QString::null, ".ly");
+ file->setAutoDelete(true);
+ if (!file->name()) {
+ // CurrentProgressDialog::freeze();
+ KMessageBox::sorry(this, i18n("Failed to open a temporary file for LilyPond export."));
+ delete file;
+ }
+ if (!exportLilyPondFile(file->name(), true)) {
+ return ;
+ }
+ KProcess *proc = new KProcess;
+ *proc << "rosegarden-lilypondview";
+ *proc << "--graphical";
+ *proc << "--print";
+ *proc << file->name();
+ connect(proc, SIGNAL(processExited(KProcess *)),
+ this, SLOT(slotLilyPondViewProcessExited(KProcess *)));
+ m_lilyTempFileMap[proc] = file;
+ proc->start(KProcess::NotifyOnExit);
+}
+
+void NotationView::slotPreviewLilyPond()
+{
+ KTmpStatusMsg msg(i18n("Previewing LilyPond file..."), this);
+ KTempFile *file = new KTempFile(QString::null, ".ly");
+ file->setAutoDelete(true);
+ if (!file->name()) {
+ // CurrentProgressDialog::freeze();
+ KMessageBox::sorry(this, i18n("Failed to open a temporary file for LilyPond export."));
+ delete file;
+ }
+ if (!exportLilyPondFile(file->name(), true)) {
+ return ;
+ }
+ KProcess *proc = new KProcess;
+ *proc << "rosegarden-lilypondview";
+ *proc << "--graphical";
+ *proc << "--pdf";
+ *proc << file->name();
+ connect(proc, SIGNAL(processExited(KProcess *)),
+ this, SLOT(slotLilyPondViewProcessExited(KProcess *)));
+ m_lilyTempFileMap[proc] = file;
+ proc->start(KProcess::NotifyOnExit);
+}
+
+void NotationView::slotLilyPondViewProcessExited(KProcess *p)
+{
+ delete m_lilyTempFileMap[p];
+ m_lilyTempFileMap.erase(p);
+ delete p;
+}
+
+bool NotationView::exportLilyPondFile(QString file, bool forPreview)
+{
+ QString caption = "", heading = "";
+ if (forPreview) {
+ caption = i18n("LilyPond Preview Options");
+ heading = i18n("LilyPond preview options");
+ }
+
+ LilyPondOptionsDialog dialog(this, m_doc, caption, heading);
+ if (dialog.exec() != QDialog::Accepted) {
+ return false;
+ }
+
+ ProgressDialog progressDlg(i18n("Exporting LilyPond file..."),
+ 100,
+ this);
+
+ LilyPondExporter e(this, m_doc, std::string(QFile::encodeName(file)));
+
+ connect(&e, SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+
+ connect(&e, SIGNAL(incrementProgress(int)),
+ progressDlg.progressBar(), SLOT(advance(int)));
+
+ if (!e.write()) {
+ // CurrentProgressDialog::freeze();
+ KMessageBox::sorry(this, i18n("Export failed. The file could not be opened for writing."));
+ return false;
+ }
+
+ return true;
+}
+
+void NotationView::slotEditCut()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Cutting selection to clipboard..."), this);
+
+ addCommandToHistory(new CutCommand(*m_currentEventSelection,
+ getDocument()->getClipboard()));
+}
+
+void NotationView::slotEditDelete()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Deleting selection..."), this);
+
+ addCommandToHistory(new EraseCommand(*m_currentEventSelection));
+}
+
+void NotationView::slotEditCopy()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Copying selection to clipboard..."), this);
+
+ addCommandToHistory(new CopyCommand(*m_currentEventSelection,
+ getDocument()->getClipboard()));
+}
+
+void NotationView::slotEditCutAndClose()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Cutting selection to clipboard..."), this);
+
+ addCommandToHistory(new CutAndCloseCommand(*m_currentEventSelection,
+ getDocument()->getClipboard()));
+}
+
+static const QString RESTRICTED_PASTE_FAILED_DESCRIPTION = i18n(
+ "The Restricted paste type requires enough empty " \
+ "space (containing only rests) at the paste position " \
+ "to hold all of the events to be pasted.\n" \
+ "Not enough space was found.\n" \
+ "If you want to paste anyway, consider using one of " \
+ "the other paste types from the \"Paste...\" option " \
+ "on the Edit menu. You can also change the default " \
+ "paste type to something other than Restricted if " \
+ "you wish."
+ );
+
+void NotationView::slotEditPaste()
+{
+ Clipboard * clipboard = getDocument()->getClipboard();
+
+ if (clipboard->isEmpty()) {
+ slotStatusHelpMsg(i18n("Clipboard is empty"));
+ return ;
+ }
+ if (!clipboard->isSingleSegment()) {
+ slotStatusHelpMsg(i18n("Can't paste multiple Segments into one"));
+ return ;
+ }
+
+ slotStatusHelpMsg(i18n("Inserting clipboard contents..."));
+
+ LinedStaff *staff = getCurrentLinedStaff();
+ Segment &segment = staff->getSegment();
+
+ // Paste at cursor position
+ //
+ timeT insertionTime = getInsertionTime();
+ timeT endTime = insertionTime +
+ (clipboard->getSingleSegment()->getEndTime() -
+ clipboard->getSingleSegment()->getStartTime());
+
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+ PasteEventsCommand::PasteType defaultType = (PasteEventsCommand::PasteType)
+ config->readUnsignedNumEntry("pastetype",
+ PasteEventsCommand::Restricted);
+
+ PasteEventsCommand *command = new PasteEventsCommand
+ (segment, clipboard, insertionTime, defaultType);
+
+ if (!command->isPossible()) {
+ KMessageBox::detailedError
+ (this,
+ i18n("Couldn't paste at this point."), RESTRICTED_PASTE_FAILED_DESCRIPTION);
+ } else {
+ addCommandToHistory(command);
+ setCurrentSelection(new EventSelection(command->getPastedEvents()));
+ slotSetInsertCursorPosition(endTime, true, false);
+ }
+}
+
+void NotationView::slotEditGeneralPaste()
+{
+ Clipboard *clipboard = getDocument()->getClipboard();
+
+ if (clipboard->isEmpty()) {
+ slotStatusHelpMsg(i18n("Clipboard is empty"));
+ return ;
+ }
+
+ slotStatusHelpMsg(i18n("Inserting clipboard contents..."));
+
+ LinedStaff *staff = getCurrentLinedStaff();
+ Segment &segment = staff->getSegment();
+
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+ PasteEventsCommand::PasteType defaultType = (PasteEventsCommand::PasteType)
+ config->readUnsignedNumEntry("pastetype",
+ PasteEventsCommand::Restricted);
+
+ PasteNotationDialog dialog(this, defaultType);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ PasteEventsCommand::PasteType type = dialog.getPasteType();
+ if (dialog.setAsDefault()) {
+ config->setGroup(NotationViewConfigGroup);
+ config->writeEntry("pastetype", type);
+ }
+
+ timeT insertionTime = getInsertionTime();
+ timeT endTime = insertionTime +
+ (clipboard->getSingleSegment()->getEndTime() -
+ clipboard->getSingleSegment()->getStartTime());
+
+ PasteEventsCommand *command = new PasteEventsCommand
+ (segment, clipboard, insertionTime, type);
+
+ if (!command->isPossible()) {
+ KMessageBox::detailedError
+ (this,
+ i18n("Couldn't paste at this point."),
+ i18n(RESTRICTED_PASTE_FAILED_DESCRIPTION));
+ } else {
+ addCommandToHistory(command);
+ setCurrentSelection(new EventSelection
+ (segment, insertionTime, endTime));
+ slotSetInsertCursorPosition(endTime, true, false);
+ }
+ }
+}
+
+void
+NotationView::slotMoveEventsUpStaff()
+{
+ LinedStaff *targetStaff = getStaffAbove();
+ if (!targetStaff) return;
+ if (!m_currentEventSelection) return;
+ Segment &targetSegment = targetStaff->getSegment();
+
+ KMacroCommand *command = new KMacroCommand(i18n("Move Events to Staff Above"));
+
+ timeT insertionTime = m_currentEventSelection->getStartTime();
+
+ Clipboard *c = new Clipboard;
+ CopyCommand *cc = new CopyCommand(*m_currentEventSelection, c);
+ cc->execute();
+
+ command->addCommand(new EraseCommand(*m_currentEventSelection));;
+
+ command->addCommand(new PasteEventsCommand
+ (targetSegment, c,
+ insertionTime,
+ PasteEventsCommand::NoteOverlay));
+
+ addCommandToHistory(command);
+
+ delete c;
+}
+
+void
+NotationView::slotMoveEventsDownStaff()
+{
+ LinedStaff *targetStaff = getStaffBelow();
+ if (!targetStaff) return;
+ if (!m_currentEventSelection) return;
+ Segment &targetSegment = targetStaff->getSegment();
+
+ KMacroCommand *command = new KMacroCommand(i18n("Move Events to Staff Below"));
+
+ timeT insertionTime = m_currentEventSelection->getStartTime();
+
+ Clipboard *c = new Clipboard;
+ CopyCommand *cc = new CopyCommand(*m_currentEventSelection, c);
+ cc->execute();
+
+ command->addCommand(new EraseCommand(*m_currentEventSelection));;
+
+ command->addCommand(new PasteEventsCommand
+ (targetSegment, c,
+ insertionTime,
+ PasteEventsCommand::NoteOverlay));
+
+ addCommandToHistory(command);
+
+ delete c;
+}
+
+void NotationView::slotPreviewSelection()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ getDocument()->slotSetLoop(m_currentEventSelection->getStartTime(),
+ m_currentEventSelection->getEndTime());
+}
+
+void NotationView::slotClearLoop()
+{
+ getDocument()->slotSetLoop(0, 0);
+}
+
+void NotationView::slotClearSelection()
+{
+ // Actually we don't clear the selection immediately: if we're
+ // using some tool other than the select tool, then the first
+ // press switches us back to the select tool.
+
+ NotationSelector *selector = dynamic_cast<NotationSelector *>(m_tool);
+
+ if (!selector) {
+ slotSelectSelected();
+ } else {
+ setCurrentSelection(0);
+ }
+}
+
+void NotationView::slotEditSelectFromStart()
+{
+ timeT t = getInsertionTime();
+ Segment &segment = m_staffs[m_currentStaff]->getSegment();
+ setCurrentSelection(new EventSelection(segment,
+ segment.getStartTime(),
+ t));
+}
+
+void NotationView::slotEditSelectToEnd()
+{
+ timeT t = getInsertionTime();
+ Segment &segment = m_staffs[m_currentStaff]->getSegment();
+ setCurrentSelection(new EventSelection(segment,
+ t,
+ segment.getEndMarkerTime()));
+}
+
+void NotationView::slotEditSelectWholeStaff()
+{
+ Segment &segment = m_staffs[m_currentStaff]->getSegment();
+ setCurrentSelection(new EventSelection(segment,
+ segment.getStartTime(),
+ segment.getEndMarkerTime()));
+}
+
+void NotationView::slotFilterSelection()
+{
+ NOTATION_DEBUG << "NotationView::slotFilterSelection" << endl;
+
+ Segment *segment = getCurrentSegment();
+ EventSelection *existingSelection = m_currentEventSelection;
+ if (!segment || !existingSelection)
+ return ;
+
+ EventFilterDialog dialog(this);
+ if (dialog.exec() == QDialog::Accepted) {
+ NOTATION_DEBUG << "slotFilterSelection- accepted" << endl;
+
+ bool haveEvent = false;
+
+ EventSelection *newSelection = new EventSelection(*segment);
+ EventSelection::eventcontainer &ec =
+ existingSelection->getSegmentEvents();
+ for (EventSelection::eventcontainer::iterator i =
+ ec.begin(); i != ec.end(); ++i) {
+ if (dialog.keepEvent(*i)) {
+ haveEvent = true;
+ newSelection->addEvent(*i);
+ }
+ }
+
+ if (haveEvent)
+ setCurrentSelection(newSelection);
+ else
+ setCurrentSelection(0);
+ }
+}
+
+void NotationView::slotFinePositionLeft()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Pushing selection left..."), this);
+
+ // half a note body width
+ addCommandToHistory(new IncrementDisplacementsCommand
+ (*m_currentEventSelection, -500, 0));
+}
+
+void NotationView::slotFinePositionRight()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Pushing selection right..."), this);
+
+ // half a note body width
+ addCommandToHistory(new IncrementDisplacementsCommand
+ (*m_currentEventSelection, 500, 0));
+}
+
+void NotationView::slotFinePositionUp()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Pushing selection up..."), this);
+
+ // half line height
+ addCommandToHistory(new IncrementDisplacementsCommand
+ (*m_currentEventSelection, 0, -500));
+}
+
+void NotationView::slotFinePositionDown()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Pushing selection down..."), this);
+
+ // half line height
+ addCommandToHistory(new IncrementDisplacementsCommand
+ (*m_currentEventSelection, 0, 500));
+}
+
+void NotationView::slotFinePositionRestore()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Restoring computed positions..."), this);
+
+ addCommandToHistory(new ResetDisplacementsCommand(*m_currentEventSelection));
+}
+
+void NotationView::slotMakeVisible()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Making visible..."), this);
+
+ addCommandToHistory(new SetVisibilityCommand(*m_currentEventSelection, true));
+}
+
+void NotationView::slotMakeInvisible()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Making invisible..."), this);
+
+ addCommandToHistory(new SetVisibilityCommand(*m_currentEventSelection, false));
+}
+
+void NotationView::slotToggleToolsToolBar()
+{
+ toggleNamedToolBar("Tools Toolbar");
+}
+
+void NotationView::slotToggleNotesToolBar()
+{
+ toggleNamedToolBar("Notes Toolbar");
+}
+
+void NotationView::slotToggleRestsToolBar()
+{
+ toggleNamedToolBar("Rests Toolbar");
+}
+
+void NotationView::slotToggleAccidentalsToolBar()
+{
+ toggleNamedToolBar("Accidentals Toolbar");
+}
+
+void NotationView::slotToggleClefsToolBar()
+{
+ toggleNamedToolBar("Clefs Toolbar");
+}
+
+void NotationView::slotToggleMetaToolBar()
+{
+ toggleNamedToolBar("Meta Toolbar");
+}
+
+void NotationView::slotToggleMarksToolBar()
+{
+ toggleNamedToolBar("Marks Toolbar");
+}
+
+void NotationView::slotToggleGroupToolBar()
+{
+ toggleNamedToolBar("Group Toolbar");
+}
+
+void NotationView::slotToggleLayoutToolBar()
+{
+ toggleNamedToolBar("Layout Toolbar");
+}
+
+void NotationView::slotToggleTransportToolBar()
+{
+ toggleNamedToolBar("Transport Toolbar");
+}
+
+void NotationView::toggleNamedToolBar(const QString& toolBarName, bool* force)
+{
+ KToolBar *namedToolBar = toolBar(toolBarName);
+
+ if (!namedToolBar) {
+ NOTATION_DEBUG << "NotationView::toggleNamedToolBar() : toolBar "
+ << toolBarName << " not found" << endl;
+ return ;
+ }
+
+ if (!force) {
+
+ if (namedToolBar->isVisible())
+ namedToolBar->hide();
+ else
+ namedToolBar->show();
+ } else {
+
+ if (*force)
+ namedToolBar->show();
+ else
+ namedToolBar->hide();
+ }
+
+ setSettingsDirty();
+
+}
+
+void NotationView::slotGroupBeam()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Beaming group..."), this);
+
+ addCommandToHistory(new BeamCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotGroupAutoBeam()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Auto-beaming selection..."), this);
+
+ addCommandToHistory(new AutoBeamCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotGroupBreak()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Breaking groups..."), this);
+
+ addCommandToHistory(new BreakCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotGroupSimpleTuplet()
+{
+ slotGroupTuplet(true);
+}
+
+void NotationView::slotGroupGeneralTuplet()
+{
+ slotGroupTuplet(false);
+}
+
+void NotationView::slotGroupTuplet(bool simple)
+{
+ timeT t = 0;
+ timeT unit = 0;
+ int tupled = 2;
+ int untupled = 3;
+ Segment *segment = 0;
+ bool hasTimingAlready = false;
+
+ if (m_currentEventSelection) {
+
+ t = m_currentEventSelection->getStartTime();
+
+ timeT duration = m_currentEventSelection->getTotalDuration();
+ Note::Type unitType =
+ Note::getNearestNote(duration / 3, 0).getNoteType();
+ unit = Note(unitType).getDuration();
+
+ if (!simple) {
+ TupletDialog dialog(this, unitType, duration);
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+ unit = Note(dialog.getUnitType()).getDuration();
+ tupled = dialog.getTupledCount();
+ untupled = dialog.getUntupledCount();
+ hasTimingAlready = dialog.hasTimingAlready();
+ }
+
+ segment = &m_currentEventSelection->getSegment();
+
+ } else {
+
+ t = getInsertionTime();
+
+ NoteInserter *currentInserter = dynamic_cast<NoteInserter *>
+ (m_toolBox->getTool(NoteInserter::ToolName));
+
+ Note::Type unitType;
+
+ if (currentInserter) {
+ unitType = currentInserter->getCurrentNote().getNoteType();
+ } else {
+ unitType = Note::Quaver;
+ }
+
+ unit = Note(unitType).getDuration();
+
+ if (!simple) {
+ TupletDialog dialog(this, unitType);
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+ unit = Note(dialog.getUnitType()).getDuration();
+ tupled = dialog.getTupledCount();
+ untupled = dialog.getUntupledCount();
+ hasTimingAlready = dialog.hasTimingAlready();
+ }
+
+ segment = &m_staffs[m_currentStaff]->getSegment();
+ }
+
+ addCommandToHistory(new TupletCommand
+ (*segment, t, unit, untupled, tupled, hasTimingAlready));
+
+ if (!hasTimingAlready) {
+ slotSetInsertCursorPosition(t + (unit * tupled), true, false);
+ }
+}
+
+void NotationView::slotGroupUnTuplet()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Untupleting..."), this);
+
+ addCommandToHistory(new UnTupletCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotGroupSlur()
+{
+ KTmpStatusMsg msg(i18n("Adding slur..."), this);
+ slotAddIndication(Indication::Slur, i18n("slur"));
+}
+
+void NotationView::slotGroupPhrasingSlur()
+{
+ KTmpStatusMsg msg(i18n("Adding phrasing slur..."), this);
+ slotAddIndication(Indication::PhrasingSlur, i18n("phrasing slur"));
+}
+
+void NotationView::slotGroupGlissando()
+{
+ KTmpStatusMsg msg(i18n("Adding glissando..."), this);
+ slotAddIndication(Indication::Glissando, i18n("glissando"));
+}
+
+void NotationView::slotGroupCrescendo()
+{
+ KTmpStatusMsg msg(i18n("Adding crescendo..."), this);
+ slotAddIndication(Indication::Crescendo, i18n("dynamic"));
+}
+
+void NotationView::slotGroupDecrescendo()
+{
+ KTmpStatusMsg msg(i18n("Adding decrescendo..."), this);
+ slotAddIndication(Indication::Decrescendo, i18n("dynamic"));
+}
+
+void NotationView::slotGroupOctave2Up()
+{
+ KTmpStatusMsg msg(i18n("Adding octave..."), this);
+ slotAddIndication(Indication::QuindicesimaUp, i18n("ottava"));
+}
+
+void NotationView::slotGroupOctaveUp()
+{
+ KTmpStatusMsg msg(i18n("Adding octave..."), this);
+ slotAddIndication(Indication::OttavaUp, i18n("ottava"));
+}
+
+void NotationView::slotGroupOctaveDown()
+{
+ KTmpStatusMsg msg(i18n("Adding octave..."), this);
+ slotAddIndication(Indication::OttavaDown, i18n("ottava"));
+}
+
+void NotationView::slotGroupOctave2Down()
+{
+ KTmpStatusMsg msg(i18n("Adding octave..."), this);
+ slotAddIndication(Indication::QuindicesimaDown, i18n("ottava"));
+}
+
+void NotationView::slotAddIndication(std::string type, QString desc)
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ AddIndicationCommand *command =
+ new AddIndicationCommand(type, *m_currentEventSelection);
+
+ if (command->canExecute()) {
+ addCommandToHistory(command);
+ setSingleSelectedEvent(m_currentEventSelection->getSegment(),
+ command->getLastInsertedEvent());
+ } else {
+ KMessageBox::sorry(this, i18n("Can't add overlapping %1 indications").arg(desc)); // TODO PLURAL - how many 'indications' ?
+ delete command;
+ }
+}
+
+void NotationView::slotGroupMakeChord()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Making chord..."), this);
+
+ MakeChordCommand *command =
+ new MakeChordCommand(*m_currentEventSelection);
+
+ addCommandToHistory(command);
+}
+
+void NotationView::slotTransformsNormalizeRests()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Normalizing rests..."), this);
+
+ addCommandToHistory(new NormalizeRestsCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsCollapseRests()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Collapsing rests..."), this);
+
+ addCommandToHistory(new CollapseRestsCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsCollapseNotes()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Collapsing notes..."), this);
+
+ addCommandToHistory(new CollapseNotesCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsTieNotes()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Tying notes..."), this);
+
+ addCommandToHistory(new TieNotesCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsUntieNotes()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Untying notes..."), this);
+
+ addCommandToHistory(new UntieNotesCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsMakeNotesViable()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Making notes viable..."), this);
+
+ addCommandToHistory(new MakeNotesViableCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsDeCounterpoint()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Removing counterpoint..."), this);
+
+ addCommandToHistory(new DeCounterpointCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsStemsUp()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Pointing stems up..."), this);
+
+ addCommandToHistory(new ChangeStemsCommand
+ (true, *m_currentEventSelection));
+}
+
+void NotationView::slotTransformsStemsDown()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Pointing stems down..."), this);
+
+ addCommandToHistory(new ChangeStemsCommand
+ (false, *m_currentEventSelection));
+
+}
+
+void NotationView::slotTransformsRestoreStems()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Restoring computed stem directions..."), this);
+
+ addCommandToHistory(new RestoreStemsCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsSlursAbove()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Positioning slurs..."), this);
+
+ addCommandToHistory(new ChangeSlurPositionCommand
+ (true, *m_currentEventSelection));
+}
+
+void NotationView::slotTransformsSlursBelow()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Positioning slurs..."), this);
+
+ addCommandToHistory(new ChangeSlurPositionCommand
+ (false, *m_currentEventSelection));
+
+}
+
+void NotationView::slotTransformsRestoreSlurs()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Restoring slur positions..."), this);
+
+ addCommandToHistory(new RestoreSlursCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsTiesAbove()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Positioning ties..."), this);
+
+ addCommandToHistory(new ChangeTiePositionCommand
+ (true, *m_currentEventSelection));
+}
+
+void NotationView::slotTransformsTiesBelow()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Positioning ties..."), this);
+
+ addCommandToHistory(new ChangeTiePositionCommand
+ (false, *m_currentEventSelection));
+
+}
+
+void NotationView::slotTransformsRestoreTies()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Restoring tie positions..."), this);
+
+ addCommandToHistory(new RestoreTiesCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsFixQuantization()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Fixing notation quantization..."), this);
+
+ addCommandToHistory(new FixNotationQuantizeCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsRemoveQuantization()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Removing notation quantization..."), this);
+
+ addCommandToHistory(new RemoveNotationQuantizeCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotSetStyleFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ if (!m_currentEventSelection)
+ return ;
+
+ if (name.left(6) == "style_") {
+ name = name.right(name.length() - 6);
+
+ KTmpStatusMsg msg(i18n("Changing to %1 style...").arg(name),
+ this);
+
+ addCommandToHistory(new ChangeStyleCommand
+ (NoteStyleName(qstrtostr(name)),
+ *m_currentEventSelection));
+ } else {
+ KMessageBox::sorry
+ (this, i18n("Unknown style action %1").arg(name));
+ }
+}
+
+void NotationView::slotInsertNoteFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ Segment &segment = m_staffs[m_currentStaff]->getSegment();
+
+ NoteInserter *noteInserter = dynamic_cast<NoteInserter *>(m_tool);
+ if (!noteInserter) {
+ KMessageBox::sorry(this, i18n("No note duration selected"));
+ return ;
+ }
+
+ int pitch = 0;
+ Accidental accidental =
+ Accidentals::NoAccidental;
+
+ timeT time(getInsertionTime());
+ Rosegarden::Key key = segment.getKeyAtTime(time);
+ Clef clef = segment.getClefAtTime(time);
+
+ try {
+
+ pitch = getPitchFromNoteInsertAction(name, accidental, clef, key);
+
+ } catch (...) {
+
+ KMessageBox::sorry
+ (this, i18n("Unknown note insert action %1").arg(name));
+ return ;
+ }
+
+ KTmpStatusMsg msg(i18n("Inserting note"), this);
+
+ NOTATION_DEBUG << "Inserting note at pitch " << pitch << endl;
+
+ noteInserter->insertNote(segment, time, pitch, accidental);
+}
+
+void NotationView::slotInsertRest()
+{
+ Segment &segment = m_staffs[m_currentStaff]->getSegment();
+ timeT time = getInsertionTime();
+
+ RestInserter *restInserter = dynamic_cast<RestInserter *>(m_tool);
+
+ if (!restInserter) {
+
+ NoteInserter *noteInserter = dynamic_cast<NoteInserter *>(m_tool);
+ if (!noteInserter) {
+ KMessageBox::sorry(this, i18n("No note duration selected"));
+ return ;
+ }
+
+ Note note(noteInserter->getCurrentNote());
+
+ restInserter = dynamic_cast<RestInserter*>
+ (m_toolBox->getTool(RestInserter::ToolName));
+
+ restInserter->slotSetNote(note.getNoteType());
+ restInserter->slotSetDots(note.getDots());
+ }
+
+ restInserter->insertNote(segment, time,
+ 0, Accidentals::NoAccidental, true);
+}
+
+void NotationView::slotSwitchFromRestToNote()
+{
+ RestInserter *restInserter = dynamic_cast<RestInserter *>(m_tool);
+ if (!restInserter) {
+ KMessageBox::sorry(this, i18n("No rest duration selected"));
+ return ;
+ }
+
+ Note note(restInserter->getCurrentNote());
+
+ QString actionName = NotationStrings::getReferenceName(note, false);
+ actionName = actionName.replace("-", "_");
+
+ KRadioAction *action = dynamic_cast<KRadioAction *>
+ (actionCollection()->action(actionName));
+
+ if (!action) {
+ std::cerr << "WARNING: Failed to find note action \""
+ << actionName << "\"" << std::endl;
+ } else {
+ action->activate();
+ }
+
+ NoteInserter *noteInserter = dynamic_cast<NoteInserter*>
+ (m_toolBox->getTool(NoteInserter::ToolName));
+
+ if (noteInserter) {
+ noteInserter->slotSetNote(note.getNoteType());
+ noteInserter->slotSetDots(note.getDots());
+ setTool(noteInserter);
+ }
+
+ setMenuStates();
+}
+
+void NotationView::slotSwitchFromNoteToRest()
+{
+ NoteInserter *noteInserter = dynamic_cast<NoteInserter *>(m_tool);
+ if (!noteInserter) {
+ KMessageBox::sorry(this, i18n("No note duration selected"));
+ return ;
+ }
+
+ Note note(noteInserter->getCurrentNote());
+
+ QString actionName = NotationStrings::getReferenceName(note, true);
+ actionName = actionName.replace("-", "_");
+
+ KRadioAction *action = dynamic_cast<KRadioAction *>
+ (actionCollection()->action(actionName));
+
+ if (!action) {
+ std::cerr << "WARNING: Failed to find rest action \""
+ << actionName << "\"" << std::endl;
+ } else {
+ action->activate();
+ }
+
+ RestInserter *restInserter = dynamic_cast<RestInserter*>
+ (m_toolBox->getTool(RestInserter::ToolName));
+
+ if (restInserter) {
+ restInserter->slotSetNote(note.getNoteType());
+ restInserter->slotSetDots(note.getDots());
+ setTool(restInserter);
+ }
+
+ setMenuStates();
+}
+
+void NotationView::slotToggleDot()
+{
+ NoteInserter *noteInserter = dynamic_cast<NoteInserter *>(m_tool);
+ if (noteInserter) {
+ Note note(noteInserter->getCurrentNote());
+ if (note.getNoteType() == Note::Shortest ||
+ note.getNoteType() == Note::Longest)
+ return ;
+ noteInserter->slotSetDots(note.getDots() ? 0 : 1);
+ setTool(noteInserter);
+ } else {
+ RestInserter *restInserter = dynamic_cast<RestInserter *>(m_tool);
+ if (restInserter) {
+ Note note(restInserter->getCurrentNote());
+ if (note.getNoteType() == Note::Shortest ||
+ note.getNoteType() == Note::Longest)
+ return ;
+ restInserter->slotSetDots(note.getDots() ? 0 : 1);
+ setTool(restInserter);
+ } else {
+ KMessageBox::sorry(this, i18n("No note or rest duration selected"));
+ }
+ }
+
+ setMenuStates();
+}
+
+void NotationView::slotRespellDoubleFlat()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Forcing accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Set,
+ Accidentals::DoubleFlat,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotRespellFlat()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Forcing accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Set,
+ Accidentals::Flat,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotRespellNatural()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Forcing accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Set,
+ Accidentals::Natural,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotRespellSharp()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Forcing accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Set,
+ Accidentals::Sharp,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotRespellDoubleSharp()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Forcing accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Set,
+ Accidentals::DoubleSharp,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotRespellUp()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Forcing accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Up,
+ Accidentals::NoAccidental,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotRespellDown()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Forcing accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Down,
+ Accidentals::NoAccidental,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotRespellRestore()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Restoring accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Restore,
+ Accidentals::NoAccidental,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotShowCautionary()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Showing cautionary accidentals..."), this);
+
+ addCommandToHistory(new MakeAccidentalsCautionaryCommand
+ (true, *m_currentEventSelection));
+}
+
+void NotationView::slotCancelCautionary()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Cancelling cautionary accidentals..."), this);
+
+ addCommandToHistory(new MakeAccidentalsCautionaryCommand
+ (false, *m_currentEventSelection));
+}
+
+void NotationView::slotTransformsQuantize()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ QuantizeDialog dialog(this, true);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ KTmpStatusMsg msg(i18n("Quantizing..."), this);
+ addCommandToHistory(new EventQuantizeCommand
+ (*m_currentEventSelection,
+ dialog.getQuantizer()));
+ }
+}
+
+void NotationView::slotTransformsInterpret()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ InterpretDialog dialog(this);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ KTmpStatusMsg msg(i18n("Interpreting selection..."), this);
+ addCommandToHistory(new InterpretCommand
+ (*m_currentEventSelection,
+ getDocument()->getComposition().getNotationQuantizer(),
+ dialog.getInterpretations()));
+ }
+}
+
+void NotationView::slotSetNoteDurations(Note::Type type, bool notationOnly)
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Setting note durations..."), this);
+ addCommandToHistory(new SetNoteTypeCommand(*m_currentEventSelection, type, notationOnly));
+}
+
+void NotationView::slotAddDot()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Adding dot..."), this);
+ addCommandToHistory(new AddDotCommand(*m_currentEventSelection, false));
+}
+
+void NotationView::slotAddDotNotationOnly()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Adding dot..."), this);
+ addCommandToHistory(new AddDotCommand(*m_currentEventSelection, true));
+}
+
+void NotationView::slotAddSlashes()
+{
+ const QObject *s = sender();
+ if (!m_currentEventSelection)
+ return ;
+
+ QString name = s->name();
+ int slashes = name.right(1).toInt();
+
+ addCommandToHistory(new AddSlashesCommand
+ (slashes, *m_currentEventSelection));
+}
+
+void NotationView::slotMarksAddTextMark()
+{
+ if (m_currentEventSelection) {
+ bool pressedOK = false;
+
+ QString txt = KLineEditDlg::getText(i18n("Text: "), "", &pressedOK, this);
+
+ if (pressedOK) {
+ addCommandToHistory(new AddTextMarkCommand
+ (qstrtostr(txt), *m_currentEventSelection));
+ }
+ }
+}
+
+void NotationView::slotMarksAddFingeringMark()
+{
+ if (m_currentEventSelection) {
+ bool pressedOK = false;
+
+ QString txt = KLineEditDlg::getText(i18n("Fingering: "), "", &pressedOK, this);
+
+ if (pressedOK) {
+ addCommandToHistory(new AddFingeringMarkCommand
+ (qstrtostr(txt), *m_currentEventSelection));
+ }
+ }
+}
+
+void NotationView::slotMarksAddFingeringMarkFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ if (name.left(14) == "add_fingering_") {
+
+ QString fingering = name.right(name.length() - 14);
+
+ if (fingering == "plus")
+ fingering = "+";
+
+ if (m_currentEventSelection) {
+ addCommandToHistory(new AddFingeringMarkCommand
+ (qstrtostr(fingering), *m_currentEventSelection));
+ }
+ }
+}
+
+void NotationView::slotMarksRemoveMarks()
+{
+ if (m_currentEventSelection)
+ addCommandToHistory(new RemoveMarksCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotMarksRemoveFingeringMarks()
+{
+ if (m_currentEventSelection)
+ addCommandToHistory(new RemoveFingeringMarksCommand
+ (*m_currentEventSelection));
+}
+
+void
+NotationView::slotMakeOrnament()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ EventSelection::eventcontainer &ec =
+ m_currentEventSelection->getSegmentEvents();
+
+ int basePitch = -1;
+ int baseVelocity = -1;
+ NoteStyle *style = NoteStyleFactory::getStyle(NoteStyleFactory::DefaultStyle);
+
+ for (EventSelection::eventcontainer::iterator i =
+ ec.begin(); i != ec.end(); ++i) {
+ if ((*i)->isa(Note::EventType)) {
+ if ((*i)->has(BaseProperties::PITCH)) {
+ basePitch = (*i)->get
+ <Int>
+ (BaseProperties::PITCH);
+ style = NoteStyleFactory::getStyleForEvent(*i);
+ if (baseVelocity != -1)
+ break;
+ }
+ if ((*i)->has(BaseProperties::VELOCITY)) {
+ baseVelocity = (*i)->get
+ <Int>
+ (BaseProperties::VELOCITY);
+ if (basePitch != -1)
+ break;
+ }
+ }
+ }
+
+ Staff *staff = getCurrentStaff();
+ Segment &segment = staff->getSegment();
+
+ timeT absTime = m_currentEventSelection->getStartTime();
+ timeT duration = m_currentEventSelection->getTotalDuration();
+ Note note(Note::getNearestNote(duration));
+
+ Track *track =
+ segment.getComposition()->getTrackById(segment.getTrack());
+ QString name;
+ int barNo = segment.getComposition()->getBarNumber(absTime);
+ if (track) {
+ name = QString(i18n("Ornament track %1 bar %2").arg(track->getPosition() + 1).arg(barNo + 1));
+ } else {
+ name = QString(i18n("Ornament bar %1").arg(barNo + 1));
+ }
+
+ MakeOrnamentDialog dialog(this, name, basePitch);
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+
+ name = dialog.getName();
+ basePitch = dialog.getBasePitch();
+
+ KMacroCommand *command = new KMacroCommand(i18n("Make Ornament"));
+
+ command->addCommand(new CutCommand
+ (*m_currentEventSelection,
+ getDocument()->getClipboard()));
+
+ command->addCommand(new PasteToTriggerSegmentCommand
+ (&getDocument()->getComposition(),
+ getDocument()->getClipboard(),
+ name, basePitch));
+
+ command->addCommand(new InsertTriggerNoteCommand
+ (segment, absTime, note, basePitch, baseVelocity,
+ style->getName(),
+ getDocument()->getComposition().getNextTriggerSegmentId(),
+ true,
+ BaseProperties::TRIGGER_SEGMENT_ADJUST_SQUISH,
+ Marks::NoMark)); //!!!
+
+ addCommandToHistory(command);
+}
+
+void
+NotationView::slotUseOrnament()
+{
+ // Take an existing note and match an ornament to it.
+
+ if (!m_currentEventSelection)
+ return ;
+
+ UseOrnamentDialog dialog(this, &getDocument()->getComposition());
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+
+ addCommandToHistory(new SetTriggerCommand(*m_currentEventSelection,
+ dialog.getId(),
+ true,
+ dialog.getRetune(),
+ dialog.getTimeAdjust(),
+ dialog.getMark(),
+ i18n("Use Ornament")));
+}
+
+void
+NotationView::slotRemoveOrnament()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ addCommandToHistory(new ClearTriggersCommand(*m_currentEventSelection,
+ i18n("Remove Ornaments")));
+}
+
+void NotationView::slotEditAddClef()
+{
+ Staff *staff = getCurrentStaff();
+ Segment &segment = staff->getSegment();
+ static Clef lastClef;
+ Clef clef;
+ Rosegarden::Key key;
+ timeT insertionTime = getInsertionTime(clef, key);
+
+ ClefDialog dialog(this, m_notePixmapFactory, lastClef);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ ClefDialog::ConversionType conversion = dialog.getConversionType();
+
+ bool shouldChangeOctave = (conversion != ClefDialog::NoConversion);
+ bool shouldTranspose = (conversion == ClefDialog::Transpose);
+
+ addCommandToHistory
+ (new ClefInsertionCommand
+ (segment, insertionTime, dialog.getClef(),
+ shouldChangeOctave, shouldTranspose));
+
+ lastClef = dialog.getClef();
+ }
+}
+
+void NotationView::slotEditAddKeySignature()
+{
+ Staff *staff = getCurrentStaff();
+ Segment &segment = staff->getSegment();
+ Clef clef;
+ Rosegarden::Key key;
+ timeT insertionTime = getInsertionTime(clef, key);
+
+ //!!! experimental:
+ CompositionTimeSliceAdapter adapter
+ (&getDocument()->getComposition(), insertionTime,
+ getDocument()->getComposition().getDuration());
+ AnalysisHelper helper;
+ key = helper.guessKey(adapter);
+
+ KeySignatureDialog dialog
+ (this, m_notePixmapFactory, clef, key, true, true,
+ i18n("Estimated key signature shown"));
+
+ if (dialog.exec() == QDialog::Accepted &&
+ dialog.isValid()) {
+
+ KeySignatureDialog::ConversionType conversion =
+ dialog.getConversionType();
+
+ bool transposeKey = dialog.shouldBeTransposed();
+ bool applyToAll = dialog.shouldApplyToAll();
+ bool ignorePercussion = dialog.shouldIgnorePercussion();
+
+ if (applyToAll) {
+ addCommandToHistory
+ (new MultiKeyInsertionCommand
+ (getDocument(),
+ insertionTime, dialog.getKey(),
+ conversion == KeySignatureDialog::Convert,
+ conversion == KeySignatureDialog::Transpose,
+ transposeKey,
+ ignorePercussion));
+ } else {
+ addCommandToHistory
+ (new KeyInsertionCommand
+ (segment,
+ insertionTime, dialog.getKey(),
+ conversion == KeySignatureDialog::Convert,
+ conversion == KeySignatureDialog::Transpose,
+ transposeKey,
+ false));
+ }
+ }
+}
+
+void NotationView::slotEditAddSustain(bool down)
+{
+ Staff *staff = getCurrentStaff();
+ Segment &segment = staff->getSegment();
+ timeT insertionTime = getInsertionTime();
+
+ Studio *studio = &getDocument()->getStudio();
+ Track *track = segment.getComposition()->getTrackById(segment.getTrack());
+
+ if (track) {
+
+ Instrument *instrument = studio->getInstrumentById
+ (track->getInstrument());
+ if (instrument) {
+ MidiDevice *device = dynamic_cast<MidiDevice *>
+ (instrument->getDevice());
+ if (device) {
+ for (ControlList::const_iterator i =
+ device->getControlParameters().begin();
+ i != device->getControlParameters().end(); ++i) {
+
+ if (i->getType() == Controller::EventType &&
+ (i->getName() == "Sustain" ||
+ strtoqstr(i->getName()) == i18n("Sustain"))) {
+
+ addCommandToHistory
+ (new SustainInsertionCommand(segment, insertionTime, down,
+ i->getControllerValue()));
+ return ;
+ }
+ }
+ } else if (instrument->getDevice() &&
+ instrument->getDevice()->getType() == Device::SoftSynth) {
+ addCommandToHistory
+ (new SustainInsertionCommand(segment, insertionTime, down, 64));
+ }
+ }
+ }
+
+ KMessageBox::sorry(this, i18n("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."));
+}
+
+void NotationView::slotEditAddSustainDown()
+{
+ slotEditAddSustain(true);
+}
+
+void NotationView::slotEditAddSustainUp()
+{
+ slotEditAddSustain(false);
+}
+
+void NotationView::slotEditTranspose()
+{
+ IntervalDialog intervalDialog(this, true, true);
+ int ok = intervalDialog.exec();
+
+ int semitones = intervalDialog.getChromaticDistance();
+ int steps = intervalDialog.getDiatonicDistance();
+
+ if (!ok || (semitones == 0 && steps == 0)) return;
+
+ // TODO combine commands into one
+ for (int i = 0; i < m_segments.size(); i++)
+ {
+ addCommandToHistory(new SegmentTransposeCommand(*(m_segments[i]),
+ intervalDialog.getChangeKey(), steps, semitones,
+ intervalDialog.getTransposeSegmentBack()));
+ }
+}
+
+void NotationView::slotEditSwitchPreset()
+{
+ PresetHandlerDialog dialog(this, true);
+
+ if (dialog.exec() != QDialog::Accepted) return;
+
+ if (dialog.getConvertAllSegments()) {
+ // get all segments for this track and convert them.
+ Composition& comp = getDocument()->getComposition();
+ TrackId selectedTrack = getCurrentSegment()->getTrack();
+
+ // satisfy #1885251 the way that seems most reasonble to me at the
+ // moment, only changing track parameters when acting on all segments on
+ // this track from the notation view
+ //
+ //!!! This won't be undoable, and I'm not sure if that's seriously
+ // wrong, or just mildly wrong, but I'm betting somebody will tell me
+ // about it if this was inappropriate
+ Track *track = comp.getTrackById(selectedTrack);
+ track->setPresetLabel(dialog.getName());
+ track->setClef(dialog.getClef());
+ track->setTranspose(dialog.getTranspose());
+ track->setLowestPlayable(dialog.getLowRange());
+ track->setHighestPlayable(dialog.getHighRange());
+
+ addCommandToHistory(new SegmentSyncCommand(comp.getSegments(), selectedTrack,
+ dialog.getTranspose(),
+ dialog.getLowRange(),
+ dialog.getHighRange(),
+ clefIndexToClef(dialog.getClef())));
+ } else {
+ addCommandToHistory(new SegmentSyncCommand(m_segments,
+ dialog.getTranspose(),
+ dialog.getLowRange(),
+ dialog.getHighRange(),
+ clefIndexToClef(dialog.getClef())));
+ }
+
+ m_doc->slotDocumentModified();
+ emit updateView();
+}
+
+void NotationView::slotEditElement(NotationStaff *staff,
+ NotationElement *element, bool advanced)
+{
+ if (advanced) {
+
+ EventEditDialog dialog(this, *element->event(), true);
+
+ if (dialog.exec() == QDialog::Accepted &&
+ dialog.isModified()) {
+
+ EventEditCommand *command = new EventEditCommand
+ (staff->getSegment(),
+ element->event(),
+ dialog.getEvent());
+
+ addCommandToHistory(command);
+ }
+
+ } else if (element->event()->isa(Clef::EventType)) {
+
+ try {
+ ClefDialog dialog(this, m_notePixmapFactory,
+ Clef(*element->event()));
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ ClefDialog::ConversionType conversion = dialog.getConversionType();
+ bool shouldChangeOctave = (conversion != ClefDialog::NoConversion);
+ bool shouldTranspose = (conversion == ClefDialog::Transpose);
+ addCommandToHistory
+ (new ClefInsertionCommand
+ (staff->getSegment(), element->event()->getAbsoluteTime(),
+ dialog.getClef(), shouldChangeOctave, shouldTranspose));
+ }
+ } catch (Exception e) {
+ std::cerr << e.getMessage() << std::endl;
+ }
+
+ return ;
+
+ } else if (element->event()->isa(Rosegarden::Key::EventType)) {
+
+ try {
+ Clef clef(staff->getSegment().getClefAtTime
+ (element->event()->getAbsoluteTime()));
+ KeySignatureDialog dialog
+ (this, m_notePixmapFactory, clef, Rosegarden::Key(*element->event()),
+ false, true);
+
+ if (dialog.exec() == QDialog::Accepted &&
+ dialog.isValid()) {
+
+ KeySignatureDialog::ConversionType conversion =
+ dialog.getConversionType();
+
+ addCommandToHistory
+ (new KeyInsertionCommand
+ (staff->getSegment(),
+ element->event()->getAbsoluteTime(), dialog.getKey(),
+ conversion == KeySignatureDialog::Convert,
+ conversion == KeySignatureDialog::Transpose,
+ dialog.shouldBeTransposed(),
+ dialog.shouldIgnorePercussion()));
+ }
+
+ } catch (Exception e) {
+ std::cerr << e.getMessage() << std::endl;
+ }
+
+ return ;
+
+ } else if (element->event()->isa(Text::EventType)) {
+
+ try {
+ TextEventDialog dialog
+ (this, m_notePixmapFactory, Text(*element->event()));
+ if (dialog.exec() == QDialog::Accepted) {
+ TextInsertionCommand *command = new TextInsertionCommand
+ (staff->getSegment(),
+ element->event()->getAbsoluteTime(),
+ dialog.getText());
+ KMacroCommand *macroCommand = new KMacroCommand(command->name());
+ macroCommand->addCommand(new EraseEventCommand(staff->getSegment(),
+ element->event(), false));
+ macroCommand->addCommand(command);
+ addCommandToHistory(macroCommand);
+ }
+ } catch (Exception e) {
+ std::cerr << e.getMessage() << std::endl;
+ }
+
+ return ;
+
+ } else if (element->isNote() &&
+ element->event()->has(BaseProperties::TRIGGER_SEGMENT_ID)) {
+
+ int id = element->event()->get
+ <Int>
+ (BaseProperties::TRIGGER_SEGMENT_ID);
+ emit editTriggerSegment(id);
+ return ;
+
+ } else {
+
+ SimpleEventEditDialog dialog(this, getDocument(), *element->event(), false);
+
+ if (dialog.exec() == QDialog::Accepted &&
+ dialog.isModified()) {
+
+ EventEditCommand *command = new EventEditCommand
+ (staff->getSegment(),
+ element->event(),
+ dialog.getEvent());
+
+ addCommandToHistory(command);
+ }
+ }
+}
+
+void NotationView::slotBeginLilyPondRepeat()
+{}
+
+void NotationView::slotDebugDump()
+{
+ if (m_currentEventSelection) {
+ EventSelection::eventcontainer &ec =
+ m_currentEventSelection->getSegmentEvents();
+ int n = 0;
+ for (EventSelection::eventcontainer::iterator i =
+ ec.begin();
+ i != ec.end(); ++i) {
+ std::cerr << "\n" << n++ << " [" << (*i) << "]" << std::endl;
+ (*i)->dump(std::cerr);
+ }
+ }
+}
+
+void
+NotationView::slotSetPointerPosition(timeT time)
+{
+ slotSetPointerPosition(time, m_playTracking);
+}
+
+void
+NotationView::slotSetPointerPosition(timeT time, bool scroll)
+{
+ Composition &comp = getDocument()->getComposition();
+ int barNo = comp.getBarNumber(time);
+
+ int minCy = 0;
+ double cx = 0;
+ bool haveMinCy = false;
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+
+ double layoutX = m_hlayout->getXForTimeByEvent(time);
+ Segment &seg = m_staffs[i]->getSegment();
+
+ bool good = true;
+
+ if (barNo >= m_hlayout->getLastVisibleBarOnStaff(*m_staffs[i])) {
+ if (seg.isRepeating() && time < seg.getRepeatEndTime()) {
+ timeT mappedTime =
+ seg.getStartTime() +
+ ((time - seg.getStartTime()) %
+ (seg.getEndMarkerTime() - seg.getStartTime()));
+ layoutX = m_hlayout->getXForTimeByEvent(mappedTime);
+ } else {
+ good = false;
+ }
+ } else if (barNo < m_hlayout->getFirstVisibleBarOnStaff(*m_staffs[i])) {
+ good = false;
+ }
+
+ if (!good) {
+
+ m_staffs[i]->hidePointer();
+
+ } else {
+
+ m_staffs[i]->setPointerPosition(layoutX);
+
+ int cy;
+ m_staffs[i]->getPointerPosition(cx, cy);
+
+ if (!haveMinCy || cy < minCy) {
+ minCy = cy;
+ haveMinCy = true;
+ }
+ }
+ }
+
+ if (m_pageMode == LinedStaff::LinearMode) {
+ // be careful not to prevent user from scrolling up and down
+ haveMinCy = false;
+ }
+
+ if (scroll) {
+ getCanvasView()->slotScrollHoriz(int(cx));
+ if (haveMinCy) {
+ getCanvasView()->slotScrollVertToTop(minCy);
+ }
+ }
+
+ updateView();
+}
+
+void
+NotationView::slotUpdateRecordingSegment(Segment *segment,
+ timeT updateFrom)
+{
+ NOTATION_DEBUG << "NotationView::slotUpdateRecordingSegment: segment " << segment << ", updateFrom " << updateFrom << ", end time " << segment->getEndMarkerTime() << endl;
+ if (updateFrom >= segment->getEndMarkerTime())
+ return ;
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ if (&m_staffs[i]->getSegment() == segment) {
+ refreshSegment(segment, 0, 0);
+ }
+ }
+ NOTATION_DEBUG << "NotationView::slotUpdateRecordingSegment: don't have segment " << segment << endl;
+}
+
+void
+NotationView::slotSetCurrentStaff(double x, int y)
+{
+ unsigned int staffNo;
+ for (staffNo = 0; staffNo < m_staffs.size(); ++staffNo) {
+ if (m_staffs[staffNo]->containsCanvasCoords(x, y))
+ break;
+ }
+
+ if (staffNo < m_staffs.size()) {
+ slotSetCurrentStaff(staffNo);
+ }
+}
+
+void
+NotationView::slotSetCurrentStaff(int staffNo)
+{
+ NOTATION_DEBUG << "NotationView::slotSetCurrentStaff(" << staffNo << ")" << endl;
+
+ if (m_currentStaff != staffNo) {
+
+ m_staffs[m_currentStaff]->setCurrent(false);
+
+ m_currentStaff = staffNo;
+
+ m_staffs[m_currentStaff]->setCurrent(true);
+
+ Segment *segment = &m_staffs[m_currentStaff]->getSegment();
+
+ m_chordNameRuler->setCurrentSegment(segment);
+ m_rawNoteRuler->setCurrentSegment(segment);
+ m_rawNoteRuler->repaint();
+ setControlRulersCurrentSegment();
+
+ updateView();
+
+ slotSetInsertCursorPosition(getInsertionTime(), false, false);
+
+ m_headersGroup->setCurrent(
+ m_staffs[staffNo]->getSegment().getTrack());
+ }
+}
+
+void
+NotationView::slotCurrentStaffUp()
+{
+ LinedStaff *staff = getStaffAbove();
+ if (!staff) return;
+ slotSetCurrentStaff(staff->getId());
+}
+
+void
+NotationView::slotCurrentStaffDown()
+{
+ LinedStaff *staff = getStaffBelow();
+ if (!staff) return;
+ slotSetCurrentStaff(staff->getId());
+}
+
+void
+NotationView::slotCurrentSegmentPrior()
+{
+ if (m_staffs.size() < 2)
+ return ;
+
+ Composition *composition =
+ m_staffs[m_currentStaff]->getSegment().getComposition();
+
+ Track *track = composition->
+ getTrackById(m_staffs[m_currentStaff]->getSegment().getTrack());
+ if (!track)
+ return ;
+
+ int lastStaffOnTrack = -1;
+
+ //
+ // TODO: Cycle segments through rather in time order?
+ // Cycle only segments in the field of view?
+ //
+ for (int i = m_staffs.size()-1; i >= 0; --i) {
+ if (m_staffs[i]->getSegment().getTrack() == track->getId()) {
+ if (lastStaffOnTrack < 0) {
+ lastStaffOnTrack = i;
+ }
+ if (i < m_currentStaff) {
+ slotSetCurrentStaff(i);
+ slotEditSelectWholeStaff();
+ return ;
+ }
+ }
+ }
+ if (lastStaffOnTrack >= 0) {
+ slotSetCurrentStaff(lastStaffOnTrack);
+ slotEditSelectWholeStaff();
+ return ;
+ }
+}
+
+void
+NotationView::slotCurrentSegmentNext()
+{
+ if (m_staffs.size() < 2)
+ return ;
+
+ Composition *composition =
+ m_staffs[m_currentStaff]->getSegment().getComposition();
+
+ Track *track = composition->
+ getTrackById(m_staffs[m_currentStaff]->getSegment().getTrack());
+ if (!track)
+ return ;
+
+ int firstStaffOnTrack = -1;
+
+ //
+ // TODO: Cycle segments through rather in time order?
+ // Cycle only segments in the field of view?
+ //
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ if (m_staffs[i]->getSegment().getTrack() == track->getId()) {
+ if (firstStaffOnTrack < 0) {
+ firstStaffOnTrack = i;
+ }
+ if (i > m_currentStaff) {
+ slotSetCurrentStaff(i);
+ slotEditSelectWholeStaff();
+ return ;
+ }
+ }
+ }
+ if (firstStaffOnTrack >= 0) {
+ slotSetCurrentStaff(firstStaffOnTrack);
+ slotEditSelectWholeStaff();
+ return ;
+ }
+}
+
+void
+NotationView::slotSetInsertCursorPosition(double x, int y, bool scroll,
+ bool updateNow)
+{
+ NOTATION_DEBUG << "NotationView::slotSetInsertCursorPosition: x " << x << ", y " << y << ", scroll " << scroll << ", now " << updateNow << endl;
+
+ slotSetCurrentStaff(x, y);
+
+ LinedStaff *staff = getLinedStaff(m_currentStaff);
+ Event *clefEvt, *keyEvt;
+ NotationElementList::iterator i =
+ staff->getElementUnderCanvasCoords(x, y, clefEvt, keyEvt);
+
+ if (i == staff->getViewElementList()->end()) {
+ slotSetInsertCursorPosition(staff->getSegment().getEndTime(), scroll,
+ updateNow);
+ } else {
+ slotSetInsertCursorPosition((*i)->getViewAbsoluteTime(), scroll,
+ updateNow);
+ }
+}
+
+void
+NotationView::slotSetInsertCursorPosition(timeT t, bool scroll, bool updateNow)
+{
+ NOTATION_DEBUG << "NotationView::slotSetInsertCursorPosition: time " << t << ", scroll " << scroll << ", now " << updateNow << endl;
+
+ m_insertionTime = t;
+ if (scroll) {
+ m_deferredCursorMove = CursorMoveAndMakeVisible;
+ } else {
+ m_deferredCursorMove = CursorMoveOnly;
+ }
+ if (updateNow)
+ doDeferredCursorMove();
+}
+
+void
+NotationView::slotSetInsertCursorAndRecentre(timeT t, double cx, int,
+ bool updateNow)
+{
+ NOTATION_DEBUG << "NotationView::slotSetInsertCursorAndRecentre: time " << t << ", cx " << cx << ", now " << updateNow << ", contentsx" << getCanvasView()->contentsX() << ", w " << getCanvasView()->visibleWidth() << endl;
+
+ m_insertionTime = t;
+
+ // We only do the scroll bit if cx is in the right two-thirds of
+ // the window
+
+ if (cx < (getCanvasView()->contentsX() +
+ getCanvasView()->visibleWidth() / 3)) {
+
+ m_deferredCursorMove = CursorMoveOnly;
+ } else {
+ m_deferredCursorMove = CursorMoveAndScrollToPosition;
+ m_deferredCursorScrollToX = cx;
+ }
+
+ if (updateNow)
+ doDeferredCursorMove();
+}
+
+void
+NotationView::doDeferredCursorMove()
+{
+ NOTATION_DEBUG << "NotationView::doDeferredCursorMove: m_deferredCursorMove == " << m_deferredCursorMove << endl;
+
+ if (m_deferredCursorMove == NoCursorMoveNeeded) {
+ return ;
+ }
+
+ DeferredCursorMoveType type = m_deferredCursorMove;
+ m_deferredCursorMove = NoCursorMoveNeeded;
+
+ timeT t = m_insertionTime;
+
+ if (m_staffs.size() == 0)
+ return ;
+ LinedStaff *staff = getCurrentLinedStaff();
+ Segment &segment = staff->getSegment();
+
+ if (t < segment.getStartTime()) {
+ t = segment.getStartTime();
+ }
+ if (t > segment.getEndTime()) {
+ t = segment.getEndTime();
+ }
+
+ NotationElementList::iterator i =
+ staff->getViewElementList()->findNearestTime(t);
+
+ while (i != staff->getViewElementList()->end() &&
+ !static_cast<NotationElement*>(*i)->getCanvasItem())
+ ++i;
+
+ if (i == staff->getViewElementList()->end()) {
+ //!!! ???
+ if (m_insertionTime >= staff->getSegment().getStartTime()) {
+ i = staff->getViewElementList()->begin();
+ }
+ m_insertionTime = staff->getSegment().getStartTime();
+ } else {
+ m_insertionTime = static_cast<NotationElement*>(*i)->getViewAbsoluteTime();
+ }
+
+ if (i == staff->getViewElementList()->end() ||
+ t == segment.getEndTime() ||
+ t == segment.getBarStartForTime(t)) {
+
+ staff->setInsertCursorPosition(*m_hlayout, t);
+
+ if (type == CursorMoveAndMakeVisible) {
+ double cx;
+ int cy;
+ staff->getInsertCursorPosition(cx, cy);
+ getCanvasView()->slotScrollHoriz(int(cx));
+ getCanvasView()->slotScrollVertSmallSteps(cy);
+ }
+
+ } else {
+
+ // prefer a note or rest, if there is one, to a non-spacing event
+ if (!static_cast<NotationElement*>(*i)->isNote() &&
+ !static_cast<NotationElement*>(*i)->isRest()) {
+ NotationElementList::iterator j = i;
+ while (j != staff->getViewElementList()->end()) {
+ if (static_cast<NotationElement*>(*j)->getViewAbsoluteTime() !=
+ static_cast<NotationElement*>(*i)->getViewAbsoluteTime())
+ break;
+ if (static_cast<NotationElement*>(*j)->getCanvasItem()) {
+ if (static_cast<NotationElement*>(*j)->isNote() ||
+ static_cast<NotationElement*>(*j)->isRest()) {
+ i = j;
+ break;
+ }
+ }
+ ++j;
+ }
+ }
+
+ if (static_cast<NotationElement*>(*i)->getCanvasItem()) {
+
+ staff->setInsertCursorPosition
+ (static_cast<NotationElement*>(*i)->getCanvasX() - 2,
+ int(static_cast<NotationElement*>(*i)->getCanvasY()));
+
+ if (type == CursorMoveAndMakeVisible) {
+ getCanvasView()->slotScrollHoriz
+ (int(static_cast<NotationElement*>(*i)->getCanvasX()) - 4);
+ }
+ } else {
+ std::cerr << "WARNING: No canvas item for this notation element:";
+ (*i)->event()->dump(std::cerr);
+ }
+ }
+
+ if (type == CursorMoveAndScrollToPosition) {
+
+ // get current canvas x of insert cursor, which might not be
+ // what we just set
+
+ double ccx = 0.0;
+
+ NotationElementList::iterator i =
+ staff->getViewElementList()->findTime(t);
+
+ if (i == staff->getViewElementList()->end()) {
+ if (i == staff->getViewElementList()->begin())
+ return ;
+ double lx, lwidth;
+ --i;
+ if (static_cast<NotationElement*>(*i)->getCanvasItem()) {
+ ccx = static_cast<NotationElement*>(*i)->getCanvasX();
+ static_cast<NotationElement*>(*i)->getLayoutAirspace(lx, lwidth);
+ } else {
+ std::cerr << "WARNING: No canvas item for this notation element*:";
+ (*i)->event()->dump(std::cerr);
+ }
+ ccx += lwidth;
+ } else {
+ if (static_cast<NotationElement*>(*i)->getCanvasItem()) {
+ ccx = static_cast<NotationElement*>(*i)->getCanvasX();
+ } else {
+ std::cerr << "WARNING: No canvas item for this notation element*:";
+ (*i)->event()->dump(std::cerr);
+ }
+ }
+
+ QScrollBar* hbar = getCanvasView()->horizontalScrollBar();
+ hbar->setValue(int(hbar->value() - (m_deferredCursorScrollToX - ccx)));
+ }
+
+ updateView();
+}
+
+void
+NotationView::slotJumpCursorToPlayback()
+{
+ slotSetInsertCursorPosition(getDocument()->getComposition().getPosition());
+}
+
+void
+NotationView::slotJumpPlaybackToCursor()
+{
+ emit jumpPlaybackTo(getInsertionTime());
+}
+
+void
+NotationView::slotToggleTracking()
+{
+ m_playTracking = !m_playTracking;
+}
+
+void NotationView::slotNoAccidental()
+{
+ emit changeAccidental(Accidentals::NoAccidental, false);
+}
+
+void NotationView::slotFollowAccidental()
+{
+ emit changeAccidental(Accidentals::NoAccidental, true);
+}
+
+void NotationView::slotSharp()
+{
+ emit changeAccidental(Accidentals::Sharp, false);
+}
+
+void NotationView::slotFlat()
+{
+ emit changeAccidental(Accidentals::Flat, false);
+}
+
+void NotationView::slotNatural()
+{
+ emit changeAccidental(Accidentals::Natural, false);
+}
+
+void NotationView::slotDoubleSharp()
+{
+ emit changeAccidental(Accidentals::DoubleSharp, false);
+}
+
+void NotationView::slotDoubleFlat()
+{
+ emit changeAccidental(Accidentals::DoubleFlat, false);
+}
+
+void NotationView::slotTrebleClef()
+{
+ m_currentNotePixmap->setPixmap
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-treble")));
+ setTool(m_toolBox->getTool(ClefInserter::ToolName));
+
+ dynamic_cast<ClefInserter*>(m_tool)->setClef(Clef::Treble);
+ setMenuStates();
+}
+
+void NotationView::slotAltoClef()
+{
+ m_currentNotePixmap->setPixmap
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-alto")));
+ setTool(m_toolBox->getTool(ClefInserter::ToolName));
+
+ dynamic_cast<ClefInserter*>(m_tool)->setClef(Clef::Alto);
+ setMenuStates();
+}
+
+void NotationView::slotTenorClef()
+{
+ m_currentNotePixmap->setPixmap
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-tenor")));
+ setTool(m_toolBox->getTool(ClefInserter::ToolName));
+
+ dynamic_cast<ClefInserter*>(m_tool)->setClef(Clef::Tenor);
+ setMenuStates();
+}
+
+void NotationView::slotBassClef()
+{
+ m_currentNotePixmap->setPixmap
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-bass")));
+ setTool(m_toolBox->getTool(ClefInserter::ToolName));
+
+ dynamic_cast<ClefInserter*>(m_tool)->setClef(Clef::Bass);
+ setMenuStates();
+}
+
+void NotationView::slotText()
+{
+ m_currentNotePixmap->setPixmap
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("text")));
+ setTool(m_toolBox->getTool(TextInserter::ToolName));
+ setMenuStates();
+}
+
+void NotationView::slotGuitarChord()
+{
+ m_currentNotePixmap->setPixmap
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("guitarchord")));
+ setTool(m_toolBox->getTool(GuitarChordInserter::ToolName));
+ setMenuStates();
+}
+
+void NotationView::slotEraseSelected()
+{
+ NOTATION_DEBUG << "NotationView::slotEraseSelected()" << endl;
+ setTool(m_toolBox->getTool(NotationEraser::ToolName));
+ setMenuStates();
+}
+
+void NotationView::slotSelectSelected()
+{
+ NOTATION_DEBUG << "NotationView::slotSelectSelected()" << endl;
+ setTool(m_toolBox->getTool(NotationSelector::ToolName));
+ setMenuStates();
+}
+
+void NotationView::slotLinearMode()
+{
+ setPageMode(LinedStaff::LinearMode);
+}
+
+void NotationView::slotContinuousPageMode()
+{
+ setPageMode(LinedStaff::ContinuousPageMode);
+}
+
+void NotationView::slotMultiPageMode()
+{
+ setPageMode(LinedStaff::MultiPageMode);
+}
+
+void NotationView::slotToggleChordsRuler()
+{
+ if (m_hlayout->isPageMode())
+ return ;
+ toggleWidget(m_chordNameRuler, "show_chords_ruler");
+}
+
+void NotationView::slotToggleRawNoteRuler()
+{
+ if (m_hlayout->isPageMode())
+ return ;
+ toggleWidget(m_rawNoteRuler, "show_raw_note_ruler");
+}
+
+void NotationView::slotToggleTempoRuler()
+{
+ if (m_hlayout->isPageMode())
+ return ;
+ toggleWidget(m_tempoRuler, "show_tempo_ruler");
+}
+
+void NotationView::slotToggleAnnotations()
+{
+ m_annotationsVisible = !m_annotationsVisible;
+ slotUpdateAnnotationsStatus();
+ //!!! use refresh mechanism
+ refreshSegment(0, 0, 0);
+}
+
+void NotationView::slotToggleLilyPondDirectives()
+{
+ m_lilyPondDirectivesVisible = !m_lilyPondDirectivesVisible;
+ slotUpdateLilyPondDirectivesStatus();
+ //!!! use refresh mechanism
+ refreshSegment(0, 0, 0);
+}
+
+void NotationView::slotEditLyrics()
+{
+ Staff *staff = getCurrentStaff();
+ Segment &segment = staff->getSegment();
+
+ LyricEditDialog dialog(this, &segment);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ KMacroCommand *macro = new KMacroCommand
+ (SetLyricsCommand::getGlobalName());
+
+ for (int i = 0; i < dialog.getVerseCount(); ++i) {
+ SetLyricsCommand *command = new SetLyricsCommand
+ (&segment, i, dialog.getLyricData(i));
+ macro->addCommand(command);
+ }
+
+ addCommandToHistory(macro);
+ }
+}
+
+void NotationView::slotItemPressed(int height, int staffNo,
+ QMouseEvent* e,
+ NotationElement* el)
+{
+ NOTATION_DEBUG << "NotationView::slotItemPressed(height = "
+ << height << ", staffNo = " << staffNo
+ << ")" << endl;
+
+ if (staffNo < 0 && el != 0) {
+ // We have an element but no staff -- that's because the
+ // element extended outside the staff region. But we need
+ // to handle it properly, so we rather laboriously need to
+ // find out which staff it was.
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ if (m_staffs[i]->getViewElementList()->findSingle(el) !=
+ m_staffs[i]->getViewElementList()->end()) {
+ staffNo = m_staffs[i]->getId();
+ break;
+ }
+ }
+ }
+
+ ButtonState btnState = e->state();
+
+ if (btnState & ControlButton) { // on ctrl-click, set cursor position
+
+ slotSetInsertCursorPosition(e->x(), (int)e->y());
+
+ } else {
+
+ setActiveItem(0);
+
+ timeT unknownTime = 0;
+
+ if (e->type() == QEvent::MouseButtonDblClick) {
+ m_tool->handleMouseDoubleClick(unknownTime, height,
+ staffNo, e, el);
+ } else {
+ m_tool->handleMousePress(unknownTime, height,
+ staffNo, e, el);
+ }
+ }
+}
+
+void NotationView::slotNonNotationItemPressed(QMouseEvent *e, QCanvasItem *it)
+{
+ if (e->type() != QEvent::MouseButtonDblClick)
+ return ;
+
+ Staff *staff = getStaffForCanvasCoords(e->x(), e->y());
+ if (!staff)
+ return ;
+
+ NOTATION_DEBUG << "NotationView::slotNonNotationItemPressed(doubly)" << endl;
+
+ if (dynamic_cast<QCanvasStaffNameSprite *>(it)) {
+
+ std::string name =
+ staff->getSegment().getComposition()->
+ getTrackById(staff->getSegment().getTrack())->getLabel();
+
+ bool ok = false;
+ QRegExpValidator validator(QRegExp(".*"), this); // empty is OK
+
+ QString newText = KLineEditDlg::getText(QString("Change staff name"),
+ QString("Enter new staff name"),
+ strtoqstr(name),
+ &ok,
+ this,
+ &validator);
+
+ if (ok) {
+ addCommandToHistory(new RenameTrackCommand
+ (staff->getSegment().getComposition(),
+ staff->getSegment().getTrack(),
+ qstrtostr(newText)));
+
+ emit staffLabelChanged(staff->getSegment().getTrack(), newText);
+ }
+
+ } else if (dynamic_cast<QCanvasTimeSigSprite *>(it)) {
+
+ double layoutX = (dynamic_cast<QCanvasTimeSigSprite *>(it))->getLayoutX();
+ emit editTimeSignature(m_hlayout->getTimeForX(layoutX));
+ }
+}
+
+void NotationView::slotTextItemPressed(QMouseEvent *e, QCanvasItem *it)
+{
+ if (e->type() != QEvent::MouseButtonDblClick)
+ return ;
+
+ if (it == m_title) {
+ emit editMetadata(strtoqstr(CompositionMetadataKeys::Title.getName()));
+ } else if (it == m_subtitle) {
+ emit editMetadata(strtoqstr(CompositionMetadataKeys::Subtitle.getName()));
+ } else if (it == m_composer) {
+ emit editMetadata(strtoqstr(CompositionMetadataKeys::Composer.getName()));
+ } else if (it == m_copyright) {
+ emit editMetadata(strtoqstr(CompositionMetadataKeys::Copyright.getName()));
+ } else {
+ return ;
+ }
+
+ positionStaffs();
+}
+
+void NotationView::slotMouseMoved(QMouseEvent *e)
+{
+ if (activeItem()) {
+ activeItem()->handleMouseMove(e);
+ updateView();
+ } else {
+ int follow = m_tool->handleMouseMove(0, 0, // unknown time and height
+ e);
+
+ if (getCanvasView()->isTimeForSmoothScroll()) {
+
+ if (follow & RosegardenCanvasView::FollowHorizontal) {
+ getCanvasView()->slotScrollHorizSmallSteps(e->x());
+ }
+
+ if (follow & RosegardenCanvasView::FollowVertical) {
+ getCanvasView()->slotScrollVertSmallSteps(e->y());
+ }
+
+ }
+ }
+}
+
+void NotationView::slotMouseReleased(QMouseEvent *e)
+{
+ if (activeItem()) {
+ activeItem()->handleMouseRelease(e);
+ setActiveItem(0);
+ updateView();
+ } else
+ m_tool->handleMouseRelease(0, 0, // unknown time and height
+ e);
+}
+
+void
+NotationView::slotHoveredOverNoteChanged(const QString &noteName)
+{
+ m_hoveredOverNoteName->setText(QString(" ") + noteName);
+}
+
+void
+NotationView::slotHoveredOverAbsoluteTimeChanged(unsigned int time)
+{
+ timeT t = time;
+ RealTime rt =
+ getDocument()->getComposition().getElapsedRealTime(t);
+ long ms = rt.msec();
+
+ int bar, beat, fraction, remainder;
+ getDocument()->getComposition().getMusicalTimeForAbsoluteTime
+ (t, bar, beat, fraction, remainder);
+
+ // QString message;
+ // QString format("%ld (%ld.%03lds)");
+ // format = i18n("Time: %1").arg(format);
+ // message.sprintf(format, t, rt.sec, ms);
+
+ QString message = i18n("Time: %1 (%2.%3s)")
+ .arg(QString("%1-%2-%3-%4")
+ .arg(QString("%1").arg(bar + 1).rightJustify(3, '0'))
+ .arg(QString("%1").arg(beat).rightJustify(2, '0'))
+ .arg(QString("%1").arg(fraction).rightJustify(2, '0'))
+ .arg(QString("%1").arg(remainder).rightJustify(2, '0')))
+ .arg(rt.sec)
+ .arg(QString("%1").arg(ms).rightJustify(3, '0'));
+
+ m_hoveredOverAbsoluteTime->setText(message);
+}
+
+void
+NotationView::slotInsertableNoteEventReceived(int pitch, int velocity, bool noteOn)
+{
+ //!!! Problematic. Ideally we wouldn't insert events into windows
+ //that weren't actually visible, otherwise all hell could break
+ //loose (metaphorically speaking, I should probably add). I did
+ //think of checking isActiveWindow() and returning if the current
+ //window wasn't active, but that will prevent anyone from
+ //step-recording from e.g. vkeybd, which cannot be used without
+ //losing focus (and thus active-ness) from the Rosegarden window.
+
+ //!!! I know -- we'll keep track of which edit view (or main view,
+ //or mixer, etc) is active, and we'll only allow insertion into
+ //the most recently activated. How about that?
+
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("toggle_step_by_step"));
+ if (!action) {
+ NOTATION_DEBUG << "WARNING: No toggle_step_by_step action" << endl;
+ return ;
+ }
+ if (!action->isChecked())
+ return ;
+
+ Segment &segment = m_staffs[m_currentStaff]->getSegment();
+
+ NoteInserter *noteInserter = dynamic_cast<NoteInserter *>(m_tool);
+ if (!noteInserter) {
+ static bool showingError = false;
+ if (showingError)
+ return ;
+ showingError = true;
+ KMessageBox::sorry(this, i18n("Can't insert note: No note duration selected"));
+ showingError = false;
+ return ;
+ }
+
+ if (m_inPaintEvent) {
+ NOTATION_DEBUG << "NotationView::slotInsertableNoteEventReceived: in paint event already" << endl;
+ if (noteOn) {
+ m_pendingInsertableNotes.push_back(std::pair<int, int>(pitch, velocity));
+ }
+ return ;
+ }
+
+ // If the segment is transposed, we want to take that into
+ // account. But the note has already been played back to the user
+ // at its untransposed pitch, because that's done by the MIDI THRU
+ // code in the sequencer which has no way to know whether a note
+ // was intended for step recording. So rather than adjust the
+ // pitch for playback according to the transpose setting, we have
+ // to adjust the stored pitch in the opposite direction.
+
+ pitch -= segment.getTranspose();
+
+ // KTmpStatusMsg msg(i18n("Inserting note"), this);
+
+ // We need to ensure that multiple notes hit at once come out as
+ // chords, without imposing the interpretation that overlapping
+ // notes are always chords and without getting too involved with
+ // the actual absolute times of the notes (this is still step
+ // editing, not proper recording).
+
+ // First, if we're in chord mode, there's no problem.
+
+ static int numberOfNotesOn = 0;
+ static timeT insertionTime = getInsertionTime();
+ static time_t lastInsertionTime = 0;
+
+ if (isInChordMode()) {
+ if (!noteOn)
+ return ;
+ NOTATION_DEBUG << "Inserting note in chord at pitch " << pitch << endl;
+ noteInserter->insertNote(segment, getInsertionTime(), pitch,
+ Accidentals::NoAccidental,
+ true);
+
+ } else {
+
+ if (!noteOn) {
+ numberOfNotesOn--;
+ } else if (noteOn) {
+ // Rules:
+ //
+ // * If no other note event has turned up within half a
+ // second, insert this note and advance.
+ //
+ // * Relatedly, if this note is within half a second of
+ // the previous one, they're chords. Insert the previous
+ // one, don't advance, and use the same rules for this.
+ //
+ // * If a note event turns up before that time has elapsed,
+ // we need to wait for the note-off events: if the second
+ // note happened less than half way through the first,
+ // it's a chord.
+ //
+ // We haven't implemented these yet... For now:
+ //
+ // Rules (hjj):
+ //
+ // * The overlapping notes are always included in to a chord.
+ // This is the most convenient for step inserting of chords.
+ //
+ // * The timer resets the numberOfNotesOn, if noteOff signals were
+ // drop out for some reason (which has not been encountered yet).
+
+ time_t now;
+ time (&now);
+ double elapsed = difftime(now, lastInsertionTime);
+ time (&lastInsertionTime);
+
+ if (numberOfNotesOn <= 0 || elapsed > 10.0 ) {
+ numberOfNotesOn = 0;
+ insertionTime = getInsertionTime();
+ }
+ numberOfNotesOn++;
+
+ noteInserter->insertNote(segment, insertionTime, pitch,
+ Accidentals::NoAccidental,
+ true);
+ }
+ }
+}
+
+void
+NotationView::slotInsertableNoteOnReceived(int pitch, int velocity)
+{
+ NOTATION_DEBUG << "NotationView::slotInsertableNoteOnReceived: " << pitch << endl;
+ slotInsertableNoteEventReceived(pitch, velocity, true);
+}
+
+void
+NotationView::slotInsertableNoteOffReceived(int pitch, int velocity)
+{
+ NOTATION_DEBUG << "NotationView::slotInsertableNoteOffReceived: " << pitch << endl;
+ slotInsertableNoteEventReceived(pitch, velocity, false);
+}
+
+void
+NotationView::slotInsertableTimerElapsed()
+{}
+
+void
+NotationView::slotToggleStepByStep()
+{
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("toggle_step_by_step"));
+ if (!action) {
+ NOTATION_DEBUG << "WARNING: No toggle_step_by_step action" << endl;
+ return ;
+ }
+ if (action->isChecked()) { // after toggling, that is
+ emit stepByStepTargetRequested(this);
+ } else {
+ emit stepByStepTargetRequested(0);
+ }
+}
+
+void
+NotationView::slotStepByStepTargetRequested(QObject *obj)
+{
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("toggle_step_by_step"));
+ if (!action) {
+ NOTATION_DEBUG << "WARNING: No toggle_step_by_step action" << endl;
+ return ;
+ }
+ action->setChecked(obj == this);
+}
+
+void
+NotationView::slotCheckRendered(double cx0, double cx1)
+{
+ // NOTATION_DEBUG << "slotCheckRendered(" << cx0 << "," << cx1 << ")" << endl;
+
+ bool something = false;
+
+ for (size_t i = 0; i < m_staffs.size(); ++i) {
+
+ LinedStaff *staff = m_staffs[i];
+
+ LinedStaff::LinedStaffCoords cc0 = staff->getLayoutCoordsForCanvasCoords
+ (cx0, 0);
+
+ LinedStaff::LinedStaffCoords cc1 = staff->getLayoutCoordsForCanvasCoords
+ (cx1, staff->getTotalHeight() + staff->getY());
+
+ timeT t0 = m_hlayout->getTimeForX(cc0.first);
+ timeT t1 = m_hlayout->getTimeForX(cc1.first);
+
+ if (dynamic_cast<NotationStaff *>(staff)->checkRendered(t0, t1)) {
+ something = true; //!!!
+ }
+ }
+
+ if (something) {
+ emit renderComplete();
+ if (m_renderTimer)
+ delete m_renderTimer;
+ m_renderTimer = new QTimer(this);
+ connect(m_renderTimer, SIGNAL(timeout()), SLOT(slotRenderSomething()));
+ m_renderTimer->start(0, true);
+ }
+
+ if (m_deferredCursorMove != NoCursorMoveNeeded)
+ doDeferredCursorMove();
+}
+
+void
+NotationView::slotRenderSomething()
+{
+ delete m_renderTimer;
+ m_renderTimer = 0;
+ static clock_t lastWork = 0;
+
+ clock_t now = clock();
+ long elapsed = ((now - lastWork) * 1000 / CLOCKS_PER_SEC);
+ if (elapsed < 70) {
+ m_renderTimer = new QTimer(this);
+ connect(m_renderTimer, SIGNAL(timeout()), SLOT(slotRenderSomething()));
+ m_renderTimer->start(0, true);
+ return ;
+ }
+ lastWork = now;
+
+ for (size_t i = 0; i < m_staffs.size(); ++i) {
+
+ if (m_staffs[i]->doRenderWork(m_staffs[i]->getSegment().getStartTime(),
+ m_staffs[i]->getSegment().getEndTime())) {
+ m_renderTimer = new QTimer(this);
+ connect(m_renderTimer, SIGNAL(timeout()), SLOT(slotRenderSomething()));
+ m_renderTimer->start(0, true);
+ return ;
+ }
+ }
+
+ PixmapArrayGC::deleteAll();
+ NOTATION_DEBUG << "NotationView::slotRenderSomething: updating thumbnails" << endl;
+ updateThumbnails(true);
+
+ // Update track headers when rendering is done
+ // (better late than never)
+ m_headersGroup->slotUpdateAllHeaders(getCanvasLeftX(), 0, true);
+ m_headersGroupView->setContentsPos(getCanvasView()->contentsX(),
+ getCanvasView()->contentsY());
+}
+
+NotationCanvasView* NotationView::getCanvasView()
+{
+ return dynamic_cast<NotationCanvasView *>(m_canvasView);
+}
+
+void
+NotationView::slotVerticalScrollHeadersGroup(int y)
+{
+ m_headersGroupView->setContentsPos(0, y);
+}
+
+void
+NotationView::slotShowHeadersGroup()
+{
+ m_showHeadersGroup = HeadersGroup::ShowAlways;
+ showHeadersGroup();
+
+ // Disable menu entry when headers are shown
+ m_showHeadersMenuEntry->setEnabled(false);
+}
+
+void
+NotationView::slotHideHeadersGroup()
+{
+ m_showHeadersGroup = HeadersGroup::ShowNever;
+ hideHeadersGroup();
+
+ // Enable menu entry when headers are hidden
+ m_showHeadersMenuEntry->setEnabled(true);
+}
+
+void
+NotationView::showHeadersGroup()
+{
+ if (m_headersGroupView && (m_pageMode == LinedStaff::LinearMode)) {
+ m_headersGroupView->show();
+ m_headersTopFrame->show();
+ m_rulerBoxFiller->show();
+ }
+}
+
+void
+NotationView::hideHeadersGroup()
+{
+ if (m_headersGroupView) {
+ m_headersGroupView->hide();
+ m_headersTopFrame->hide();
+ m_rulerBoxFiller->hide();
+ }
+}
+
+void
+NotationView::slotUpdateHeaders(int x, int y)
+{
+ m_headersGroup->slotUpdateAllHeaders(x, y);
+ m_headersGroupView->setContentsPos(x, y);
+}
+
+void
+NotationView::slotHeadersWidthChanged(int w)
+{
+ m_headersTopFrame->setFixedWidth(w);
+ m_rulerBoxFiller->setFixedWidth(w);
+ m_canvasView->updateLeftWidgetGeometry();
+}
+
+
+int
+NotationView::getCanvasVisibleWidth()
+{
+ if (getCanvasView()) {
+ return getCanvasView()->visibleWidth();
+ } else {
+ return -1;
+ }
+}
+
+int
+NotationView::getHeadersTopFrameMinWidth()
+{
+ /// TODO : use a real button width got from a real button
+
+ // 2 buttons (2 x 24) + 2 margins (2 x 4) + buttons spacing (4)
+ return 4 + 24 + 4 + 24 + 4;
+}
+
+}
+#include "NotationView.moc"
diff --git a/src/gui/editors/notation/NotationView.h b/src/gui/editors/notation/NotationView.h
new file mode 100644
index 0000000..7678f8a
--- /dev/null
+++ b/src/gui/editors/notation/NotationView.h
@@ -0,0 +1,1131 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTATIONVIEW_H_
+#define _RG_NOTATIONVIEW_H_
+
+#include "base/NotationTypes.h"
+#include "base/Track.h"
+#include "gui/general/EditView.h"
+#include "gui/general/LinedStaff.h"
+#include "gui/general/LinedStaffManager.h"
+#include "NotationProperties.h"
+#include "NotationCanvasView.h"
+#include <string>
+#include <kprocess.h>
+#include <ktempfile.h>
+#include <qmap.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <vector>
+#include "base/Event.h"
+#include "gui/general/ClefIndex.h"
+
+
+class QWidget;
+class QTimer;
+class QPaintEvent;
+class QObject;
+class QMouseEvent;
+class QLabel;
+class QCursor;
+class QCanvasItem;
+class QCanvas;
+class KProgress;
+class KComboBox;
+class KActionMenu;
+class KAction;
+
+
+namespace Rosegarden
+{
+
+class Staff;
+class Segment;
+class ScrollBoxDialog;
+class RulerScale;
+class RosegardenGUIDoc;
+class RawNoteRuler;
+class ProgressDialog;
+class ProgressBar;
+class NotePixmapFactory;
+class NotationVLayout;
+class NotationStaff;
+class NotationHLayout;
+class NotationElement;
+class NoteActionData;
+class NoteActionDataMap;
+class MarkActionData;
+class MarkActionDataMap;
+class NoteChangeActionData;
+class NoteChangeActionDataMap;
+class Key;
+class EventSelection;
+class Event;
+class Clef;
+class ChordNameRuler;
+class QDeferScrollView;
+class HeadersGroup;
+
+
+/**
+ * NotationView is a view for one or more Staff objects, each of
+ * which contains the notation data associated with a Segment.
+ * NotationView owns the Staff objects it displays.
+ *
+ * This class manages the relationship between NotationHLayout/
+ * NotationVLayout and Staff data, as well as using rendering the
+ * actual notes (using NotePixmapFactory to generate the pixmaps).
+ */
+
+class NotationView : public EditView,
+ public LinedStaffManager
+{
+ friend class NoteInserter;
+ friend class ClefInserter;
+ friend class NotationEraser;
+ friend class NotationSelectionPaster;
+ friend class LilyPondExporter;
+
+ Q_OBJECT
+
+public:
+ explicit NotationView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ QWidget *parent,
+ bool showProgressive); // update during initial render?
+
+ /**
+ * Constructor for printing only. If parent is provided, a
+ * progress dialog will be shown -- otherwise not. If another
+ * NotationView is provided, the fonts and other settings used
+ * for printing will be taken from that view.
+ */
+ explicit NotationView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ QWidget *parent,
+ NotationView *referenceView);
+
+ ~NotationView();
+
+// void initialLayout();
+
+ /// constructed successfully? (main reason it might not is user hit Cancel)
+ bool isOK() const { return m_ok; }
+
+ /**
+ * Return the view-local PropertyName definitions for this view
+ */
+ const NotationProperties &getProperties() const;
+
+ /// Return the number of staffs
+ int getStaffCount() { return m_staffs.size(); }
+
+ /// Return a pointer to the staff at the specified index
+ Staff *getStaff(int i) {
+ return getLinedStaff(i);
+ }
+
+ /// Return a pointer to the staff corresponding to the given segment
+ Staff *getStaff(const Segment &segment) {
+ return getLinedStaff(segment);
+ }
+
+ /// Return a pointer to the staff at the specified index
+ LinedStaff *getLinedStaff(int i);
+
+ /// Return a pointer to the staff corresponding to the given segment
+ LinedStaff *getLinedStaff(const Segment &segment);
+
+ /// Return a pointer to the staff at the specified index
+ NotationStaff *getNotationStaff(int i) {
+ if (i >= 0 && unsigned(i) < m_staffs.size()) return m_staffs[i];
+ else return 0;
+ }
+
+ /// Return a pointer to the staff corresponding to the given segment
+ NotationStaff *getNotationStaff(const Segment &segment);
+
+ /// Return true if the staff at the specified index is the current one
+ bool isCurrentStaff(int i);
+
+ QCanvas* canvas() { return getCanvasView()->canvas(); }
+
+ void setCanvasCursor(const QCursor &cursor) {
+ getCanvasView()->viewport()->setCursor(cursor);
+ }
+
+ void setHeightTracking(bool t) {
+ getCanvasView()->setHeightTracking(t);
+ }
+
+ /**
+ * Returns true if the view is actually for printing
+ */
+ bool isInPrintMode() { return m_printMode; }
+
+ /**
+ * Set the note or rest selected by the user from the toolbars
+ */
+ void setCurrentSelectedNote(const char *pixmapName,
+ bool isRest, Note::Type,
+ int dots = 0);
+
+ /**
+ * Set the note or rest selected by the user from the toolbars
+ */
+ void setCurrentSelectedNote(const NoteActionData &);
+
+ /**
+ * Discover whether chord-mode insertions are enabled (as opposed
+ * to the default melody-mode)
+ */
+ bool isInChordMode();
+
+ /**
+ * Discover whether triplet-mode insertions are enabled
+ */
+ bool isInTripletMode();
+
+ /**
+ * Discover whether grace-mode insertions are enabled
+ */
+ bool isInGraceMode();
+
+ /**
+ * Discover whether annotations are being displayed or not
+ */
+ bool areAnnotationsVisible() { return m_annotationsVisible; }
+
+ /**
+ * Discover whether LilyPond directives are being displayed or not
+ */
+ bool areLilyPondDirectivesVisible() { return m_lilyPondDirectivesVisible; }
+
+ /**
+ * Set the current event selection.
+ *
+ * If preview is true, sound the selection as well.
+ *
+ * If redrawNow is true, recolour the elements on the canvas;
+ * otherwise just line up a refresh for the next paint event.
+ *
+ * (If the selection has changed as part of a modification to a
+ * segment, redrawNow should be unnecessary and undesirable, as a
+ * paint event will occur in the next event loop following the
+ * command invocation anyway.)
+ */
+ virtual void setCurrentSelection(EventSelection*,
+ bool preview = false,
+ bool redrawNow = false);
+
+ /**
+ * Set the current event selection to a single event
+ */
+ void setSingleSelectedEvent(int staffNo,
+ Event *event,
+ bool preview = false,
+ bool redrawNow = false);
+
+ /**
+ * Set the current event selection to a single event
+ */
+ void setSingleSelectedEvent(Segment &segment,
+ Event *event,
+ bool preview = false,
+ bool redrawNow = false);
+
+ /**
+ * Show and sound the given note. The height is used for display,
+ * the pitch for performance, so the two need not correspond (e.g.
+ * under ottava there may be octave differences).
+ */
+ void showPreviewNote(int staffNo, double layoutX,
+ int pitch, int height,
+ const Note &note,
+ bool grace,
+ int velocity = -1);
+
+ /// Remove any visible preview note
+ void clearPreviewNote();
+
+ /// Sound the given note
+ void playNote(Segment &segment, int pitch, int velocity = -1);
+
+ /// Switches between page- and linear- layout modes
+ void setPageMode(LinedStaff::PageMode mode);
+
+ /// Returns the page width according to the layout mode (page/linear)
+ int getPageWidth();
+
+ /// Returns the page height according to the layout mode (page/linear)
+ int getPageHeight();
+
+ /// Returns the margins within the page (zero if not in MultiPageMode)
+ void getPageMargins(int &left, int &top);
+
+ /// Scrolls the view such that the given time is centered
+ void scrollToTime(timeT t);
+
+ NotePixmapFactory *getNotePixmapFactory() const {
+ return m_notePixmapFactory;
+ }
+
+ virtual void refreshSegment(Segment *segment,
+ timeT startTime = 0,
+ timeT endTime = 0);
+
+ /**
+ * From LinedStaffManager
+ */
+ virtual LinedStaff* getStaffForCanvasCoords(int x, int y) const;
+
+
+ /**
+ * Overridden from EditView
+ */
+ virtual void updateView();
+
+ /**
+ * Render segments on printing painter. This uses the current
+ * font size and layout, rather than the optimal ones for the
+ * printer configuration (notation editing is not quite WYSIWYG,
+ * and we may be in a non-page mode).
+ *
+ * To print optimally use slotFilePrint, which will create
+ * another NotationView with the optimal settings and call print
+ * on that.
+ */
+ virtual void print(bool previewOnly = false);
+
+ /**
+ * Return X of the left of the canvas visible part.
+ */
+ double getCanvasLeftX() { return getCanvasView()->contentsX(); }
+
+ virtual RulerScale* getHLayout();
+
+ /**
+ * Return the notation window width
+ */
+ int getCanvasVisibleWidth();
+
+ /**
+ * Return the minimal width which shall be allocated to
+ * the track headers top frame.
+ * (The width of the close button + the width of an info
+ * button still to come).
+ */
+ int getHeadersTopFrameMinWidth();
+
+public slots:
+
+ /**
+ * Print the current set of segments, by creating another
+ * NotationView with the printing configuration but the same
+ * segments, font etc as this view and asking it to print.
+ */
+ void slotFilePrint();
+
+ /**
+ * Preview the current set of segments, by creating another
+ * NotationView with the printing configuration but the same
+ * segments, font etc as this view and asking it to preview.
+ */
+ void slotFilePrintPreview();
+
+ /**
+ * export a LilyPond file
+ */
+ bool exportLilyPondFile(QString url, bool forPreview = false);
+
+ /**
+ * Export to a temporary file and process
+ */
+ void slotPrintLilyPond();
+ void slotPreviewLilyPond();
+ void slotLilyPondViewProcessExited(KProcess *);
+
+ /**
+ * put the marked text/object into the clipboard and remove it
+ * from the document
+ */
+ void slotEditCut();
+
+ /**
+ * put the marked text/object into the clipboard
+ */
+ void slotEditCopy();
+
+ /**
+ * paste the clipboard into the document
+ */
+ void slotEditPaste();
+
+ /**
+ * cut the selection and close the gap, moving subsequent events
+ * towards the start of the segment
+ */
+ void slotEditCutAndClose();
+
+ /**
+ * paste the clipboard into the document, offering a choice for how
+ */
+ void slotEditGeneralPaste();
+
+ /**
+ * delete the selection (cut without the copy)
+ */
+ void slotEditDelete();
+
+ /**
+ * move the selection to the staff above
+ */
+ void slotMoveEventsUpStaff();
+
+ /**
+ * move the selection to the staff below
+ */
+ void slotMoveEventsDownStaff();
+
+ /**
+ * toggles the tools toolbar
+ */
+ void slotToggleToolsToolBar();
+
+ /**
+ * toggles the notes toolbar
+ */
+ void slotToggleNotesToolBar();
+
+ /**
+ * toggles the rests toolbar
+ */
+ void slotToggleRestsToolBar();
+
+ /**
+ * toggles the accidentals toolbar
+ */
+ void slotToggleAccidentalsToolBar();
+
+ /**
+ * toggles the clefs toolbar
+ */
+ void slotToggleClefsToolBar();
+
+ /**
+ * toggles the marks toolbar
+ */
+ void slotToggleMarksToolBar();
+
+ /**
+ * toggles the group toolbar
+ */
+ void slotToggleGroupToolBar();
+
+ /**
+ * toggles the layout toolbar
+ */
+ void slotToggleLayoutToolBar();
+
+ /**
+ * toggles the transport toolbar
+ */
+ void slotToggleTransportToolBar();
+
+ /**
+ * toggles the meta toolbar
+ */
+ void slotToggleMetaToolBar();
+
+ /// note switch slot
+ void slotNoteAction();
+
+ /// switch to last selected note
+ void slotLastNoteAction();
+
+ /// accidental switch slots
+ void slotNoAccidental();
+ void slotFollowAccidental();
+ void slotSharp();
+ void slotFlat();
+ void slotNatural();
+ void slotDoubleSharp();
+ void slotDoubleFlat();
+
+ /// clef switch slots
+ void slotTrebleClef();
+ void slotAltoClef();
+ void slotTenorClef();
+ void slotBassClef();
+
+ /// text tool
+ void slotText();
+
+ /// guitar chord tool
+ void slotGuitarChord();
+
+ /// editing tools
+ void slotEraseSelected();
+ void slotSelectSelected();
+
+ void slotToggleStepByStep();
+
+ /// status stuff
+ void slotUpdateInsertModeStatus();
+ void slotUpdateAnnotationsStatus();
+ void slotUpdateLilyPondDirectivesStatus();
+
+ /// edit menu
+ void slotPreviewSelection();
+ void slotClearLoop();
+ void slotClearSelection();
+ void slotEditSelectFromStart();
+ void slotEditSelectToEnd();
+ void slotEditSelectWholeStaff();
+ void slotFilterSelection();
+
+ /// view menu
+ void slotLinearMode();
+ void slotContinuousPageMode();
+ void slotMultiPageMode();
+ void slotToggleChordsRuler();
+ void slotToggleRawNoteRuler();
+ void slotToggleTempoRuler();
+ void slotToggleAnnotations();
+ void slotToggleLilyPondDirectives();
+ void slotEditLyrics();
+
+ /// Notation header slots
+ void slotShowHeadersGroup();
+ void slotHideHeadersGroup();
+ void slotVerticalScrollHeadersGroup(int);
+ void slotUpdateHeaders(int x, int y);
+ void slotHeadersWidthChanged(int w);
+
+ /// Adjust notation header view when bottom ruler added or removed
+ void slotCanvasBottomWidgetHeightChanged(int);
+
+ /// group slots
+ void slotGroupBeam();
+ void slotGroupAutoBeam();
+ void slotGroupBreak();
+ void slotGroupSimpleTuplet();
+ void slotGroupGeneralTuplet();
+ void slotGroupTuplet(bool simple);
+ void slotGroupUnTuplet();
+ void slotGroupSlur();
+ void slotGroupPhrasingSlur();
+ void slotGroupGlissando();
+ void slotGroupCrescendo();
+ void slotGroupDecrescendo();
+ void slotGroupMakeChord();
+ void slotGroupOctave2Up();
+ void slotGroupOctaveUp();
+ void slotGroupOctaveDown();
+ void slotGroupOctave2Down();
+ void slotAddIndication(std::string type, QString cat);
+
+ /// transforms slots
+ void slotTransformsNormalizeRests();
+ void slotTransformsCollapseRests();
+ void slotTransformsCollapseNotes();
+ void slotTransformsTieNotes();
+ void slotTransformsUntieNotes();
+ void slotTransformsMakeNotesViable();
+ void slotTransformsDeCounterpoint();
+ void slotTransformsStemsUp();
+ void slotTransformsStemsDown();
+ void slotTransformsRestoreStems();
+ void slotTransformsSlursAbove();
+ void slotTransformsSlursBelow();
+ void slotTransformsRestoreSlurs();
+ void slotTransformsTiesAbove();
+ void slotTransformsTiesBelow();
+ void slotTransformsRestoreTies();
+ void slotTransformsQuantize();
+ void slotTransformsFixQuantization();
+ void slotTransformsRemoveQuantization();
+ void slotTransformsInterpret();
+
+ void slotRespellDoubleFlat();
+ void slotRespellFlat();
+ void slotRespellNatural();
+ void slotRespellSharp();
+ void slotRespellDoubleSharp();
+ void slotRespellUp();
+ void slotRespellDown();
+ void slotRespellRestore();
+ void slotShowCautionary();
+ void slotCancelCautionary();
+
+ void slotSetStyleFromAction();
+ void slotInsertNoteFromAction();
+ void slotInsertRest();
+ void slotSwitchFromRestToNote();
+ void slotSwitchFromNoteToRest();
+ void slotToggleDot();
+
+ void slotAddMark();
+ void slotMarksAddTextMark();
+ void slotMarksAddFingeringMark();
+ void slotMarksAddFingeringMarkFromAction();
+ void slotMarksRemoveMarks();
+ void slotMarksRemoveFingeringMarks();
+ void slotMakeOrnament();
+ void slotUseOrnament();
+ void slotRemoveOrnament();
+
+ void slotNoteChangeAction();
+ void slotSetNoteDurations(Note::Type, bool notationOnly);
+ void slotAddDot();
+ void slotAddDotNotationOnly();
+
+ void slotAddSlashes();
+
+ void slotEditAddClef();
+ void slotEditAddKeySignature();
+ void slotEditAddSustainDown();
+ void slotEditAddSustainUp();
+ void slotEditAddSustain(bool down);
+ void slotEditTranspose();
+ void slotEditSwitchPreset();
+ void slotEditElement(NotationStaff *, NotationElement *, bool advanced);
+
+ void slotFinePositionLeft();
+ void slotFinePositionRight();
+ void slotFinePositionUp();
+ void slotFinePositionDown();
+ void slotFinePositionRestore();
+
+ void slotMakeVisible();
+ void slotMakeInvisible();
+
+ void slotDebugDump();
+
+ /// Canvas actions slots
+
+ /**
+ * Called when a mouse press occurred on a notation element
+ * or somewhere on a staff
+ */
+ void slotItemPressed(int height, int staffNo, QMouseEvent*, NotationElement*);
+
+ /**
+ * Called when a mouse press occurred on a non-notation element
+ */
+ void slotNonNotationItemPressed(QMouseEvent *e, QCanvasItem *i);
+
+ /**
+ * Called when a mouse press occurred on a QCanvasText
+ */
+ void slotTextItemPressed(QMouseEvent *e, QCanvasItem *i);
+
+ void slotMouseMoved(QMouseEvent*);
+ void slotMouseReleased(QMouseEvent*);
+
+ /**
+ * Called when the mouse cursor moves over a different height on
+ * the staff
+ *
+ * @see NotationCanvasView#hoveredOverNoteChange()
+ */
+ void slotHoveredOverNoteChanged(const QString&);
+
+ /**
+ * Called when the mouse cursor moves over a note which is at a
+ * different time on the staff
+ *
+ * @see NotationCanvasView#hoveredOverAbsoluteTimeChange()
+ */
+ void slotHoveredOverAbsoluteTimeChanged(unsigned int);
+
+ /**
+ * Set the time pointer position during playback (purely visual,
+ * doesn't affect playback). This is also at liberty to highlight
+ * some notes, if it so desires...
+ */
+ void slotSetPointerPosition(timeT position);
+
+ /**
+ * As above, but with the ability to specify whether to scroll or
+ * not to follow the pointer (above method uses the play tracking
+ * setting to determine that)
+ */
+ void slotSetPointerPosition(timeT position, bool scroll);
+
+ /**
+ * Update the recording segment if it's one of the ones in the
+ * view
+ */
+ void slotUpdateRecordingSegment(Segment *recordingSegment,
+ timeT updatedFrom);
+
+ /// Set the current staff to the one containing the given canvas Y coord
+ void slotSetCurrentStaff(double canvasX, int canvasY);
+
+ /// Set the current staff to that with the given id
+ void slotSetCurrentStaff(int staffNo);
+
+ /**
+ * Set the insert cursor position (from the top LoopRuler).
+ * If the segment has recently been changed and no refresh has
+ * occurred since, pass updateNow false; then the move will
+ * happen on the next update.
+ */
+ void slotSetInsertCursorPosition(timeT position,
+ bool scroll, bool updateNow);
+
+ virtual void slotSetInsertCursorPosition(timeT position) {
+ slotSetInsertCursorPosition(position, true, true);
+ }
+
+ /// Set the insert cursor position from a mouse event location
+ void slotSetInsertCursorPosition(double canvasX, int canvasY,
+ bool scroll, bool updateNow);
+
+ void slotSetInsertCursorPosition(double canvasX, int canvasY) {
+ slotSetInsertCursorPosition(canvasX, canvasY, true, true);
+ }
+
+ /**
+ * Set the insert cursor position and scroll so it's at given point.
+ * If the segment has recently been changed and no refresh has
+ * occurred since, pass updateNow false; then the move will
+ * happen on the next update.
+ */
+ void slotSetInsertCursorAndRecentre(timeT position,
+ double cx, int cy,
+ bool updateNow = true);
+
+ void slotSetInsertCursorAndRecentre(timeT position,
+ double cx, double cy) {
+ slotSetInsertCursorAndRecentre(position, cx, static_cast<int>(cy), true);
+ }
+
+ /// Set insert cursor to playback pointer position
+ void slotJumpCursorToPlayback();
+
+ /// Set playback pointer to insert cursor position (affects playback)
+ void slotJumpPlaybackToCursor();
+
+ /// Toggle tracking with the position pointer during playback
+ void slotToggleTracking();
+
+ /// Change the current staff to the one preceding the current one
+ void slotCurrentStaffUp();
+
+ /// Change the current staff to the one following the current one
+ void slotCurrentStaffDown();
+
+ /// Change the current segment to the one following the current one
+ void slotCurrentSegmentPrior();
+
+ /// Change the current segment to the one preceding the current one
+ void slotCurrentSegmentNext();
+
+ /// Changes the font of the staffs on the view, gets font name from sender
+ void slotChangeFontFromAction();
+
+ /// Changes the font of the staffs on the view
+ void slotChangeFont(std::string newFont);
+
+ /// Changes the font and font size of the staffs on the view
+ void slotChangeFont(std::string newFont, int newSize);
+
+ /// Changes the font of the staffs on the view
+ void slotChangeFont(const QString &newFont);
+
+ /// Changes the font size of the staffs on the view
+ void slotChangeFontSize(int newSize);
+
+ /// Changes the font size of the staffs on the view, gets size from sender
+ void slotChangeFontSizeFromAction();
+
+ /// Changes the font size of the staffs on the view to the nth size in the available size list
+ void slotChangeFontSizeFromStringValue(const QString&);
+
+ /// Changes to the next font size up
+ void slotZoomIn();
+
+ /// Changes to the next font size down
+ void slotZoomOut();
+
+ /// Changes the hlayout spacing of the staffs on the view
+ void slotChangeSpacing(int newSpacing);
+
+ /// Changes the hlayout spacing of the staffs on the view
+ void slotChangeSpacingFromStringValue(const QString&);
+
+ /// Changes the hlayout spacing of the staffs on the view
+ void slotChangeSpacingFromAction();
+
+ /// Changes the hlayout proportion of the staffs on the view
+ void slotChangeProportion(int newProportion);
+
+ /// Changes the hlayout proportion of the staffs on the view
+ void slotChangeProportionFromIndex(int newProportionIndex);
+
+ /// Changes the hlayout proportion of the staffs on the view
+ void slotChangeProportionFromAction();
+
+ /// Note-on received asynchronously -- consider step-by-step editing
+ void slotInsertableNoteOnReceived(int pitch, int velocity);
+
+ /// Note-off received asynchronously -- consider step-by-step editing
+ void slotInsertableNoteOffReceived(int pitch, int velocity);
+
+ /// Note-on or note-off received asynchronously -- as above
+ void slotInsertableNoteEventReceived(int pitch, int velocity, bool noteOn);
+
+ /// A timer set when a note-on event was received has elapsed
+ void slotInsertableTimerElapsed();
+
+ /// The given QObject has originated a step-by-step-editing request
+ void slotStepByStepTargetRequested(QObject *);
+
+ /// Do on-demand rendering for a region.
+ void slotCheckRendered(double cx0, double cx1);
+
+ /// Do some background rendering work.
+ void slotRenderSomething();
+
+ void slotSetOperationNameAndStatus(QString);
+
+ // Update notation view based on track/staff name change
+ void slotUpdateStaffName();
+
+ // LilyPond Directive slots
+ void slotBeginLilyPondRepeat();
+
+signals:
+ /**
+ * Emitted when the note selected in the palette changes
+ */
+ void changeCurrentNote(bool isRest, Note::Type);
+
+ /**
+ * Emitted when a new accidental has been choosen by the user
+ */
+ void changeAccidental(Accidental, bool follow);
+
+ /**
+ * Emitted when the selection has been cut or copied
+ *
+ * @see NotationSelector#hideSelection
+ */
+ void usedSelection();
+
+ void play();
+ void stop();
+ void fastForwardPlayback();
+ void rewindPlayback();
+ void fastForwardPlaybackToEnd();
+ void rewindPlaybackToBeginning();
+ void jumpPlaybackTo(timeT);
+ void panic();
+
+ /// progress Report
+ void setProgress(int);
+ void incrementProgress(int);
+ void setOperationName(QString);
+
+ void stepByStepTargetRequested(QObject *);
+
+ void renderComplete();
+
+ void editTimeSignature(timeT);
+
+ void editMetadata(QString);
+
+ void editTriggerSegment(int);
+
+ void staffLabelChanged(TrackId id, QString label);
+
+protected:
+
+ virtual void paintEvent(QPaintEvent* e);
+
+ /**
+ * init the action maps for notes, marks etc
+ */
+ void initActionDataMaps();
+
+protected slots:
+ /**
+ * save general Options like all bar positions and status as well
+ * as the geometry and the recent file list to the configuration
+ * file
+ */
+ virtual void slotSaveOptions();
+
+protected:
+
+ /**
+ * read general Options again and initialize all variables like the recent file list
+ */
+ virtual void readOptions();
+
+ void setOneToolbar(const char *actionName,
+ const char *toolbarName);
+
+ /**
+ * create menus and toolbars
+ */
+ virtual void setupActions();
+
+ /**
+ * create or re-initialise (after font change) the font size menu
+ */
+ virtual void setupFontSizeMenu(std::string oldFontName = "");
+
+ /**
+ * Set KDE3+ menu states based on the current selection
+ */
+ virtual void setMenuStates();
+
+ /**
+ * setup status bar
+ */
+ virtual void initStatusBar();
+
+ /**
+ * Place the staffs at the correct x & y coordinates (before layout)
+ */
+ void positionStaffs();
+
+ /**
+ * Place the page pixmaps (if any) at the correct x & y
+ * coordinates (after layout)
+ */
+ void positionPages();
+
+ /**
+ * Update the panner thumbnail images. If complete is true,
+ * copy the entire mini-canvas.
+ */
+ void updateThumbnails(bool complete);
+
+ /**
+ * setup the layout/font toolbar
+ */
+ void initLayoutToolbar();
+
+ /**
+ * Helper function to toggle a toolbar given its name
+ * If \a force point to a bool, then the bool's value
+ * is used to show/hide the toolbar.
+ */
+ void toggleNamedToolBar(const QString& toolBarName, bool* force = 0);
+
+ /// Calls all the relevant preparse and layout methods
+ virtual bool applyLayout(int staffNo = -1,
+ timeT startTime = 0,
+ timeT endTime = 0);
+
+ /**
+ * Readjust the size of the canvas after a layout
+ *
+ * Checks the total width computed by the horizontal layout
+ *
+ * @see NotationHLayout#getTotalWidth()
+ */
+ void readjustCanvasSize();
+
+ /**
+ * Override from EditView
+ * @see EditView#getViewSize
+ */
+ virtual QSize getViewSize();
+
+ /**
+ * Override from EditView
+ * @see EditView#setViewSize
+ */
+ virtual void setViewSize(QSize);
+
+ /**
+ * Set the note pixmap factory
+ *
+ * The previous pixmap factory is deleted
+ */
+ void setNotePixmapFactory(NotePixmapFactory*);
+
+ virtual NotationCanvasView* getCanvasView();
+
+ virtual Segment *getCurrentSegment();
+ virtual Staff *getCurrentStaff() { return getCurrentLinedStaff(); }
+ virtual LinedStaff *getCurrentLinedStaff();
+
+ virtual LinedStaff *getStaffAbove();
+ virtual LinedStaff *getStaffBelow();
+
+ virtual bool hasSegment(Segment *segment);
+
+ /**
+ * Return the time at which the insert cursor may be found.
+ */
+ virtual timeT getInsertionTime();
+
+ /**
+ * Return the time at which the insert cursor may be found,
+ * and the time signature, clef and key at that time.
+ */
+ virtual timeT getInsertionTime(Clef &clef,
+ Rosegarden::Key &key);
+
+ void doDeferredCursorMove();
+
+ void removeViewLocalProperties(Event*);
+
+ void setupProgress(KProgress*);
+ void setupProgress(ProgressDialog*);
+ void setupDefaultProgress();
+ void disconnectProgress();
+
+ /**
+ * Test whether we've had too many preview notes recently
+ */
+ bool canPreviewAnotherNote();
+
+ virtual void updateViewCaption();
+
+ void showHeadersGroup();
+ void hideHeadersGroup();
+
+
+ //--------------- Data members ---------------------------------
+
+ NotationProperties m_properties;
+
+ /// Displayed in the status bar, shows number of events selected
+ QLabel *m_selectionCounter;
+
+ /// Displayed in the status bar, shows insertion mode
+ QLabel *m_insertModeLabel;
+
+ /// Displayed in the status bar, shows when annotations are hidden
+ QLabel *m_annotationsLabel;
+
+ /// Displayed in the status bar, shows when LilyPond directives are hidden
+ QLabel *m_lilyPondDirectivesLabel;
+
+ /// Displayed in the status bar, shows progress of current operation
+ ProgressBar *m_progressBar;
+
+ /// Displayed in the status bar, holds the pixmap of the current note
+ QLabel* m_currentNotePixmap;
+
+ /// Displayed in the status bar, shows the pitch the cursor is at
+ QLabel* m_hoveredOverNoteName;
+
+ /// Displayed in the status bar, shows the absolute time the cursor is at
+ QLabel* m_hoveredOverAbsoluteTime;
+
+ std::vector<NotationStaff*> m_staffs;
+ int m_currentStaff;
+ int m_lastFinishingStaff;
+
+ QCanvasItem *m_title;
+ QCanvasItem *m_subtitle;
+ QCanvasItem *m_composer;
+ QCanvasItem *m_copyright;
+ std::vector<QCanvasItem *> m_pages;
+ std::vector<QCanvasItem *> m_pageNumbers;
+
+ timeT m_insertionTime;
+ enum DeferredCursorMoveType {
+ NoCursorMoveNeeded,
+ CursorMoveOnly,
+ CursorMoveAndMakeVisible,
+ CursorMoveAndScrollToPosition
+ };
+ DeferredCursorMoveType m_deferredCursorMove;
+ double m_deferredCursorScrollToX;
+
+ QString m_lastNoteAction;
+
+ std::string m_fontName;
+ int m_fontSize;
+ LinedStaff::PageMode m_pageMode;
+ int m_leftGutter;
+
+ NotePixmapFactory *m_notePixmapFactory;
+
+ NotationHLayout* m_hlayout;
+ NotationVLayout* m_vlayout;
+
+ ChordNameRuler *m_chordNameRuler;
+ QWidget *m_tempoRuler;
+ RawNoteRuler *m_rawNoteRuler;
+ bool m_annotationsVisible;
+ bool m_lilyPondDirectivesVisible;
+
+ KAction* m_selectDefaultNote;
+
+ typedef QMap<QString, NoteActionData *> NoteActionDataMap;
+ static NoteActionDataMap* m_noteActionDataMap;
+
+ typedef QMap<QString, NoteChangeActionData *> NoteChangeActionDataMap;
+ static NoteChangeActionDataMap* m_noteChangeActionDataMap;
+
+ typedef QMap<QString, MarkActionData *> MarkActionDataMap;
+ static MarkActionDataMap *m_markActionDataMap;
+
+ KComboBox *m_fontCombo;
+ KComboBox *m_fontSizeCombo;
+ KComboBox *m_spacingCombo;
+ KActionMenu *m_fontSizeActionMenu;
+ ScrollBoxDialog *m_pannerDialog;
+ QTimer *m_renderTimer;
+
+ bool m_playTracking;
+
+ std::vector<std::pair<int, int> > m_pendingInsertableNotes;
+
+ enum { PROGRESS_NONE,
+ PROGRESS_BAR,
+ PROGRESS_DIALOG } m_progressDisplayer;
+
+ bool m_inhibitRefresh;
+ bool m_ok;
+
+ bool m_printMode;
+ int m_printSize;
+
+ static std::map<KProcess *, KTempFile *> m_lilyTempFileMap;
+
+ int m_showHeadersGroup;
+ QDeferScrollView * m_headersGroupView;
+ HeadersGroup * m_headersGroup;
+ QFrame * m_headersTopFrame;
+
+ KAction * m_showHeadersMenuEntry;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteCharacter.cpp b/src/gui/editors/notation/NoteCharacter.cpp
new file mode 100644
index 0000000..fdcb578
--- /dev/null
+++ b/src/gui/editors/notation/NoteCharacter.cpp
@@ -0,0 +1,133 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NoteCharacter.h"
+
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qcanvas.h>
+#include <qbitmap.h>
+
+
+namespace Rosegarden
+{
+
+NoteCharacter::NoteCharacter() :
+ m_hotspot(0, 0),
+ m_pixmap(new QPixmap()),
+ m_rep(0)
+{}
+
+NoteCharacter::NoteCharacter(QPixmap pixmap,
+ QPoint hotspot, NoteCharacterDrawRep *rep) :
+ m_hotspot(hotspot),
+ m_pixmap(new QPixmap(pixmap)),
+ m_rep(rep)
+{}
+
+NoteCharacter::NoteCharacter(const NoteCharacter &c) :
+ m_hotspot(c.m_hotspot),
+ m_pixmap(new QPixmap(*c.m_pixmap)),
+ m_rep(c.m_rep)
+{
+ // nothing else
+}
+
+NoteCharacter &
+NoteCharacter::operator=(const NoteCharacter &c)
+{
+ if (&c == this)
+ return * this;
+ m_hotspot = c.m_hotspot;
+ m_pixmap = new QPixmap(*c.m_pixmap);
+ m_rep = c.m_rep;
+ return *this;
+}
+
+NoteCharacter::~NoteCharacter()
+{
+ delete m_pixmap;
+}
+
+int
+NoteCharacter::getWidth() const
+{
+ return m_pixmap->width();
+}
+
+int
+NoteCharacter::getHeight() const
+{
+ return m_pixmap->height();
+}
+
+QPoint
+NoteCharacter::getHotspot() const
+{
+ return m_hotspot;
+}
+
+QPixmap *
+NoteCharacter::getPixmap() const
+{
+ return m_pixmap;
+}
+
+QCanvasPixmap *
+NoteCharacter::getCanvasPixmap() const
+{
+ return new QCanvasPixmap(*m_pixmap, m_hotspot);
+}
+
+void
+NoteCharacter::draw(QPainter *painter, int x, int y) const
+{
+ if (!m_rep) {
+
+ painter->drawPixmap(x, y, *m_pixmap);
+
+ } else {
+
+ NoteCharacterDrawRep a(m_rep->size());
+
+ for (unsigned int i = 0; i < m_rep->size(); ++i) {
+ QPoint p(m_rep->point(i));
+ a.setPoint(i, p.x() + x, p.y() + y);
+ }
+
+ painter->drawLineSegments(a);
+ }
+}
+
+void
+NoteCharacter::drawMask(QPainter *painter, int x, int y) const
+{
+ if (!m_rep && m_pixmap->mask()) {
+ painter->drawPixmap(x, y, *(m_pixmap->mask()));
+ }
+}
+
+}
diff --git a/src/gui/editors/notation/NoteCharacter.h b/src/gui/editors/notation/NoteCharacter.h
new file mode 100644
index 0000000..bc9359e
--- /dev/null
+++ b/src/gui/editors/notation/NoteCharacter.h
@@ -0,0 +1,93 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTECHARACTER_H_
+#define _RG_NOTECHARACTER_H_
+
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qpointarray.h>
+
+
+class QPainter;
+class QCanvasPixmap;
+
+namespace Rosegarden
+{
+
+class NoteCharacterDrawRep : public QPointArray
+{
+public:
+ NoteCharacterDrawRep(int size = 0) : QPointArray(size) { }
+};
+
+
+/**
+ * NoteCharacter knows how to draw a character from a font. It may be
+ * optimised for screen (using QPixmap underneath to produce
+ * low-resolution colour or greyscale glyphs) or printer (using some
+ * internal representation to draw in high-resolution monochrome on a
+ * print device). You can use screen characters on a printer and vice
+ * versa, but the performance and quality might not be as good.
+ *
+ * NoteCharacter objects are always constructed by the NoteFont, never
+ * directly.
+ */
+
+class NoteCharacter
+{
+public:
+ NoteCharacter();
+ NoteCharacter(const NoteCharacter &);
+ NoteCharacter &operator=(const NoteCharacter &);
+ ~NoteCharacter();
+
+ int getWidth() const;
+ int getHeight() const;
+
+ QPoint getHotspot() const;
+
+ QPixmap *getPixmap() const;
+ QCanvasPixmap *getCanvasPixmap() const;
+
+ void draw(QPainter *painter, int x, int y) const;
+ void drawMask(QPainter *painter, int x, int y) const;
+
+private:
+ friend class NoteFont;
+ NoteCharacter(QPixmap pixmap, QPoint hotspot, NoteCharacterDrawRep *rep);
+
+ QPoint m_hotspot;
+ QPixmap *m_pixmap; // I own this
+ NoteCharacterDrawRep *m_rep; // I don't own this, it's a reference to a static in the NoteFont
+};
+
+
+// Encapsulates NoteFontMap, and loads pixmaps etc on demand
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteCharacterNames.cpp b/src/gui/editors/notation/NoteCharacterNames.cpp
new file mode 100644
index 0000000..bcd450c
--- /dev/null
+++ b/src/gui/editors/notation/NoteCharacterNames.cpp
@@ -0,0 +1,123 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral right of the authors to claim authorship of this work
+ has been asserted.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "NoteCharacterNames.h"
+
+namespace Rosegarden
+{
+
+namespace NoteCharacterNames
+{
+
+const CharName SHARP = "MUSIC SHARP SIGN";
+const CharName FLAT = "MUSIC FLAT SIGN";
+const CharName NATURAL = "MUSIC NATURAL SIGN";
+const CharName DOUBLE_SHARP = "MUSICAL SYMBOL DOUBLE SHARP";
+const CharName DOUBLE_FLAT = "MUSICAL SYMBOL DOUBLE FLAT";
+
+const CharName BREVE = "MUSICAL SYMBOL BREVE";
+const CharName WHOLE_NOTE = "MUSICAL SYMBOL WHOLE NOTE";
+const CharName VOID_NOTEHEAD = "MUSICAL SYMBOL VOID NOTEHEAD";
+const CharName NOTEHEAD_BLACK = "MUSICAL SYMBOL NOTEHEAD BLACK";
+
+const CharName X_NOTEHEAD = "MUSICAL SYMBOL X NOTEHEAD";
+const CharName CIRCLE_X_NOTEHEAD = "MUSICAL SYMBOL CIRCLE X NOTEHEAD";
+const CharName BREVIS = "MUSICAL SYMBOL BREVIS";
+const CharName SEMIBREVIS_WHITE = "MUSICAL SYMBOL SEMIBREVIS WHITE";
+const CharName SEMIBREVIS_BLACK = "MUSICAL SYMBOL SEMIBREVIS BLACK";
+const CharName TRIANGLE_NOTEHEAD_UP_WHITE = "MUSICAL SYMBOL TRIANGLE NOTEHEAD UP WHITE";
+const CharName TRIANGLE_NOTEHEAD_UP_BLACK = "MUSICAL SYMBOL TRIANGLE NOTEHEAD UP BLACK";
+const CharName SQUARE_NOTEHEAD_WHITE = "MUSICAL SYMBOL SQUARE NOTEHEAD WHITE";
+const CharName SQUARE_NOTEHEAD_BLACK = "MUSICAL SYMBOL SQUARE NOTEHEAD BLACK";
+
+// These two names are not valid Unicode names. They describe flags
+// that should be used to compose multi-flag notes, rather than used
+// on their own. Unicode has no code point for these, but they're
+// common in real fonts. COMBINING PARTIAL FLAG is a flag that may be
+// drawn several times to make a multi-flag note; COMBINING PARTIAL
+// FLAG FINAL may be used as the flag nearest the note head and may
+// have an additional swash. (In many fonts, the FLAG 1 character may
+// also be suitable for use as PARTIAL FLAG FINAL).
+const CharName FLAG_PARTIAL = "MUSICAL SYMBOL COMBINING PARTIAL FLAG";
+const CharName FLAG_PARTIAL_FINAL = "MUSICAL SYMBOL COMBINING PARTIAL FLAG FINAL";
+
+const CharName FLAG_1 = "MUSICAL SYMBOL COMBINING FLAG-1";
+const CharName FLAG_2 = "MUSICAL SYMBOL COMBINING FLAG-2";
+const CharName FLAG_3 = "MUSICAL SYMBOL COMBINING FLAG-3";
+const CharName FLAG_4 = "MUSICAL SYMBOL COMBINING FLAG-4";
+
+const CharName MULTI_REST = "MUSICAL SYMBOL MULTI REST"; // Unicode-4 glyph 1D13A
+const CharName MULTI_REST_ON_STAFF = "MUSICAL SYMBOL MULTI REST ON STAFF";
+const CharName WHOLE_REST = "MUSICAL SYMBOL WHOLE REST"; // Unicode-4 glyph 1D13B
+const CharName WHOLE_REST_ON_STAFF = "MUSICAL SYMBOL WHOLE REST ON STAFF";
+const CharName HALF_REST = "MUSICAL SYMBOL HALF REST"; // Unicode-4 glyph 1D13C
+const CharName HALF_REST_ON_STAFF = "MUSICAL SYMBOL HALF REST ON STAFF";
+const CharName QUARTER_REST = "MUSICAL SYMBOL QUARTER REST";
+const CharName EIGHTH_REST = "MUSICAL SYMBOL EIGHTH REST";
+const CharName SIXTEENTH_REST = "MUSICAL SYMBOL SIXTEENTH REST";
+const CharName THIRTY_SECOND_REST = "MUSICAL SYMBOL THIRTY-SECOND REST";
+const CharName SIXTY_FOURTH_REST = "MUSICAL SYMBOL SIXTY-FOURTH REST";
+
+const CharName DOT = "MUSICAL SYMBOL COMBINING AUGMENTATION DOT";
+
+const CharName ACCENT = "MUSICAL SYMBOL COMBINING ACCENT";
+const CharName TENUTO = "MUSICAL SYMBOL COMBINING TENUTO";
+const CharName STACCATO = "MUSICAL SYMBOL COMBINING STACCATO";
+const CharName STACCATISSIMO = "MUSICAL SYMBOL COMBINING STACCATISSIMO";
+const CharName MARCATO = "MUSICAL SYMBOL COMBINING MARCATO";
+const CharName FERMATA = "MUSICAL SYMBOL FERMATA";
+const CharName TRILL = "MUSICAL SYMBOL TR";
+const CharName TRILL_LINE = "MUSICAL SYMBOL COMBINING TRILL LINE";
+const CharName TURN = "MUSICAL SYMBOL TURN";
+
+const CharName MORDENT = "MUSICAL SYMBOL MORDENT";
+const CharName MORDENT_INVERTED = "MUSICAL SYMBOL INVERTED MORDENT";
+const CharName MORDENT_LONG = "MUSICAL SYMBOL LONG MORDENT";
+const CharName MORDENT_LONG_INVERTED = "MUSICAL SYMBOL LONG INVERTED MORDENT";
+
+const CharName PEDAL_MARK = "MUSICAL SYMBOL PEDAL MARK";
+const CharName PEDAL_UP_MARK = "MUSICAL SYMBOL PEDAL UP MARK";
+
+const CharName UP_BOW = "MUSICAL SYMBOL COMBINING UP BOW";
+const CharName DOWN_BOW = "MUSICAL SYMBOL COMBINING DOWN BOW";
+
+const CharName C_CLEF = "MUSICAL SYMBOL C CLEF";
+const CharName G_CLEF = "MUSICAL SYMBOL G CLEF";
+const CharName F_CLEF = "MUSICAL SYMBOL F CLEF";
+
+const CharName COMMON_TIME = "MUSICAL SYMBOL COMMON TIME";
+const CharName CUT_TIME = "MUSICAL SYMBOL CUT TIME";
+const CharName DIGIT_ZERO = "DIGIT ZERO";
+const CharName DIGIT_ONE = "DIGIT ONE";
+const CharName DIGIT_TWO = "DIGIT TWO";
+const CharName DIGIT_THREE = "DIGIT THREE";
+const CharName DIGIT_FOUR = "DIGIT FOUR";
+const CharName DIGIT_FIVE = "DIGIT FIVE";
+const CharName DIGIT_SIX = "DIGIT SIX";
+const CharName DIGIT_SEVEN = "DIGIT SEVEN";
+const CharName DIGIT_EIGHT = "DIGIT EIGHT";
+const CharName DIGIT_NINE = "DIGIT NINE";
+
+const CharName UNKNOWN = "__UNKNOWN__";
+
+}
+
+}
diff --git a/src/gui/editors/notation/NoteCharacterNames.h b/src/gui/editors/notation/NoteCharacterNames.h
new file mode 100644
index 0000000..9022ecd
--- /dev/null
+++ b/src/gui/editors/notation/NoteCharacterNames.h
@@ -0,0 +1,120 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral right of the authors to claim authorship of this work
+ has been asserted.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _NOTE_CHAR_NAME_H_
+#define _NOTE_CHAR_NAME_H_
+
+#include "PropertyName.h"
+
+namespace Rosegarden {
+
+typedef PropertyName CharName;
+
+/// A selection of Unicode character names for symbols in a note font
+
+namespace NoteCharacterNames
+{
+extern const CharName SHARP;
+extern const CharName FLAT;
+extern const CharName NATURAL;
+extern const CharName DOUBLE_SHARP;
+extern const CharName DOUBLE_FLAT;
+
+extern const CharName BREVE;
+extern const CharName WHOLE_NOTE;
+extern const CharName VOID_NOTEHEAD;
+extern const CharName NOTEHEAD_BLACK;
+
+extern const CharName X_NOTEHEAD;
+extern const CharName CIRCLE_X_NOTEHEAD;
+extern const CharName SEMIBREVIS_WHITE;
+extern const CharName SEMIBREVIS_BLACK;
+extern const CharName TRIANGLE_NOTEHEAD_UP_WHITE;
+extern const CharName TRIANGLE_NOTEHEAD_UP_BLACK;
+extern const CharName SQUARE_NOTEHEAD_WHITE;
+extern const CharName SQUARE_NOTEHEAD_BLACK;
+
+extern const CharName FLAG_PARTIAL;
+extern const CharName FLAG_PARTIAL_FINAL;
+
+extern const CharName FLAG_1;
+extern const CharName FLAG_2;
+extern const CharName FLAG_3;
+extern const CharName FLAG_4;
+
+extern const CharName MULTI_REST;
+extern const CharName MULTI_REST_ON_STAFF;
+extern const CharName WHOLE_REST;
+extern const CharName WHOLE_REST_ON_STAFF;
+extern const CharName HALF_REST;
+extern const CharName HALF_REST_ON_STAFF;
+extern const CharName QUARTER_REST;
+extern const CharName EIGHTH_REST;
+extern const CharName SIXTEENTH_REST;
+extern const CharName THIRTY_SECOND_REST;
+extern const CharName SIXTY_FOURTH_REST;
+
+extern const CharName DOT;
+
+extern const CharName ACCENT;
+extern const CharName TENUTO;
+extern const CharName STACCATO;
+extern const CharName STACCATISSIMO;
+extern const CharName MARCATO;
+extern const CharName FERMATA;
+extern const CharName TRILL;
+extern const CharName TRILL_LINE;
+extern const CharName TURN;
+extern const CharName UP_BOW;
+extern const CharName DOWN_BOW;
+
+extern const CharName MORDENT;
+extern const CharName MORDENT_INVERTED;
+extern const CharName MORDENT_LONG;
+extern const CharName MORDENT_LONG_INVERTED;
+
+extern const CharName PEDAL_MARK;
+extern const CharName PEDAL_UP_MARK;
+
+extern const CharName C_CLEF;
+extern const CharName G_CLEF;
+extern const CharName F_CLEF;
+
+extern const CharName COMMON_TIME;
+extern const CharName CUT_TIME;
+extern const CharName DIGIT_ZERO;
+extern const CharName DIGIT_ONE;
+extern const CharName DIGIT_TWO;
+extern const CharName DIGIT_THREE;
+extern const CharName DIGIT_FOUR;
+extern const CharName DIGIT_FIVE;
+extern const CharName DIGIT_SIX;
+extern const CharName DIGIT_SEVEN;
+extern const CharName DIGIT_EIGHT;
+extern const CharName DIGIT_NINE;
+
+extern const CharName UNKNOWN;
+}
+
+}
+
+#endif
+
diff --git a/src/gui/editors/notation/NoteFont.cpp b/src/gui/editors/notation/NoteFont.cpp
new file mode 100644
index 0000000..95746c3
--- /dev/null
+++ b/src/gui/editors/notation/NoteFont.cpp
@@ -0,0 +1,650 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NoteFont.h"
+#include "misc/Debug.h"
+
+#include "misc/Strings.h"
+#include "base/Exception.h"
+#include "gui/general/PixmapFunctions.h"
+#include "NoteCharacter.h"
+#include "NoteFontMap.h"
+#include "SystemFont.h"
+#include <qbitmap.h>
+#include <qgarray.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+
+namespace Rosegarden
+{
+
+NoteFont::FontPixmapMap *NoteFont::m_fontPixmapMap = 0;
+
+NoteFont::DrawRepMap *NoteFont::m_drawRepMap = 0;
+QPixmap *NoteFont::m_blankPixmap = 0;
+
+
+NoteFont::NoteFont(std::string fontName, int size) :
+ m_fontMap(fontName)
+{
+ // Do the size checks first, to avoid doing the extra work if they fail
+
+ std::set<int> sizes = m_fontMap.getSizes();
+
+ if (sizes.size() > 0) {
+ m_size = *sizes.begin();
+ } else {
+ throw BadNoteFont(std::string("No sizes listed for font ") + fontName);
+ }
+
+ if (size > 0) {
+ if (sizes.find(size) == sizes.end()) {
+ throw BadNoteFont(qstrtostr(QString("Font \"%1\" not available in size %2").arg(strtoqstr(fontName)).arg(size)));
+ } else {
+ m_size = size;
+ }
+ }
+
+ // Create the global font map and blank pixmap if necessary
+
+ if (m_fontPixmapMap == 0) {
+ m_fontPixmapMap = new FontPixmapMap();
+ }
+
+ if (m_blankPixmap == 0) {
+ m_blankPixmap = new QPixmap(10, 10);
+ m_blankPixmap->setMask(QBitmap(10, 10, TRUE));
+ }
+
+ // Locate our font's pixmap map in the font map, create if necessary
+
+ std::string fontKey = qstrtostr(QString("__%1__%2__")
+ .arg(strtoqstr(m_fontMap.getName()))
+ .arg(m_size));
+
+ FontPixmapMap::iterator i = m_fontPixmapMap->find(fontKey);
+ if (i == m_fontPixmapMap->end()) {
+ (*m_fontPixmapMap)[fontKey] = new PixmapMap();
+ }
+
+ m_map = (*m_fontPixmapMap)[fontKey];
+}
+
+NoteFont::~NoteFont()
+{
+ // empty
+}
+
+bool
+NoteFont::getStemThickness(unsigned int &thickness) const
+{
+ thickness = m_size / 9 + 1;
+ return m_fontMap.getStemThickness(m_size, thickness);
+}
+
+bool
+NoteFont::getBeamThickness(unsigned int &thickness) const
+{
+ thickness = m_size / 2;
+ return m_fontMap.getBeamThickness(m_size, thickness);
+}
+
+bool
+NoteFont::getStemLength(unsigned int &length) const
+{
+ getStaffLineThickness(length);
+ length = (m_size + length) * 7 / 2;
+ return m_fontMap.getStemLength(m_size, length);
+}
+
+bool
+NoteFont::getFlagSpacing(unsigned int &spacing) const
+{
+ spacing = m_size;
+ return m_fontMap.getFlagSpacing(m_size, spacing);
+}
+
+bool
+NoteFont::getStaffLineThickness(unsigned int &thickness) const
+{
+ thickness = (m_size < 7 ? 1 : m_size / 7);
+ return m_fontMap.getStaffLineThickness(m_size, thickness);
+}
+
+bool
+NoteFont::getLegerLineThickness(unsigned int &thickness) const
+{
+ thickness = (m_size < 6 ? 1 : m_size / 6);
+ return m_fontMap.getLegerLineThickness(m_size, thickness);
+}
+
+bool
+NoteFont::lookup(CharName charName, bool inverted, QPixmap *&pixmap) const
+{
+ PixmapMap::iterator i = m_map->find(charName);
+ if (i != m_map->end()) {
+ if (inverted) {
+ pixmap = i->second.second;
+ if (!pixmap && i->second.first)
+ return false;
+ } else {
+ pixmap = i->second.first;
+ if (!pixmap && i->second.second)
+ return false;
+ }
+ return true;
+ }
+ pixmap = 0;
+ return false;
+}
+
+void
+NoteFont::add
+(CharName charName, bool inverted, QPixmap *pixmap) const
+{
+ PixmapMap::iterator i = m_map->find(charName);
+ if (i != m_map->end()) {
+ if (inverted) {
+ delete i->second.second;
+ i->second.second = pixmap;
+ } else {
+ delete i->second.first;
+ i->second.first = pixmap;
+ }
+ } else {
+ if (inverted) {
+ (*m_map)[charName] = PixmapPair(0, pixmap);
+ } else {
+ (*m_map)[charName] = PixmapPair(pixmap, 0);
+ }
+ }
+}
+
+NoteCharacterDrawRep *
+NoteFont::lookupDrawRep(QPixmap *pixmap) const
+{
+ if (!m_drawRepMap)
+ m_drawRepMap = new DrawRepMap();
+
+ if (m_drawRepMap->find(pixmap) != m_drawRepMap->end()) {
+
+ return (*m_drawRepMap)[pixmap];
+
+ } else {
+
+ QImage image = pixmap->convertToImage();
+ if (image.isNull())
+ return 0;
+
+ if (image.depth() > 1) {
+ image = image.convertDepth(1, Qt::MonoOnly | Qt::ThresholdDither);
+ }
+
+ NoteCharacterDrawRep *a = new NoteCharacterDrawRep();
+
+ for (int yi = 0; yi < image.height(); ++yi) {
+
+ unsigned char *line = image.scanLine(yi);
+
+ int startx = 0;
+
+ for (int xi = 0; xi <= image.width(); ++xi) {
+
+ bool pixel = false;
+
+ if (xi < image.width()) {
+ if (image.bitOrder() == QImage::LittleEndian) {
+ if (*(line + (xi >> 3)) & 1 << (xi & 7))
+ pixel = true;
+ } else {
+ if (*(line + (xi >> 3)) & 1 << (7 - (xi & 7)))
+ pixel = true;
+ }
+ }
+
+ if (!pixel) {
+ if (startx < xi) {
+ a->resize(a->size() + 2, QGArray::SpeedOptim);
+ a->setPoint(a->size() - 2, startx, yi);
+ a->setPoint(a->size() - 1, xi - 1, yi);
+ }
+ startx = xi + 1;
+ }
+ }
+ }
+
+ (*m_drawRepMap)[pixmap] = a;
+ return a;
+ }
+}
+
+bool
+NoteFont::getPixmap(CharName charName, QPixmap &pixmap, bool inverted) const
+{
+ QPixmap *found = 0;
+ bool ok = lookup(charName, inverted, found);
+ if (ok) {
+ if (found) {
+ pixmap = *found;
+ return true;
+ } else {
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+ }
+
+ if (inverted && !m_fontMap.hasInversion(m_size, charName)) {
+ if (!getPixmap(charName, pixmap, !inverted))
+ return false;
+ found = new QPixmap(PixmapFunctions::flipVertical(pixmap));
+ add(charName, inverted, found);
+ pixmap = *found;
+ return true;
+ }
+
+ std::string src;
+ ok = false;
+
+ if (!inverted)
+ ok = m_fontMap.getSrc(m_size, charName, src);
+ else
+ ok = m_fontMap.getInversionSrc(m_size, charName, src);
+
+ if (ok) {
+ NOTATION_DEBUG
+ << "NoteFont::getPixmap: Loading \"" << src << "\"" << endl;
+
+ found = new QPixmap(strtoqstr(src));
+
+ if (!found->isNull()) {
+
+ if (found->mask() == 0) {
+ std::cerr << "NoteFont::getPixmap: Warning: No automatic mask "
+ << "for character \"" << charName << "\""
+ << (inverted ? " (inverted)" : "") << " in font \""
+ << m_fontMap.getName() << "-" << m_size
+ << "\"; consider making xpm background transparent"
+ << std::endl;
+ found->setMask(PixmapFunctions::generateMask(*found));
+ }
+
+ add(charName, inverted, found);
+ pixmap = *found;
+ return true;
+ }
+
+ std::cerr << "NoteFont::getPixmap: Warning: Unable to read pixmap file " << src << std::endl;
+ } else {
+
+ int code = -1;
+ if (!inverted)
+ ok = m_fontMap.getCode(m_size, charName, code);
+ else
+ ok = m_fontMap.getInversionCode(m_size, charName, code);
+
+ int glyph = -1;
+ if (!inverted)
+ ok = m_fontMap.getGlyph(m_size, charName, glyph);
+ else
+ ok = m_fontMap.getInversionGlyph(m_size, charName, glyph);
+
+ if (code < 0 && glyph < 0) {
+ std::cerr << "NoteFont::getPixmap: Warning: No pixmap, code, or glyph for character \""
+ << charName << "\"" << (inverted ? " (inverted)" : "")
+ << " in font \"" << m_fontMap.getName() << "\"" << std::endl;
+ add(charName, inverted, 0);
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+
+ int charBase = 0;
+ SystemFont *systemFont =
+ m_fontMap.getSystemFont(m_size, charName, charBase);
+
+ if (!systemFont) {
+ if (!inverted && m_fontMap.hasInversion(m_size, charName)) {
+ if (!getPixmap(charName, pixmap, !inverted))
+ return false;
+ found = new QPixmap(PixmapFunctions::flipVertical(pixmap));
+ add(charName, inverted, found);
+ pixmap = *found;
+ return true;
+ }
+
+ std::cerr << "NoteFont::getPixmap: Warning: No system font for character \""
+ << charName << "\"" << (inverted ? " (inverted)" : "")
+ << " in font \"" << m_fontMap.getName() << "\"" << std::endl;
+
+ add(charName, inverted, 0);
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+
+ SystemFont::Strategy strategy =
+ m_fontMap.getStrategy(m_size, charName);
+
+ bool success;
+ found = new QPixmap(systemFont->renderChar(charName,
+ glyph,
+ code + charBase,
+ strategy,
+ success));
+
+ if (success) {
+ add(charName, inverted, found);
+ pixmap = *found;
+ return true;
+ } else {
+ add(charName, inverted, 0);
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+ }
+
+ add(charName, inverted, 0);
+ pixmap = *m_blankPixmap;
+ return false;
+}
+
+bool
+NoteFont::getColouredPixmap(CharName baseCharName, QPixmap &pixmap,
+ int hue, int minValue, bool inverted) const
+{
+ CharName charName(getNameWithColour(baseCharName, hue));
+
+ QPixmap *found = 0;
+ bool ok = lookup(charName, inverted, found);
+ if (ok) {
+ if (found) {
+ pixmap = *found;
+ return true;
+ } else {
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+ }
+
+ QPixmap basePixmap;
+ ok = getPixmap(baseCharName, basePixmap, inverted);
+
+ if (!ok) {
+ add(charName, inverted, 0);
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+
+ found = new QPixmap
+ (PixmapFunctions::colourPixmap(basePixmap, hue, minValue));
+ add(charName, inverted, found);
+ pixmap = *found;
+ return ok;
+}
+
+bool
+NoteFont::getShadedPixmap(CharName baseCharName, QPixmap &pixmap,
+ bool inverted) const
+{
+ CharName charName(getNameShaded(baseCharName));
+
+ QPixmap *found = 0;
+ bool ok = lookup(charName, inverted, found);
+ if (ok) {
+ if (found) {
+ pixmap = *found;
+ return true;
+ } else {
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+ }
+
+ QPixmap basePixmap;
+ ok = getPixmap(baseCharName, basePixmap, inverted);
+
+ if (!ok) {
+ add(charName, inverted, 0);
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+
+ found = new QPixmap(PixmapFunctions::shadePixmap(basePixmap));
+ add(charName, inverted, found);
+ pixmap = *found;
+ return ok;
+}
+
+CharName
+NoteFont::getNameWithColour(CharName base, int hue) const
+{
+ return qstrtostr(QString("%1__%2").arg(hue).arg(strtoqstr(base)));
+}
+
+CharName
+NoteFont::getNameShaded(CharName base) const
+{
+ return qstrtostr(QString("shaded__%1").arg(strtoqstr(base)));
+}
+
+bool
+NoteFont::getDimensions(CharName charName, int &x, int &y, bool inverted) const
+{
+ QPixmap pixmap;
+ bool ok = getPixmap(charName, pixmap, inverted);
+ x = pixmap.width();
+ y = pixmap.height();
+ return ok;
+}
+
+int
+NoteFont::getWidth(CharName charName) const
+{
+ int x, y;
+ getDimensions(charName, x, y);
+ return x;
+}
+
+int
+NoteFont::getHeight(CharName charName) const
+{
+ int x, y;
+ getDimensions(charName, x, y);
+ return y;
+}
+
+bool
+NoteFont::getHotspot(CharName charName, int &x, int &y, bool inverted) const
+{
+ int w, h;
+ getDimensions(charName, w, h, inverted);
+ bool ok = m_fontMap.getHotspot(m_size, charName, w, h, x, y);
+
+ if (!ok) {
+ x = 0;
+ y = h / 2;
+ }
+
+ if (inverted) {
+ y = h - y;
+ }
+
+ return ok;
+}
+
+QPoint
+NoteFont::getHotspot(CharName charName, bool inverted) const
+{
+ int x, y;
+ (void)getHotspot(charName, x, y, inverted);
+ return QPoint(x, y);
+}
+
+bool
+NoteFont::getCharacter(CharName charName,
+ NoteCharacter &character,
+ CharacterType type,
+ bool inverted)
+{
+ QPixmap pixmap;
+ if (!getPixmap(charName, pixmap, inverted))
+ return false;
+
+ if (type == Screen) {
+ character = NoteCharacter(pixmap,
+ getHotspot(charName, inverted),
+ 0);
+ } else {
+
+ // Get the pointer direct from cache (depends on earlier call
+ // to getPixmap to put it in the cache if available)
+
+ QPixmap *pmapptr = 0;
+ bool found = lookup(charName, inverted, pmapptr);
+
+ NoteCharacterDrawRep *rep = 0;
+ if (found && pmapptr)
+ rep = lookupDrawRep(pmapptr);
+
+ character = NoteCharacter(pixmap,
+ getHotspot(charName, inverted),
+ rep);
+ }
+
+ return true;
+}
+
+NoteCharacter
+NoteFont::getCharacter(CharName charName,
+ CharacterType type,
+ bool inverted)
+{
+ NoteCharacter character;
+ getCharacter(charName, character, type, inverted);
+ return character;
+}
+
+bool
+NoteFont::getCharacterColoured(CharName charName,
+ int hue, int minValue,
+ NoteCharacter &character,
+ CharacterType type,
+ bool inverted)
+{
+ QPixmap pixmap;
+ if (!getColouredPixmap(charName, pixmap, hue, minValue, inverted)) {
+ return false;
+ }
+
+ if (type == Screen) {
+
+ character = NoteCharacter(pixmap,
+ getHotspot(charName, inverted),
+ 0);
+
+ } else {
+
+ // Get the pointer direct from cache (depends on earlier call
+ // to getPixmap to put it in the cache if available)
+
+ QPixmap *pmapptr = 0;
+ CharName cCharName(getNameWithColour(charName, hue));
+ bool found = lookup(cCharName, inverted, pmapptr);
+
+ NoteCharacterDrawRep *rep = 0;
+ if (found && pmapptr)
+ rep = lookupDrawRep(pmapptr);
+
+ character = NoteCharacter(pixmap,
+ getHotspot(charName, inverted),
+ rep);
+ }
+
+ return true;
+}
+
+NoteCharacter
+NoteFont::getCharacterColoured(CharName charName,
+ int hue, int minValue,
+ CharacterType type,
+ bool inverted)
+{
+ NoteCharacter character;
+ getCharacterColoured(charName, hue, minValue, character, type, inverted);
+ return character;
+}
+
+bool
+NoteFont::getCharacterShaded(CharName charName,
+ NoteCharacter &character,
+ CharacterType type,
+ bool inverted)
+{
+ QPixmap pixmap;
+ if (!getShadedPixmap(charName, pixmap, inverted)) {
+ return false;
+ }
+
+ if (type == Screen) {
+
+ character = NoteCharacter(pixmap,
+ getHotspot(charName, inverted),
+ 0);
+
+ } else {
+
+ // Get the pointer direct from cache (depends on earlier call
+ // to getPixmap to put it in the cache if available)
+
+ QPixmap *pmapptr = 0;
+ CharName cCharName(getNameShaded(charName));
+ bool found = lookup(cCharName, inverted, pmapptr);
+
+ NoteCharacterDrawRep *rep = 0;
+ if (found && pmapptr)
+ rep = lookupDrawRep(pmapptr);
+
+ character = NoteCharacter(pixmap,
+ getHotspot(charName, inverted),
+ rep);
+ }
+
+ return true;
+}
+
+NoteCharacter
+NoteFont::getCharacterShaded(CharName charName,
+ CharacterType type,
+ bool inverted)
+{
+ NoteCharacter character;
+ getCharacterShaded(charName, character, type, inverted);
+ return character;
+}
+
+}
diff --git a/src/gui/editors/notation/NoteFont.h b/src/gui/editors/notation/NoteFont.h
new file mode 100644
index 0000000..81a3b19
--- /dev/null
+++ b/src/gui/editors/notation/NoteFont.h
@@ -0,0 +1,184 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTEFONT_H_
+#define _RG_NOTEFONT_H_
+
+#include "base/Exception.h"
+#include <map>
+#include "NoteCharacter.h"
+#include "NoteFontMap.h"
+#include <set>
+#include <string>
+#include <qpoint.h>
+#include <utility>
+#include "gui/editors/notation/NoteCharacterNames.h"
+
+
+class QPixmap;
+class PixmapMap;
+class NoteCharacterDrawRep;
+class FontPixmapMap;
+class DrawRepMap;
+
+
+namespace Rosegarden
+{
+
+
+
+class NoteFont
+{
+public:
+ enum CharacterType { Screen, Printer };
+
+ typedef Exception BadNoteFont;
+ ~NoteFont();
+
+ std::string getName() const { return m_fontMap.getName(); }
+ int getSize() const { return m_size; }
+ bool isSmooth() const { return m_fontMap.isSmooth(); }
+ const NoteFontMap &getNoteFontMap() const { return m_fontMap; }
+
+ /// Returns false + thickness=1 if not specified
+ bool getStemThickness(unsigned int &thickness) const;
+
+ /// Returns false + a guess at suitable thickness if not specified
+ bool getBeamThickness(unsigned int &thickness) const;
+
+ /// Returns false + a guess at suitable length if not specified
+ bool getStemLength(unsigned int &length) const;
+
+ /// Returns false + a guess at suitable spacing if not specified
+ bool getFlagSpacing(unsigned int &spacing) const;
+
+ /// Returns false + thickness=1 if not specified
+ bool getStaffLineThickness(unsigned int &thickness) const;
+
+ /// Returns false + thickness=1 if not specified
+ bool getLegerLineThickness(unsigned int &thickness) const;
+
+ /// Returns false if not available
+ bool getCharacter(CharName charName,
+ NoteCharacter &character,
+ CharacterType type = Screen,
+ bool inverted = false);
+
+ /// Returns an empty character if not available
+ NoteCharacter getCharacter(CharName charName,
+ CharacterType type = Screen,
+ bool inverted = false);
+
+ /// Returns false if not available
+ bool getCharacterColoured(CharName charName,
+ int hue, int minValue,
+ NoteCharacter &character,
+ CharacterType type = Screen,
+ bool inverted = false);
+
+ /// Returns an empty character if not available
+ NoteCharacter getCharacterColoured(CharName charName,
+ int hue, int minValue,
+ CharacterType type = Screen,
+ bool inverted = false);
+
+ /// Returns false if not available
+ bool getCharacterShaded(CharName charName,
+ NoteCharacter &character,
+ CharacterType type = Screen,
+ bool inverted = false);
+
+ /// Returns an empty character if not available
+ NoteCharacter getCharacterShaded(CharName charName,
+ CharacterType type = Screen,
+ bool inverted = false);
+
+ /// Returns false + dimensions of blank pixmap if none found
+ bool getDimensions(CharName charName, int &x, int &y,
+ bool inverted = false) const;
+
+ /// Ignores problems, returning dimension of blank pixmap if necessary
+ int getWidth(CharName charName) const;
+
+ /// Ignores problems, returning dimension of blank pixmap if necessary
+ int getHeight(CharName charName) const;
+
+ /// Returns false + centre-left of pixmap if no hotspot specified
+ bool getHotspot(CharName charName, int &x, int &y,
+ bool inverted = false) const;
+
+ /// Ignores problems, returns centre-left of pixmap if necessary
+ QPoint getHotspot(CharName charName, bool inverted = false) const;
+
+private:
+ /// Returns false + blank pixmap if it can't find the right one
+ bool getPixmap(CharName charName, QPixmap &pixmap,
+ bool inverted = false) const;
+
+ /// Returns false + blank pixmap if it can't find the right one
+ bool getColouredPixmap(CharName charName, QPixmap &pixmap,
+ int hue, int minValue,
+ bool inverted = false) const;
+
+ /// Returns false + blank pixmap if it can't find the right one
+ bool getShadedPixmap(CharName charName, QPixmap &pixmap,
+ bool inverted = false) const;
+
+ friend class NoteFontFactory;
+ NoteFont(std::string fontName, int size = 0);
+ std::set<int> getSizes() const { return m_fontMap.getSizes(); }
+
+ bool lookup(CharName charName, bool inverted, QPixmap *&pixmap) const;
+ void add(CharName charName, bool inverted, QPixmap *pixmap) const;
+
+ NoteCharacterDrawRep *lookupDrawRep(QPixmap *pixmap) const;
+
+ CharName getNameWithColour(CharName origName, int hue) const;
+ CharName getNameShaded(CharName origName) const;
+
+ typedef std::pair<QPixmap *, QPixmap *> PixmapPair;
+ typedef std::map<CharName, PixmapPair> PixmapMap;
+ typedef std::map<std::string, PixmapMap *> FontPixmapMap;
+
+ typedef std::map<QPixmap *, NoteCharacterDrawRep *> DrawRepMap;
+
+ //--------------- Data members ---------------------------------
+
+ int m_size;
+ NoteFontMap m_fontMap;
+
+ mutable PixmapMap *m_map; // pointer at a member of m_fontPixmapMap
+
+ static FontPixmapMap *m_fontPixmapMap;
+ static DrawRepMap *m_drawRepMap;
+
+ static QPixmap *m_blankPixmap;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteFontFactory.cpp b/src/gui/editors/notation/NoteFontFactory.cpp
new file mode 100644
index 0000000..2decce4
--- /dev/null
+++ b/src/gui/editors/notation/NoteFontFactory.cpp
@@ -0,0 +1,236 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NoteFontFactory.h"
+#include "misc/Debug.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/Exception.h"
+#include "gui/kdeext/KStartupLogo.h"
+#include "NoteFont.h"
+#include "NoteFontMap.h"
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kmessagebox.h>
+#include <qdir.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+std::set<std::string>
+NoteFontFactory::getFontNames(bool forceRescan)
+{
+ NOTATION_DEBUG << "NoteFontFactory::getFontNames: forceRescan = " << forceRescan << endl;
+
+ if (forceRescan)
+ m_fontNames.clear();
+ if (!m_fontNames.empty())
+ return m_fontNames;
+
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+
+ QString fontNameList = "";
+ if (!forceRescan) {
+ fontNameList = config->readEntry("notefontlist");
+ }
+
+ NOTATION_DEBUG << "NoteFontFactory::getFontNames: read from cache: " << fontNameList << endl;
+
+ QStringList names = QStringList::split(",", fontNameList);
+
+ if (names.empty()) {
+
+ NOTATION_DEBUG << "NoteFontFactory::getFontNames: No names available, rescanning..." << endl;
+
+ QString mappingDir =
+ KGlobal::dirs()->findResource("appdata", "fonts/mappings/");
+ QDir dir(mappingDir);
+ if (!dir.exists()) {
+ std::cerr << "NoteFontFactory::getFontNames: mapping directory \""
+ << mappingDir << "\" not found" << std::endl;
+ return m_fontNames;
+ }
+
+ dir.setFilter(QDir::Files | QDir::Readable);
+ QStringList files = dir.entryList();
+ for (QStringList::Iterator i = files.begin(); i != files.end(); ++i) {
+
+ if ((*i).length() > 4 && (*i).right(4).lower() == ".xml") {
+
+ std::string name(qstrtostr((*i).left((*i).length() - 4)));
+
+ try {
+ NoteFontMap map(name);
+ if (map.ok())
+ names.append(strtoqstr(map.getName()));
+ } catch (Exception e) {
+ KStartupLogo::hideIfStillThere();
+ KMessageBox::error(0, strtoqstr(e.getMessage()));
+ throw;
+ }
+ }
+ }
+ }
+
+ QString savedNames = "";
+
+ for (QStringList::Iterator i = names.begin(); i != names.end(); ++i) {
+ m_fontNames.insert(qstrtostr(*i));
+ if (i != names.begin())
+ savedNames += ",";
+ savedNames += *i;
+ }
+
+ config->writeEntry("notefontlist", savedNames);
+
+ return m_fontNames;
+}
+
+std::vector<int>
+NoteFontFactory::getAllSizes(std::string fontName)
+{
+ NoteFont *font = getFont(fontName, 0);
+ if (!font)
+ return std::vector<int>();
+
+ std::set
+ <int> s(font->getSizes());
+ std::vector<int> v;
+ for (std::set
+ <int>::iterator i = s.begin(); i != s.end(); ++i) {
+ v.push_back(*i);
+ }
+
+ std::sort(v.begin(), v.end());
+ return v;
+}
+
+std::vector<int>
+NoteFontFactory::getScreenSizes(std::string fontName)
+{
+ NoteFont *font = getFont(fontName, 0);
+ if (!font)
+ return std::vector<int>();
+
+ std::set
+ <int> s(font->getSizes());
+ std::vector<int> v;
+ for (std::set
+ <int>::iterator i = s.begin(); i != s.end(); ++i) {
+ if (*i <= 16)
+ v.push_back(*i);
+ }
+ std::sort(v.begin(), v.end());
+ return v;
+}
+
+NoteFont *
+NoteFontFactory::getFont(std::string fontName, int size)
+{
+ std::map<std::pair<std::string, int>, NoteFont *>::iterator i =
+ m_fonts.find(std::pair<std::string, int>(fontName, size));
+
+ if (i == m_fonts.end()) {
+ try {
+ NoteFont *font = new NoteFont(fontName, size);
+ m_fonts[std::pair<std::string, int>(fontName, size)] = font;
+ return font;
+ } catch (Exception e) {
+ KStartupLogo::hideIfStillThere();
+ KMessageBox::error(0, strtoqstr(e.getMessage()));
+ throw;
+ }
+ } else {
+ return i->second;
+ }
+}
+
+std::string
+NoteFontFactory::getDefaultFontName()
+{
+ static std::string defaultFont = "";
+ if (defaultFont != "")
+ return defaultFont;
+
+ std::set
+ <std::string> fontNames = getFontNames();
+
+ if (fontNames.find("Feta") != fontNames.end())
+ defaultFont = "Feta";
+ else {
+ fontNames = getFontNames(true);
+ if (fontNames.find("Feta") != fontNames.end())
+ defaultFont = "Feta";
+ else if (fontNames.find("Feta Pixmaps") != fontNames.end())
+ defaultFont = "Feta Pixmaps";
+ else if (fontNames.size() > 0)
+ defaultFont = *fontNames.begin();
+ else {
+ QString message = i18n("Can't obtain a default font -- no fonts found");
+ KStartupLogo::hideIfStillThere();
+ KMessageBox::error(0, message);
+ throw NoFontsAvailable(qstrtostr(message));
+ }
+ }
+
+ return defaultFont;
+}
+
+int
+NoteFontFactory::getDefaultSize(std::string fontName)
+{
+ // always return 8 if it's supported!
+ std::vector<int> sizes(getScreenSizes(fontName));
+ for (unsigned int i = 0; i < sizes.size(); ++i) {
+ if (sizes[i] == 8)
+ return sizes[i];
+ }
+ return sizes[sizes.size() / 2];
+}
+
+bool
+NoteFontFactory::isAvailableInSize(std::string fontName, int size)
+{
+ std::vector<int> sizes(getAllSizes(fontName));
+ for (unsigned int i = 0; i < sizes.size(); ++i) {
+ if (sizes[i] == size)
+ return true;
+ }
+ return false;
+}
+
+std::set<std::string> NoteFontFactory::m_fontNames;
+std::map<std::pair<std::string, int>, NoteFont *> NoteFontFactory::m_fonts;
+
+}
diff --git a/src/gui/editors/notation/NoteFontFactory.h b/src/gui/editors/notation/NoteFontFactory.h
new file mode 100644
index 0000000..33e6e80
--- /dev/null
+++ b/src/gui/editors/notation/NoteFontFactory.h
@@ -0,0 +1,71 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTEFONTFACTORY_H_
+#define _RG_NOTEFONTFACTORY_H_
+
+#include "base/Exception.h"
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+
+
+
+namespace Rosegarden
+{
+
+class NoteFont;
+
+
+class NoteFontFactory
+{
+public:
+ typedef Exception NoFontsAvailable;
+
+ // Any method passed a fontName argument may throw BadFont or
+ // MappingFileReadFailed; any other method may throw NoFontsAvailable
+
+ static NoteFont *getFont(std::string fontName, int size);
+
+ static std::set<std::string> getFontNames(bool forceRescan = false);
+ static std::vector<int> getAllSizes(std::string fontName); // sorted
+ static std::vector<int> getScreenSizes(std::string fontName); // sorted
+
+ static std::string getDefaultFontName();
+ static int getDefaultSize(std::string fontName);
+ static bool isAvailableInSize(std::string fontName, int size);
+
+private:
+ static std::set<std::string> m_fontNames;
+ static std::map<std::pair<std::string, int>, NoteFont *> m_fonts;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteFontMap.cpp b/src/gui/editors/notation/NoteFontMap.cpp
new file mode 100644
index 0000000..e11c126
--- /dev/null
+++ b/src/gui/editors/notation/NoteFontMap.cpp
@@ -0,0 +1,1088 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NoteFontMap.h"
+#include "misc/Debug.h"
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Strings.h"
+#include "base/Exception.h"
+#include "SystemFont.h"
+#include <kglobal.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qpixmap.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+
+namespace Rosegarden
+{
+
+NoteFontMap::NoteFontMap(std::string name) :
+ m_name(name),
+ m_smooth(false),
+ m_srcDirectory(name),
+ m_characterDestination(0),
+ m_hotspotCharName(""),
+ m_errorString(i18n("unknown error")),
+ m_ok(true)
+{
+ m_fontDirectory = KGlobal::dirs()->findResource("appdata", "fonts/");
+
+ QString mapFileName;
+
+ QString mapFileMixedName = QString("%1/mappings/%2.xml")
+ .arg(m_fontDirectory)
+ .arg(strtoqstr(name));
+
+ QFileInfo mapFileMixedInfo(mapFileMixedName);
+
+ if (!mapFileMixedInfo.isReadable()) {
+
+ QString lowerName = strtoqstr(name).lower();
+ lowerName.replace(QRegExp(" "), "_");
+ QString mapFileLowerName = QString("%1/mappings/%2.xml")
+ .arg(m_fontDirectory)
+ .arg(lowerName);
+
+ QFileInfo mapFileLowerInfo(mapFileLowerName);
+
+ if (!mapFileLowerInfo.isReadable()) {
+ if (mapFileLowerName != mapFileMixedName) {
+ throw MappingFileReadFailed
+ (qstrtostr(i18n("Can't open font mapping file %1 or %2").
+ arg(mapFileMixedName).arg(mapFileLowerName)));
+ } else {
+ throw MappingFileReadFailed
+ (qstrtostr(i18n("Can't open font mapping file %1").
+ arg(mapFileMixedName)));
+ }
+ } else {
+ mapFileName = mapFileLowerName;
+ }
+ } else {
+ mapFileName = mapFileMixedName;
+ }
+
+ QFile mapFile(mapFileName);
+
+ QXmlInputSource source(mapFile);
+ QXmlSimpleReader reader;
+ reader.setContentHandler(this);
+ reader.setErrorHandler(this);
+ bool ok = reader.parse(source);
+ mapFile.close();
+
+ if (!ok) {
+ throw MappingFileReadFailed(qstrtostr(m_errorString));
+ }
+}
+
+NoteFontMap::~NoteFontMap()
+{
+ for (SystemFontMap::iterator i = m_systemFontCache.begin();
+ i != m_systemFontCache.end(); ++i) {
+ delete i->second;
+ }
+}
+
+bool
+NoteFontMap::characters(QString &chars)
+{
+ if (!m_characterDestination)
+ return true;
+ *m_characterDestination += qstrtostr(chars);
+ return true;
+}
+
+int
+NoteFontMap::toSize(int baseSize, double factor, bool limitAtOne)
+{
+ double dsize = factor * baseSize;
+ dsize += 0.5;
+ if (limitAtOne && dsize < 1.0)
+ dsize = 1.0;
+ return int(dsize);
+}
+
+bool
+NoteFontMap::startElement(const QString &, const QString &,
+ const QString &qName,
+ const QXmlAttributes &attributes)
+{
+ QString lcName = qName.lower();
+ m_characterDestination = 0;
+
+ // The element names are actually unique within the whole file;
+ // we don't bother checking we're in the right context. Leave that
+ // to the DTD, when we have one.
+
+ if (lcName == "rosegarden-font-encoding") {
+
+ QString s;
+
+ s = attributes.value("name");
+ if (s) {
+ m_name = qstrtostr(s);
+ m_srcDirectory = m_name;
+ }
+
+ } else if (lcName == "font-information") {
+
+ QString s;
+
+ s = attributes.value("origin");
+ if (s)
+ m_origin = qstrtostr(s);
+
+ s = attributes.value("copyright");
+ if (s)
+ m_copyright = qstrtostr(s);
+
+ s = attributes.value("mapped-by");
+ if (s)
+ m_mappedBy = qstrtostr(s);
+
+ s = attributes.value("type");
+ if (s)
+ m_type = qstrtostr(s);
+
+ s = attributes.value("autocrop");
+ if (s) {
+ std::cerr << "Warning: autocrop attribute in note font mapping file is no longer supported\n(all fonts are now always autocropped)" << std::endl;
+ }
+
+ s = attributes.value("smooth");
+ if (s)
+ m_smooth = (s.lower() == "true");
+
+ } else if (lcName == "font-sizes") {
+ }
+ else if (lcName == "font-size") {
+
+ QString s = attributes.value("note-height");
+ if (!s) {
+ m_errorString = "note-height is a required attribute of font-size";
+ return false;
+ }
+ int noteHeight = s.toInt();
+
+ SizeData &sizeData = m_sizes[noteHeight];
+
+ s = attributes.value("staff-line-thickness");
+ if (s)
+ sizeData.setStaffLineThickness(s.toInt());
+
+ s = attributes.value("leger-line-thickness");
+ if (s)
+ sizeData.setLegerLineThickness(s.toInt());
+
+ s = attributes.value("stem-thickness");
+ if (s)
+ sizeData.setStemThickness(s.toInt());
+
+ s = attributes.value("beam-thickness");
+ if (s)
+ sizeData.setBeamThickness(s.toInt());
+
+ s = attributes.value("stem-length");
+ if (s)
+ sizeData.setStemLength(s.toInt());
+
+ s = attributes.value("flag-spacing");
+ if (s)
+ sizeData.setFlagSpacing(s.toInt());
+
+ s = attributes.value("border-x");
+ if (s) {
+ std::cerr << "Warning: border-x attribute in note font mapping file is no longer supported\n(use hotspot-x for note head or flag)" << std::endl;
+ }
+
+ s = attributes.value("border-y");
+ if (s) {
+ std::cerr << "Warning: border-y attribute in note font mapping file is no longer supported" << std::endl;
+ }
+
+ int fontId = 0;
+ s = attributes.value("font-id");
+ if (s)
+ fontId = s.toInt();
+
+ s = attributes.value("font-height");
+ if (s)
+ sizeData.setFontHeight(fontId, s.toInt());
+
+ } else if (lcName == "font-scale") {
+
+ double fontHeight = -1.0;
+ double beamThickness = -1.0;
+ double stemLength = -1.0;
+ double flagSpacing = -1.0;
+ double staffLineThickness = -1.0;
+ double legerLineThickness = -1.0;
+ double stemThickness = -1.0;
+
+ QString s;
+
+ s = attributes.value("font-height");
+ if (s)
+ fontHeight = qstrtodouble(s);
+ else {
+ m_errorString = "font-height is a required attribute of font-scale";
+ return false;
+ }
+
+ s = attributes.value("staff-line-thickness");
+ if (s)
+ staffLineThickness = qstrtodouble(s);
+
+ s = attributes.value("leger-line-thickness");
+ if (s)
+ legerLineThickness = qstrtodouble(s);
+
+ s = attributes.value("stem-thickness");
+ if (s)
+ stemThickness = qstrtodouble(s);
+
+ s = attributes.value("beam-thickness");
+ if (s)
+ beamThickness = qstrtodouble(s);
+
+ s = attributes.value("stem-length");
+ if (s)
+ stemLength = qstrtodouble(s);
+
+ s = attributes.value("flag-spacing");
+ if (s)
+ flagSpacing = qstrtodouble(s);
+
+ int fontId = 0;
+ s = attributes.value("font-id");
+ if (s)
+ fontId = s.toInt();
+
+ //!!! need to be able to calculate max size -- checkFont needs
+ //to take a size argument; unfortunately Qt doesn't seem to be
+ //able to report to us when a scalable font was loaded in the
+ //wrong size, so large sizes might be significantly inaccurate
+ //as it just stops scaling up any further at somewhere around
+ //120px. We could test whether the metric for the black
+ //notehead is noticeably smaller than the notehead should be,
+ //and reject if so? [update -- no, that doesn't work either,
+ //Qt just returns the correct metric even if drawing the
+ //incorrect size]
+
+ for (int sz = 1; sz <= 30; sz += (sz == 1 ? 1 : 2)) {
+
+ SizeData & sizeData = m_sizes[sz];
+ unsigned int temp;
+
+ if (sizeData.getStaffLineThickness(temp) == false &&
+ staffLineThickness >= 0.0)
+ sizeData.setStaffLineThickness(toSize(sz, staffLineThickness, true));
+
+ if (sizeData.getLegerLineThickness(temp) == false &&
+ legerLineThickness >= 0.0)
+ sizeData.setLegerLineThickness(toSize(sz, legerLineThickness, true));
+
+ if (sizeData.getStemThickness(temp) == false &&
+ stemThickness >= 0.0)
+ sizeData.setStemThickness(toSize(sz, stemThickness, true));
+
+ if (sizeData.getBeamThickness(temp) == false &&
+ beamThickness >= 0.0)
+ sizeData.setBeamThickness(toSize(sz, beamThickness, true));
+
+ if (sizeData.getStemLength(temp) == false &&
+ stemLength >= 0.0)
+ sizeData.setStemLength(toSize(sz, stemLength, true));
+
+ if (sizeData.getFlagSpacing(temp) == false &&
+ flagSpacing >= 0.0)
+ sizeData.setFlagSpacing(toSize(sz, flagSpacing, true));
+
+ if (sizeData.getFontHeight(fontId, temp) == false)
+ sizeData.setFontHeight(fontId, toSize(sz, fontHeight, true));
+ }
+
+ } else if (lcName == "font-symbol-map") {
+ }
+ else if (lcName == "src-directory") {
+
+ QString d = attributes.value("name");
+ if (!d) {
+ m_errorString = "name is a required attribute of src-directory";
+ return false;
+ }
+
+ m_srcDirectory = qstrtostr(d);
+
+ } else if (lcName == "codebase") {
+
+ int bn = 0, fn = 0;
+ bool ok;
+ QString base = attributes.value("base");
+ if (!base) {
+ m_errorString = "base is a required attribute of codebase";
+ return false;
+ }
+ bn = base.toInt(&ok);
+ if (!ok || bn < 0) {
+ m_errorString =
+ QString("invalid base attribute \"%1\" (must be integer >= 0)").
+ arg(base);
+ return false;
+ }
+
+ QString fontId = attributes.value("font-id");
+ if (!fontId) {
+ m_errorString = "font-id is a required attribute of codebase";
+ return false;
+ }
+ fn = fontId.stripWhiteSpace().toInt(&ok);
+ if (!ok || fn < 0) {
+ m_errorString =
+ QString("invalid font-id attribute \"%1\" (must be integer >= 0)").
+ arg(fontId);
+ return false;
+ }
+
+ m_bases[fn] = bn;
+
+ } else if (lcName == "symbol") {
+
+ QString symbolName = attributes.value("name");
+ if (!symbolName) {
+ m_errorString = "name is a required attribute of symbol";
+ return false;
+ }
+ SymbolData symbolData;
+
+ QString src = attributes.value("src");
+ QString code = attributes.value("code");
+ QString glyph = attributes.value("glyph");
+
+ int icode = -1;
+ bool ok = false;
+ if (code) {
+ icode = code.stripWhiteSpace().toInt(&ok);
+ if (!ok || icode < 0) {
+ m_errorString =
+ QString("invalid code attribute \"%1\" (must be integer >= 0)").
+ arg(code);
+ return false;
+ }
+ symbolData.setCode(icode);
+ }
+
+ int iglyph = -1;
+ ok = false;
+ if (glyph) {
+ iglyph = glyph.stripWhiteSpace().toInt(&ok);
+ if (!ok || iglyph < 0) {
+ m_errorString =
+ QString("invalid glyph attribute \"%1\" (must be integer >= 0)").
+ arg(glyph);
+ return false;
+ }
+ symbolData.setGlyph(iglyph);
+ }
+
+ if (!src && icode < 0 && iglyph < 0) {
+ m_errorString = "symbol must have either src, code, or glyph attribute";
+ return false;
+ }
+ if (src)
+ symbolData.setSrc(qstrtostr(src));
+
+ QString inversionSrc = attributes.value("inversion-src");
+ if (inversionSrc)
+ symbolData.setInversionSrc(qstrtostr(inversionSrc));
+
+ QString inversionCode = attributes.value("inversion-code");
+ if (inversionCode) {
+ icode = inversionCode.stripWhiteSpace().toInt(&ok);
+ if (!ok || icode < 0) {
+ m_errorString =
+ QString("invalid inversion code attribute \"%1\" (must be integer >= 0)").
+ arg(inversionCode);
+ return false;
+ }
+ symbolData.setInversionCode(icode);
+ }
+
+ QString inversionGlyph = attributes.value("inversion-glyph");
+ if (inversionGlyph) {
+ iglyph = inversionGlyph.stripWhiteSpace().toInt(&ok);
+ if (!ok || iglyph < 0) {
+ m_errorString =
+ QString("invalid inversion glyph attribute \"%1\" (must be integer >= 0)").
+ arg(inversionGlyph);
+ return false;
+ }
+ symbolData.setInversionGlyph(iglyph);
+ }
+
+ QString fontId = attributes.value("font-id");
+ if (fontId) {
+ int n = fontId.stripWhiteSpace().toInt(&ok);
+ if (!ok || n < 0) {
+ m_errorString =
+ QString("invalid font-id attribute \"%1\" (must be integer >= 0)").
+ arg(fontId);
+ return false;
+ }
+ symbolData.setFontId(n);
+ }
+
+ m_data[qstrtostr(symbolName.upper())] = symbolData;
+
+ } else if (lcName == "font-hotspots") {
+ }
+ else if (lcName == "hotspot") {
+
+ QString s = attributes.value("name");
+ if (!s) {
+ m_errorString = "name is a required attribute of hotspot";
+ return false;
+ }
+ m_hotspotCharName = qstrtostr(s.upper());
+
+ } else if (lcName == "scaled") {
+
+ if (m_hotspotCharName == "") {
+ m_errorString = "scaled-element must be in hotspot-element";
+ return false;
+ }
+
+ QString s = attributes.value("x");
+ double x = -1.0;
+ if (s)
+ x = qstrtodouble(s);
+
+ s = attributes.value("y");
+ if (!s) {
+ m_errorString = "y is a required attribute of scaled";
+ return false;
+ }
+ double y = qstrtodouble(s);
+
+ HotspotDataMap::iterator i = m_hotspots.find(m_hotspotCharName);
+ if (i == m_hotspots.end()) {
+ m_hotspots[m_hotspotCharName] = HotspotData();
+ i = m_hotspots.find(m_hotspotCharName);
+ }
+
+ i->second.setScaledHotspot(x, y);
+
+ } else if (lcName == "fixed") {
+
+ if (m_hotspotCharName == "") {
+ m_errorString = "fixed-element must be in hotspot-element";
+ return false;
+ }
+
+ QString s = attributes.value("x");
+ int x = 0;
+ if (s)
+ x = s.toInt();
+
+ s = attributes.value("y");
+ int y = 0;
+ if (s)
+ y = s.toInt();
+
+ HotspotDataMap::iterator i = m_hotspots.find(m_hotspotCharName);
+ if (i == m_hotspots.end()) {
+ m_hotspots[m_hotspotCharName] = HotspotData();
+ i = m_hotspots.find(m_hotspotCharName);
+ }
+
+ i->second.addHotspot(0, x, y);
+
+ } else if (lcName == "when") {
+
+ if (m_hotspotCharName == "") {
+ m_errorString = "when-element must be in hotspot-element";
+ return false;
+ }
+
+ QString s = attributes.value("note-height");
+ if (!s) {
+ m_errorString = "note-height is a required attribute of when";
+ return false;
+ }
+ int noteHeight = s.toInt();
+
+ s = attributes.value("x");
+ int x = 0;
+ if (s)
+ x = s.toInt();
+
+ s = attributes.value("y");
+ if (!s) {
+ m_errorString = "y is a required attribute of when";
+ return false;
+ }
+ int y = s.toInt();
+
+ HotspotDataMap::iterator i = m_hotspots.find(m_hotspotCharName);
+ if (i == m_hotspots.end()) {
+ m_hotspots[m_hotspotCharName] = HotspotData();
+ i = m_hotspots.find(m_hotspotCharName);
+ }
+
+ i->second.addHotspot(noteHeight, x, y);
+
+ } else if (lcName == "font-requirements") {
+ }
+ else if (lcName == "font-requirement") {
+
+ QString id = attributes.value("font-id");
+ int n = -1;
+ bool ok = false;
+ if (id) {
+ n = id.stripWhiteSpace().toInt(&ok);
+ if (!ok) {
+ m_errorString =
+ QString("invalid font-id attribute \"%1\" (must be integer >= 0)").
+ arg(id);
+ return false;
+ }
+ } else {
+ m_errorString = "font-id is a required attribute of font-requirement";
+ return false;
+ }
+
+ QString name = attributes.value("name");
+ QString names = attributes.value("names");
+
+ if (name) {
+ if (names) {
+ m_errorString = "font-requirement may have name or names attribute, but not both";
+ return false;
+ }
+
+ SystemFont *font = SystemFont::loadSystemFont
+ (SystemFontSpec(name, 12));
+
+ if (font) {
+ m_systemFontNames[n] = name;
+ delete font;
+ } else {
+ std::cerr << QString("Warning: Unable to load font \"%1\"").arg(name) << std::endl;
+ m_ok = false;
+ }
+
+ } else if (names) {
+
+ bool have = false;
+ QStringList list = QStringList::split(",", names, false);
+ for (QStringList::Iterator i = list.begin(); i != list.end(); ++i) {
+ SystemFont *font = SystemFont::loadSystemFont
+ (SystemFontSpec(*i, 12));
+ if (font) {
+ m_systemFontNames[n] = *i;
+ have = true;
+ delete font;
+ break;
+ }
+ }
+ if (!have) {
+ std::cerr << QString("Warning: Unable to load any of the fonts in \"%1\"").
+ arg(names) << std::endl;
+ m_ok = false;
+ }
+
+ } else {
+ m_errorString = "font-requirement must have either name or names attribute";
+ return false;
+ }
+
+ QString s = attributes.value("strategy").lower();
+ SystemFont::Strategy strategy = SystemFont::PreferGlyphs;
+
+ if (s) {
+ if (s == "prefer-glyphs")
+ strategy = SystemFont::PreferGlyphs;
+ else if (s == "prefer-codes")
+ strategy = SystemFont::PreferCodes;
+ else if (s == "only-glyphs")
+ strategy = SystemFont::OnlyGlyphs;
+ else if (s == "only-codes")
+ strategy = SystemFont::OnlyCodes;
+ else {
+ std::cerr << "Warning: Unknown strategy value " << s
+ << " (known values are prefer-glyphs, prefer-codes,"
+ << " only-glyphs, only-codes)" << std::endl;
+ }
+ }
+
+ m_systemFontStrategies[n] = strategy;
+
+ } else {
+ }
+
+ if (m_characterDestination)
+ *m_characterDestination = "";
+ return true;
+}
+
+bool
+NoteFontMap::error(const QXmlParseException& exception)
+{
+ m_errorString = QString("%1 at line %2, column %3: %4")
+ .arg(exception.message())
+ .arg(exception.lineNumber())
+ .arg(exception.columnNumber())
+ .arg(m_errorString);
+ return QXmlDefaultHandler::error(exception);
+}
+
+bool
+NoteFontMap::fatalError(const QXmlParseException& exception)
+{
+ m_errorString = QString("%1 at line %2, column %3: %4")
+ .arg(exception.message())
+ .arg(exception.lineNumber())
+ .arg(exception.columnNumber())
+ .arg(m_errorString);
+ return QXmlDefaultHandler::fatalError(exception);
+}
+
+std::set<int>
+NoteFontMap::getSizes() const
+{
+ std::set<int> sizes;
+
+ for (SizeDataMap::const_iterator i = m_sizes.begin();
+ i != m_sizes.end(); ++i) {
+ sizes.insert(i->first);
+ }
+
+ return sizes;
+}
+
+std::set<CharName>
+NoteFontMap::getCharNames() const
+{
+ std::set<CharName> names;
+
+ for (SymbolDataMap::const_iterator i = m_data.begin();
+ i != m_data.end(); ++i) {
+ names.insert(i->first);
+ }
+
+ return names;
+}
+
+bool
+NoteFontMap::checkFile(int size, std::string &src) const
+{
+ QString pixmapFileMixedName = QString("%1/%2/%3/%4.xpm")
+ .arg(m_fontDirectory)
+ .arg(strtoqstr(m_srcDirectory))
+ .arg(size)
+ .arg(strtoqstr(src));
+
+ QFileInfo pixmapFileMixedInfo(pixmapFileMixedName);
+
+ if (!pixmapFileMixedInfo.isReadable()) {
+
+ QString pixmapFileLowerName = QString("%1/%2/%3/%4.xpm")
+ .arg(m_fontDirectory)
+ .arg(strtoqstr(m_srcDirectory).lower())
+ .arg(size)
+ .arg(strtoqstr(src));
+
+ QFileInfo pixmapFileLowerInfo(pixmapFileLowerName);
+
+ if (!pixmapFileLowerInfo.isReadable()) {
+ if (pixmapFileMixedName != pixmapFileLowerName) {
+ std::cerr << "Warning: Unable to open pixmap file "
+ << pixmapFileMixedName << " or " << pixmapFileLowerName
+ << std::endl;
+ } else {
+ std::cerr << "Warning: Unable to open pixmap file "
+ << pixmapFileMixedName << std::endl;
+ }
+ return false;
+ } else {
+ src = qstrtostr(pixmapFileLowerName);
+ }
+ } else {
+ src = qstrtostr(pixmapFileMixedName);
+ }
+
+ return true;
+}
+
+bool
+NoteFontMap::hasInversion(int, CharName charName) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+ return i->second.hasInversion();
+}
+
+bool
+NoteFontMap::getSrc(int size, CharName charName, std::string &src) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+
+ src = i->second.getSrc();
+ if (src == "")
+ return false;
+ return checkFile(size, src);
+}
+
+bool
+NoteFontMap::getInversionSrc(int size, CharName charName, std::string &src) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+
+ if (!i->second.hasInversion())
+ return false;
+ src = i->second.getInversionSrc();
+ if (src == "")
+ return false;
+ return checkFile(size, src);
+}
+
+SystemFont *
+NoteFontMap::getSystemFont(int size, CharName charName, int &charBase)
+const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+
+ SizeDataMap::const_iterator si = m_sizes.find(size);
+ if (si == m_sizes.end())
+ return false;
+
+ int fontId = i->second.getFontId();
+
+ unsigned int fontHeight = 0;
+ if (!si->second.getFontHeight(fontId, fontHeight)) {
+ if (fontId == 0 || !si->second.getFontHeight(0, fontHeight)) {
+ fontHeight = size;
+ }
+ }
+
+ SystemFontNameMap::const_iterator fni = m_systemFontNames.find(fontId);
+ if (fontId < 0 || fni == m_systemFontNames.end())
+ return false;
+ QString fontName = fni->second;
+
+ CharBaseMap::const_iterator bi = m_bases.find(fontId);
+ if (bi == m_bases.end())
+ charBase = 0;
+ else
+ charBase = bi->second;
+
+ SystemFontSpec spec(fontName, fontHeight);
+ SystemFontMap::const_iterator fi = m_systemFontCache.find(spec);
+ if (fi != m_systemFontCache.end()) {
+ return fi->second;
+ }
+
+ SystemFont *font = SystemFont::loadSystemFont(spec);
+ if (!font)
+ return 0;
+ m_systemFontCache[spec] = font;
+
+ NOTATION_DEBUG << "NoteFontMap::getFont: loaded font " << fontName
+ << " at pixel size " << fontHeight << endl;
+
+ return font;
+}
+
+SystemFont::Strategy
+NoteFontMap::getStrategy(int, CharName charName) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return SystemFont::PreferGlyphs;
+
+ int fontId = i->second.getFontId();
+ SystemFontStrategyMap::const_iterator si =
+ m_systemFontStrategies.find(fontId);
+
+ if (si != m_systemFontStrategies.end()) {
+ return si->second;
+ }
+
+ return SystemFont::PreferGlyphs;
+}
+
+bool
+NoteFontMap::getCode(int, CharName charName, int &code) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+
+ code = i->second.getCode();
+ return (code >= 0);
+}
+
+bool
+NoteFontMap::getInversionCode(int, CharName charName, int &code) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+
+ code = i->second.getInversionCode();
+ return (code >= 0);
+}
+
+bool
+NoteFontMap::getGlyph(int, CharName charName, int &glyph) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+
+ glyph = i->second.getGlyph();
+ return (glyph >= 0);
+}
+
+bool
+NoteFontMap::getInversionGlyph(int, CharName charName, int &glyph) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+
+ glyph = i->second.getInversionGlyph();
+ return (glyph >= 0);
+}
+
+bool
+NoteFontMap::getStaffLineThickness(int size, unsigned int &thickness) const
+{
+ SizeDataMap::const_iterator i = m_sizes.find(size);
+ if (i == m_sizes.end())
+ return false;
+
+ return i->second.getStaffLineThickness(thickness);
+}
+
+bool
+NoteFontMap::getLegerLineThickness(int size, unsigned int &thickness) const
+{
+ SizeDataMap::const_iterator i = m_sizes.find(size);
+ if (i == m_sizes.end())
+ return false;
+
+ return i->second.getLegerLineThickness(thickness);
+}
+
+bool
+NoteFontMap::getStemThickness(int size, unsigned int &thickness) const
+{
+ SizeDataMap::const_iterator i = m_sizes.find(size);
+ if (i == m_sizes.end())
+ return false;
+
+ return i->second.getStemThickness(thickness);
+}
+
+bool
+NoteFontMap::getBeamThickness(int size, unsigned int &thickness) const
+{
+ SizeDataMap::const_iterator i = m_sizes.find(size);
+ if (i == m_sizes.end())
+ return false;
+
+ return i->second.getBeamThickness(thickness);
+}
+
+bool
+NoteFontMap::getStemLength(int size, unsigned int &length) const
+{
+ SizeDataMap::const_iterator i = m_sizes.find(size);
+ if (i == m_sizes.end())
+ return false;
+
+ return i->second.getStemLength(length);
+}
+
+bool
+NoteFontMap::getFlagSpacing(int size, unsigned int &spacing) const
+{
+ SizeDataMap::const_iterator i = m_sizes.find(size);
+ if (i == m_sizes.end())
+ return false;
+
+ return i->second.getFlagSpacing(spacing);
+}
+
+bool
+NoteFontMap::getHotspot(int size, CharName charName, int width, int height,
+ int &x, int &y) const
+{
+ HotspotDataMap::const_iterator i = m_hotspots.find(charName);
+ if (i == m_hotspots.end())
+ return false;
+ return i->second.getHotspot(size, width, height, x, y);
+}
+
+bool
+NoteFontMap::HotspotData::getHotspot(int size, int width, int height,
+ int &x, int &y) const
+{
+ DataMap::const_iterator i = m_data.find(size);
+ if (i == m_data.end()) {
+ i = m_data.find(0); // fixed-pixel hotspot
+ x = 0;
+ if (m_scaled.first >= 0) {
+ x = toSize(width, m_scaled.first, false);
+ } else {
+ if (i != m_data.end()) {
+ x = i->second.first;
+ }
+ }
+ if (m_scaled.second >= 0) {
+ y = toSize(height, m_scaled.second, false);
+ return true;
+ } else {
+ if (i != m_data.end()) {
+ y = i->second.second;
+ return true;
+ }
+ return false;
+ }
+ }
+ x = i->second.first;
+ y = i->second.second;
+ return true;
+}
+
+QStringList
+NoteFontMap::getSystemFontNames() const
+{
+ QStringList names;
+ for (SystemFontNameMap::const_iterator i = m_systemFontNames.begin();
+ i != m_systemFontNames.end(); ++i) {
+ names.append(i->second);
+ }
+ return names;
+}
+
+void
+NoteFontMap::dump() const
+{
+ // debug code
+
+ std::cout << "Font data:\nName: " << getName() << "\nOrigin: " << getOrigin()
+ << "\nCopyright: " << getCopyright() << "\nMapped by: "
+ << getMappedBy() << "\nType: " << getType()
+ << "\nSmooth: " << isSmooth() << std::endl;
+
+ std::set<int> sizes = getSizes();
+ std::set<CharName> names = getCharNames();
+
+ for (std::set<int>::iterator sizei = sizes.begin(); sizei != sizes.end();
+ ++sizei) {
+
+ std::cout << "\nSize: " << *sizei << "\n" << std::endl;
+
+ unsigned int t = 0;
+
+ if (getStaffLineThickness(*sizei, t)) {
+ std::cout << "Staff line thickness: " << t << std::endl;
+ }
+
+ if (getLegerLineThickness(*sizei, t)) {
+ std::cout << "Leger line thickness: " << t << std::endl;
+ }
+
+ if (getStemThickness(*sizei, t)) {
+ std::cout << "Stem thickness: " << t << std::endl;
+ }
+
+ if (getBeamThickness(*sizei, t)) {
+ std::cout << "Beam thickness: " << t << std::endl;
+ }
+
+ if (getStemLength(*sizei, t)) {
+ std::cout << "Stem length: " << t << std::endl;
+ }
+
+ if (getFlagSpacing(*sizei, t)) {
+ std::cout << "Flag spacing: " << t << std::endl;
+ }
+
+ for (std::set<CharName>::iterator namei = names.begin();
+ namei != names.end(); ++namei) {
+
+ std::cout << "\nCharacter: " << namei->c_str() << std::endl;
+
+ std::string s;
+ int x, y, c;
+
+ if (getSrc(*sizei, *namei, s)) {
+ std::cout << "Src: " << s << std::endl;
+ }
+
+ if (getInversionSrc(*sizei, *namei, s)) {
+ std::cout << "Inversion src: " << s << std::endl;
+ }
+
+ if (getCode(*sizei, *namei, c)) {
+ std::cout << "Code: " << c << std::endl;
+ }
+
+ if (getInversionCode(*sizei, *namei, c)) {
+ std::cout << "Inversion code: " << c << std::endl;
+ }
+
+ if (getGlyph(*sizei, *namei, c)) {
+ std::cout << "Glyph: " << c << std::endl;
+ }
+
+ if (getInversionGlyph(*sizei, *namei, c)) {
+ std::cout << "Inversion glyph: " << c << std::endl;
+ }
+
+ if (getHotspot(*sizei, *namei, 1, 1, x, y)) {
+ std::cout << "Hot spot: (" << x << "," << y << ")" << std::endl;
+ }
+ }
+ }
+}
+
+}
diff --git a/src/gui/editors/notation/NoteFontMap.h b/src/gui/editors/notation/NoteFontMap.h
new file mode 100644
index 0000000..119db76
--- /dev/null
+++ b/src/gui/editors/notation/NoteFontMap.h
@@ -0,0 +1,333 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTEFONTMAP_H_
+#define _RG_NOTEFONTMAP_H_
+
+#include "base/Exception.h"
+#include <map>
+#include <set>
+#include <string>
+#include "SystemFont.h"
+#include <qstring.h>
+#include <qstringlist.h>
+#include <utility>
+#include <qxml.h>
+#include "gui/editors/notation/NoteCharacterNames.h"
+
+
+class QXmlParseException;
+class QXmlAttributes;
+
+
+namespace Rosegarden
+{
+
+
+
+class NoteFontMap : public QXmlDefaultHandler
+{
+public:
+ typedef Exception MappingFileReadFailed;
+
+ NoteFontMap(std::string name); // load and parse the XML mapping file
+ ~NoteFontMap();
+
+ /**
+ * ok() returns false if the file read succeeded but the font
+ * relies on system fonts that are not available. (If the file
+ * read fails, the constructor throws MappingFileReadFailed.)
+ */
+ bool ok() const { return m_ok; }
+
+ std::string getName() const { return m_name; }
+ std::string getOrigin() const { return m_origin; }
+ std::string getCopyright() const { return m_copyright; }
+ std::string getMappedBy() const { return m_mappedBy; }
+ std::string getType() const { return m_type; }
+ bool isSmooth() const { return m_smooth; }
+
+ std::set<int> getSizes() const;
+ std::set<CharName> getCharNames() const;
+
+ bool getStaffLineThickness(int size, unsigned int &thickness) const;
+ bool getLegerLineThickness(int size, unsigned int &thickness) const;
+ bool getStemThickness(int size, unsigned int &thickness) const;
+ bool getBeamThickness(int size, unsigned int &thickness) const;
+ bool getStemLength(int size, unsigned int &length) const;
+ bool getFlagSpacing(int size, unsigned int &spacing) const;
+
+ bool hasInversion(int size, CharName charName) const;
+
+ bool getSrc(int size, CharName charName, std::string &src) const;
+ bool getInversionSrc(int size, CharName charName, std::string &src) const;
+
+ SystemFont *getSystemFont(int size, CharName charName, int &charBase) const;
+ SystemFont::Strategy getStrategy(int size, CharName charName) const;
+ bool getCode(int size, CharName charName, int &code) const;
+ bool getInversionCode(int size, CharName charName, int &code) const;
+ bool getGlyph(int size, CharName charName, int &glyph) const;
+ bool getInversionGlyph(int size, CharName charName, int &glyph) const;
+
+ bool getHotspot(int size, CharName charName, int width, int height,
+ int &x, int &y) const;
+
+ // Xml handler methods:
+
+ virtual bool startElement
+ (const QString& namespaceURI, const QString& localName,
+ const QString& qName, const QXmlAttributes& atts);
+
+ virtual bool characters(QString &);
+
+ bool error(const QXmlParseException& exception);
+ bool fatalError(const QXmlParseException& exception);
+
+ void dump() const;
+
+ // Not for general use, but very handy for diagnostic display
+ QStringList getSystemFontNames() const;
+
+ // want this to be private, but need access from HotspotData
+ static int toSize(int baseSize, double factor, bool limitAtOne);
+
+private:
+ class SymbolData
+ {
+ public:
+ SymbolData() : m_fontId(0),
+ m_src(""), m_inversionSrc(""),
+ m_code(-1), m_inversionCode(-1),
+ m_glyph(-1), m_inversionGlyph(-1) { }
+ ~SymbolData() { }
+
+ void setFontId(int id) { m_fontId = id; }
+ int getFontId() const { return m_fontId; }
+
+ void setSrc(std::string src) { m_src = src; }
+ std::string getSrc() const { return m_src; }
+
+ void setCode(int code) { m_code = code; }
+ int getCode() const { return m_code; }
+
+ void setGlyph(int glyph) { m_glyph = glyph; }
+ int getGlyph() const { return m_glyph; }
+
+ void setInversionSrc(std::string inversion) { m_inversionSrc = inversion; }
+ std::string getInversionSrc() const { return m_inversionSrc; }
+
+ void setInversionCode(int code) { m_inversionCode = code; }
+ int getInversionCode() const { return m_inversionCode; }
+
+ void setInversionGlyph(int glyph) { m_inversionGlyph = glyph; }
+ int getInversionGlyph() const { return m_inversionGlyph; }
+
+ bool hasInversion() const {
+ return m_inversionGlyph >= 0 ||
+ m_inversionCode >= 0 ||
+ m_inversionSrc != "";
+ }
+
+ private:
+ int m_fontId;
+ std::string m_src;
+ std::string m_inversionSrc;
+ int m_code;
+ int m_inversionCode;
+ int m_glyph;
+ int m_inversionGlyph;
+ };
+
+ class HotspotData
+ {
+ private:
+ typedef std::pair<int, int> Point;
+ typedef std::pair<double, double> ScaledPoint;
+ typedef std::map<int, Point> DataMap;
+
+ public:
+ HotspotData() : m_scaled(-1.0, -1.0) { }
+ ~HotspotData() { }
+
+ void addHotspot(int size, int x, int y) {
+ m_data[size] = Point(x, y);
+ }
+
+ void setScaledHotspot(double x, double y) {
+ m_scaled = ScaledPoint(x, y);
+ }
+
+ bool getHotspot(int size, int width, int height, int &x, int &y) const;
+
+ private:
+ DataMap m_data;
+ ScaledPoint m_scaled;
+ };
+
+ class SizeData
+ {
+ public:
+ SizeData() : m_stemThickness(-1),
+ m_beamThickness(-1),
+ m_stemLength(-1),
+ m_flagSpacing(-1),
+ m_staffLineThickness(-1),
+ m_legerLineThickness(-1) { }
+ ~SizeData() { }
+
+ void setStemThickness(unsigned int i) {
+ m_stemThickness = (int)i;
+ }
+ void setBeamThickness(unsigned int i) {
+ m_beamThickness = (int)i;
+ }
+ void setStemLength(unsigned int i) {
+ m_stemLength = (int)i;
+ }
+ void setFlagSpacing(unsigned int i) {
+ m_flagSpacing = (int)i;
+ }
+ void setStaffLineThickness(unsigned int i) {
+ m_staffLineThickness = (int)i;
+ }
+ void setLegerLineThickness(unsigned int i) {
+ m_legerLineThickness = (int)i;
+ }
+ void setFontHeight(int fontId, unsigned int h) {
+ m_fontHeights[fontId] = (int)h;
+ }
+
+ bool getStemThickness(unsigned int &i) const {
+ if (m_stemThickness >= 0) {
+ i = (unsigned int)m_stemThickness;
+ return true;
+ } else return false;
+ }
+
+ bool getBeamThickness(unsigned int &i) const {
+ if (m_beamThickness >= 0) {
+ i = (unsigned int)m_beamThickness;
+ return true;
+ } else return false;
+ }
+
+ bool getStemLength(unsigned int &i) const {
+ if (m_stemLength >= 0) {
+ i = (unsigned int)m_stemLength;
+ return true;
+ } else return false;
+ }
+
+ bool getFlagSpacing(unsigned int &i) const {
+ if (m_flagSpacing >= 0) {
+ i = (unsigned int)m_flagSpacing;
+ return true;
+ } else return false;
+ }
+
+ bool getStaffLineThickness(unsigned int &i) const {
+ if (m_staffLineThickness >= 0) {
+ i = (unsigned int)m_staffLineThickness;
+ return true;
+ } else return false;
+ }
+
+ bool getLegerLineThickness(unsigned int &i) const {
+ if (m_legerLineThickness >= 0) {
+ i = (unsigned int)m_legerLineThickness;
+ return true;
+ } else return false;
+ }
+
+ bool getFontHeight(int fontId, unsigned int &h) const {
+ std::map<int, int>::const_iterator fhi = m_fontHeights.find(fontId);
+ if (fhi != m_fontHeights.end()) {
+ h = (unsigned int)fhi->second;
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ int m_stemThickness;
+ int m_beamThickness;
+ int m_stemLength;
+ int m_flagSpacing;
+ int m_staffLineThickness;
+ int m_legerLineThickness;
+ std::map<int, int> m_fontHeights; // per-font-id
+ };
+
+ //--------------- Data members ---------------------------------
+
+ std::string m_name;
+ std::string m_origin;
+ std::string m_copyright;
+ std::string m_mappedBy;
+ std::string m_type;
+ bool m_smooth;
+
+ std::string m_srcDirectory;
+
+ typedef std::map<CharName, SymbolData> SymbolDataMap;
+ SymbolDataMap m_data;
+
+ typedef std::map<CharName, HotspotData> HotspotDataMap;
+ HotspotDataMap m_hotspots;
+
+ typedef std::map<int, SizeData> SizeDataMap;
+ SizeDataMap m_sizes;
+
+ typedef std::map<int, QString> SystemFontNameMap;
+ SystemFontNameMap m_systemFontNames;
+
+ typedef std::map<int, SystemFont::Strategy> SystemFontStrategyMap;
+ SystemFontStrategyMap m_systemFontStrategies;
+
+ typedef std::map<SystemFontSpec, SystemFont *> SystemFontMap;
+ mutable SystemFontMap m_systemFontCache;
+
+ typedef std::map<int, int> CharBaseMap;
+ CharBaseMap m_bases;
+
+ // For use when reading the XML file:
+ bool m_expectingCharacters;
+ std::string *m_characterDestination;
+ std::string m_hotspotCharName;
+ QString m_errorString;
+
+ bool checkFile(int size, std::string &src) const;
+ QString m_fontDirectory;
+
+ bool m_ok;
+};
+
+
+class NoteCharacterDrawRep;
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteFontViewer.cpp b/src/gui/editors/notation/NoteFontViewer.cpp
new file mode 100644
index 0000000..81f07e9
--- /dev/null
+++ b/src/gui/editors/notation/NoteFontViewer.cpp
@@ -0,0 +1,125 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NoteFontViewer.h"
+
+#include <klocale.h>
+#include "FontViewFrame.h"
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <ktoolbar.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+void
+NoteFontViewer::slotViewChanged(int i)
+{
+ m_frame->setGlyphs(i == 0);
+
+ m_rows->clear();
+ int firstRow = -1;
+
+ for (int r = 0; r < 256; ++r) {
+ if (m_frame->hasRow(r)) {
+ m_rows->insertItem(QString("%1").arg(r));
+ if (firstRow < 0)
+ firstRow = r;
+ }
+ }
+
+ if (firstRow < 0) {
+ m_rows->setEnabled(false);
+ m_frame->setRow(0);
+ } else {
+ m_rows->setEnabled(true);
+ m_frame->setRow(firstRow);
+ }
+}
+
+void
+NoteFontViewer::slotRowChanged(const QString &s)
+{
+ bool ok;
+ int i = s.toInt(&ok);
+ if (ok)
+ m_frame->setRow(i);
+}
+
+void
+NoteFontViewer::slotFontChanged(const QString &s)
+{
+ m_frame->setFont(s);
+ slotViewChanged(m_view->currentItem());
+}
+
+NoteFontViewer::NoteFontViewer(QWidget *parent, QString noteFontName,
+ QStringList fontNames, int pixelSize) :
+ KDialogBase(parent, 0, true,
+ i18n("Note Font Viewer: %1").arg(noteFontName), Close)
+{
+ QVBox *box = makeVBoxMainWidget();
+ KToolBar* controls = new KToolBar(box);
+ controls->setMargin(3);
+
+ (void) new QLabel(i18n(" Component: "), controls);
+ m_font = new KComboBox(controls);
+
+ for (QStringList::iterator i = fontNames.begin(); i != fontNames.end();
+ ++i) {
+ m_font->insertItem(*i);
+ }
+
+ (void) new QLabel(i18n(" View: "), controls);
+ m_view = new KComboBox(controls);
+
+ m_view->insertItem(i18n("Glyphs"));
+ m_view->insertItem(i18n("Codes"));
+
+ (void) new QLabel(i18n(" Page: "), controls);
+ m_rows = new KComboBox(controls);
+
+ m_frame = new FontViewFrame(pixelSize, box);
+
+ connect(m_font, SIGNAL(activated(const QString &)),
+ this, SLOT(slotFontChanged(const QString &)));
+
+ connect(m_view, SIGNAL(activated(int)),
+ this, SLOT(slotViewChanged(int)));
+
+ connect(m_rows, SIGNAL(activated(const QString &)),
+ this, SLOT(slotRowChanged(const QString &)));
+
+ slotFontChanged(m_font->currentText());
+}
+
+}
+#include "NoteFontViewer.moc"
diff --git a/src/gui/editors/notation/NoteFontViewer.h b/src/gui/editors/notation/NoteFontViewer.h
new file mode 100644
index 0000000..31c8613
--- /dev/null
+++ b/src/gui/editors/notation/NoteFontViewer.h
@@ -0,0 +1,68 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTEFONTVIEWER_H_
+#define _RG_NOTEFONTVIEWER_H_
+
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+
+class QWidget;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class FontViewFrame;
+
+
+class NoteFontViewer : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ NoteFontViewer(QWidget *parent, QString noteFontName,
+ QStringList systemFontNames, int pixelSize);
+
+protected slots:
+ void slotFontChanged(const QString &);
+ void slotViewChanged(int);
+ void slotRowChanged(const QString &);
+
+private:
+ KComboBox *m_font;
+ KComboBox *m_view;
+ KComboBox *m_rows;
+ FontViewFrame *m_frame;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteInserter.cpp b/src/gui/editors/notation/NoteInserter.cpp
new file mode 100644
index 0000000..66adafe
--- /dev/null
+++ b/src/gui/editors/notation/NoteInserter.cpp
@@ -0,0 +1,722 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NoteInserter.h"
+#include "misc/Debug.h"
+#include <kapplication.h>
+
+#include "base/BaseProperties.h"
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/Event.h"
+#include "base/NotationTypes.h"
+#include "base/Segment.h"
+#include "base/Staff.h"
+#include "base/ViewElement.h"
+#include "commands/notation/NoteInsertionCommand.h"
+#include "commands/notation/RestInsertionCommand.h"
+#include "commands/notation/TupletCommand.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/LinedStaff.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "NotationProperties.h"
+#include "NotationStrings.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NotationStaff.h"
+#include "NotePixmapFactory.h"
+#include "NoteStyleFactory.h"
+#include <kaction.h>
+#include <kcommand.h>
+#include <kconfig.h>
+#include <qiconset.h>
+#include <qregexp.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+NoteInserter::NoteInserter(NotationView* view)
+ : NotationTool("NoteInserter", view),
+ m_noteType(Note::Quaver),
+ m_noteDots(0),
+ m_autoBeam(true),
+ m_accidental(Accidentals::NoAccidental),
+ m_lastAccidental(Accidentals::NoAccidental),
+ m_followAccidental(false)
+{
+ QIconSet icon;
+
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+ m_autoBeam = config->readBoolEntry("autobeam", true);
+ m_matrixInsertType = (config->readNumEntry("inserttype", 0) > 0);
+ m_defaultStyle = qstrtostr(config->readEntry
+ ("style", strtoqstr(NoteStyleFactory::DefaultStyle)));
+
+ KToggleAction *autoBeamAction =
+ new KToggleAction(i18n("Auto-Beam when appropriate"), 0, this,
+ SLOT(slotToggleAutoBeam()), actionCollection(),
+ "toggle_auto_beam");
+ autoBeamAction->setChecked(m_autoBeam);
+
+ for (unsigned int i = 0; i < 6; ++i) {
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap(m_actionsAccidental[i][3])));
+ KRadioAction* noteAction = new KRadioAction(i18n(m_actionsAccidental[i][0]),
+ icon, 0, this,
+ m_actionsAccidental[i][1],
+ actionCollection(),
+ m_actionsAccidental[i][2]);
+ noteAction->setExclusiveGroup("accidentals");
+ }
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("dotted-crotchet")));
+ new KToggleAction(i18n("Dotted note"), icon, 0, this,
+ SLOT(slotToggleDot()), actionCollection(),
+ "toggle_dot");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("select")));
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("rest-crotchet")));
+ new KAction(i18n("Switch to Inserting Rests"), icon, 0, this,
+ SLOT(slotRestsSelected()), actionCollection(),
+ "rests");
+
+ createMenu("noteinserter.rc");
+
+ connect(m_parentView, SIGNAL(changeAccidental(Accidental, bool)),
+ this, SLOT(slotSetAccidental(Accidental, bool)));
+}
+
+NoteInserter::NoteInserter(const QString& menuName, NotationView* view)
+ : NotationTool(menuName, view),
+ m_noteType(Note::Quaver),
+ m_noteDots(0),
+ m_autoBeam(false),
+ m_clickHappened(false),
+ m_accidental(Accidentals::NoAccidental),
+ m_lastAccidental(Accidentals::NoAccidental),
+ m_followAccidental(false)
+{
+ connect(m_parentView, SIGNAL(changeAccidental(Accidental, bool)),
+ this, SLOT(slotSetAccidental(Accidental, bool)));
+}
+
+NoteInserter::~NoteInserter()
+{}
+
+void NoteInserter::ready()
+{
+ m_clickHappened = false;
+ m_nParentView->setCanvasCursor(Qt::crossCursor);
+ m_nParentView->setHeightTracking(true);
+}
+
+void
+NoteInserter::handleLeftButtonPress(timeT,
+ int,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement*)
+{
+ if (staffNo < 0)
+ return ;
+ computeLocationAndPreview(e);
+}
+
+int
+NoteInserter::handleMouseMove(timeT,
+ int,
+ QMouseEvent *e)
+{
+ if (m_clickHappened) {
+ computeLocationAndPreview(e);
+ }
+
+ return RosegardenCanvasView::NoFollow;
+}
+
+void
+NoteInserter::handleMouseRelease(timeT,
+ int,
+ QMouseEvent *e)
+{
+ if (!m_clickHappened)
+ return ;
+ bool okay = computeLocationAndPreview(e);
+ m_clickHappened = false;
+ if (!okay)
+ return ;
+ clearPreview();
+
+ Note note(m_noteType, m_noteDots);
+ timeT endTime = m_clickTime + note.getDuration();
+ Segment &segment = m_nParentView->getStaff(m_clickStaffNo)->getSegment();
+
+ Segment::iterator realEnd = segment.findTime(endTime);
+ if (!segment.isBeforeEndMarker( realEnd) ||
+ !segment.isBeforeEndMarker(++realEnd)) {
+ endTime = segment.getEndMarkerTime();
+ } else {
+ endTime = std::max(endTime, (*realEnd)->getNotationAbsoluteTime());
+ }
+
+ Event *lastInsertedEvent = doAddCommand
+ (segment, m_clickTime, endTime, note, m_clickPitch,
+ (m_accidental == Accidentals::NoAccidental &&
+ m_followAccidental) ?
+ m_lastAccidental : m_accidental);
+
+ if (lastInsertedEvent) {
+
+ m_nParentView->setSingleSelectedEvent
+ (m_clickStaffNo, lastInsertedEvent);
+
+ if (m_nParentView->isInChordMode()) {
+ m_nParentView->slotSetInsertCursorAndRecentre
+ (lastInsertedEvent->getAbsoluteTime(), e->x(), (int)e->y(),
+ false);
+ } else {
+ m_nParentView->slotSetInsertCursorAndRecentre
+ (lastInsertedEvent->getAbsoluteTime() +
+ lastInsertedEvent->getDuration(), e->x(), (int)e->y(),
+ false);
+ }
+ }
+}
+
+void
+NoteInserter::insertNote(Segment &segment, timeT insertionTime,
+ int pitch, Accidental accidental,
+ bool suppressPreview)
+{
+ Note note(m_noteType, m_noteDots);
+ timeT endTime = insertionTime + note.getDuration();
+
+ Segment::iterator realEnd = segment.findTime(endTime);
+ if (!segment.isBeforeEndMarker( realEnd) ||
+ !segment.isBeforeEndMarker(++realEnd)) {
+ endTime = segment.getEndMarkerTime();
+ } else {
+ endTime = std::max(endTime, (*realEnd)->getNotationAbsoluteTime());
+ }
+
+ Event *lastInsertedEvent = doAddCommand
+ (segment, insertionTime, endTime, note, pitch, accidental);
+
+ if (lastInsertedEvent) {
+
+ m_nParentView->setSingleSelectedEvent(segment, lastInsertedEvent);
+
+ if (m_nParentView->isInChordMode()) {
+ m_nParentView->slotSetInsertCursorPosition
+ (lastInsertedEvent->getAbsoluteTime(), true, false);
+ } else {
+ m_nParentView->slotSetInsertCursorPosition
+ (lastInsertedEvent->getAbsoluteTime() +
+ lastInsertedEvent->getDuration(), true, false);
+ }
+ }
+
+ if (!suppressPreview)
+ m_nParentView->playNote(segment, pitch);
+}
+
+bool
+NoteInserter::computeLocationAndPreview(QMouseEvent *e)
+{
+ double x = e->x();
+ int y = (int)e->y();
+
+ LinedStaff *staff = m_nParentView->getStaffForCanvasCoords(e->x(), y);
+ if (!staff) {
+ clearPreview();
+ return false;
+ }
+
+ int staffNo = staff->getId();
+ if (m_clickHappened && staffNo != m_clickStaffNo) {
+ // abandon
+ clearPreview();
+ return false;
+ }
+
+ // If we're inserting grace notes, then we need to "dress to the
+ // right", as it were
+ bool grace = m_nParentView->isInGraceMode();
+
+ int height = staff->getHeightAtCanvasCoords(x, y);
+
+ Event *clefEvt = 0, *keyEvt = 0;
+ Clef clef;
+ Rosegarden::Key key;
+
+ NotationElementList::iterator itr =
+ staff->getElementUnderCanvasCoords(x, y, clefEvt, keyEvt);
+ if (itr == staff->getViewElementList()->end()) {
+ clearPreview();
+ return false;
+ }
+
+ NotationElement* el = static_cast<NotationElement*>(*itr);
+
+ timeT time = el->event()->getAbsoluteTime(); // not getViewAbsoluteTime()
+ m_clickInsertX = el->getLayoutX();
+ if (clefEvt)
+ clef = Clef(*clefEvt);
+ if (keyEvt)
+ key = Rosegarden::Key(*keyEvt);
+
+ int subordering = el->event()->getSubOrdering();
+ float targetSubordering = subordering;
+
+ if (grace && el->getCanvasItem()) {
+
+ NotationStaff *ns = dynamic_cast<NotationStaff *>(staff);
+ if (!ns) {
+ std::cerr << "WARNING: NoteInserter: Staff is not a NotationStaff"
+ << std::endl;
+ } else {
+ std::cerr << "x=" << x << ", el->getCanvasX()=" << el->getCanvasX() << std::endl;
+ if (el->isRest()) std::cerr << "elt is a rest" << std::endl;
+ if (x - el->getCanvasX() >
+ ns->getNotePixmapFactory(false).getNoteBodyWidth()) {
+ NotationElementList::iterator j(itr);
+ while (++j != staff->getViewElementList()->end()) {
+ NotationElement *candidate = static_cast<NotationElement *>(*j);
+ if ((candidate->isNote() || candidate->isRest()) &&
+ (candidate->getViewAbsoluteTime()
+ > el->getViewAbsoluteTime() ||
+ candidate->event()->getSubOrdering()
+ > el->event()->getSubOrdering())) {
+ itr = j;
+ el = candidate;
+ m_clickInsertX = el->getLayoutX();
+ time = el->event()->getAbsoluteTime();
+ subordering = el->event()->getSubOrdering();
+ targetSubordering = subordering;
+ break;
+ }
+ }
+ }
+ }
+
+ if (x - el->getCanvasX() < 1) {
+ targetSubordering -= 0.5;
+ }
+ }
+
+ if (el->isRest() && el->getCanvasItem()) {
+ time += getOffsetWithinRest(staffNo, itr, x);
+ m_clickInsertX += (x - el->getCanvasX());
+ }
+
+ Pitch p(height, clef, key, m_accidental);
+ int pitch = p.getPerformancePitch();
+
+ // [RFE 987960] When inserting via mouse, if no accidental is
+ // selected, we use the same accidental (and thus the same pitch)
+ // as of the previous note found at this height -- iff such a note
+ // is found more recently than the last key signature.
+
+ if (m_accidental == Accidentals::NoAccidental &&
+ m_followAccidental) {
+ Segment &segment = staff->getSegment();
+ m_lastAccidental = m_accidental;
+ Segment::iterator i = segment.findNearestTime(time);
+ while (i != segment.end()) {
+ if ((*i)->isa(Rosegarden::Key::EventType)) break;
+ if ((*i)->isa(Note::EventType)) {
+ if ((*i)->has(NotationProperties::HEIGHT_ON_STAFF) &&
+ (*i)->has(BaseProperties::PITCH)) {
+ int h = (*i)->get<Int>(NotationProperties::HEIGHT_ON_STAFF);
+ if (h == height) {
+ pitch = (*i)->get<Int>(BaseProperties::PITCH);
+ (*i)->get<String>(BaseProperties::ACCIDENTAL,
+ m_lastAccidental);
+ break;
+ }
+ }
+ }
+ if (i == segment.begin()) break;
+ --i;
+ }
+ }
+
+ bool changed = false;
+
+ if (m_clickHappened) {
+ if (time != m_clickTime ||
+ subordering != m_clickSubordering ||
+ pitch != m_clickPitch ||
+ height != m_clickHeight ||
+ staffNo != m_clickStaffNo) {
+ changed = true;
+ }
+ } else {
+ m_clickHappened = true;
+ changed = true;
+ }
+
+ if (changed) {
+ m_clickTime = time;
+ m_clickSubordering = subordering;
+ m_clickPitch = pitch;
+ m_clickHeight = height;
+ m_clickStaffNo = staffNo;
+ m_targetSubordering = targetSubordering;
+
+ showPreview();
+ }
+
+ return true;
+}
+
+void NoteInserter::showPreview()
+{
+ Segment &segment = m_nParentView->getStaff(m_clickStaffNo)->getSegment();
+
+ int pitch = m_clickPitch;
+ pitch += getOttavaShift(segment, m_clickTime) * 12;
+
+ m_nParentView->showPreviewNote(m_clickStaffNo, m_clickInsertX,
+ pitch, m_clickHeight,
+ Note(m_noteType, m_noteDots),
+ m_nParentView->isInGraceMode());
+}
+
+void NoteInserter::clearPreview()
+{
+ m_nParentView->clearPreviewNote();
+}
+
+timeT
+NoteInserter::getOffsetWithinRest(int staffNo,
+ const NotationElementList::iterator &i,
+ double &canvasX) // will be snapped
+{
+ //!!! To make this work correctly in tuplet mode, our divisor would
+ // have to be the tupletified duration of the tuplet unit -- we can
+ // do that, we just haven't yet
+ if (m_nParentView->isInTripletMode())
+ return 0;
+
+ Staff *staff = m_nParentView->getStaff(staffNo);
+ NotationElement* el = static_cast<NotationElement*>(*i);
+ if (!el->getCanvasItem())
+ return 0;
+ double offset = canvasX - el->getCanvasX();
+
+ if (offset < 0)
+ return 0;
+
+ double airX, airWidth;
+ el->getLayoutAirspace(airX, airWidth);
+ double origin = ((*i)->getLayoutX() - airX) / 2;
+ double width = airWidth - origin;
+
+ timeT duration = (*i)->getViewDuration();
+
+ TimeSignature timeSig =
+ staff->getSegment().getComposition()->getTimeSignatureAt
+ ((*i)->event()->getAbsoluteTime());
+ timeT unit = timeSig.getUnitDuration();
+
+ int unitCount = duration / unit;
+ if (unitCount > 1) {
+
+ timeT result = (int)((offset / width) * unitCount);
+ if (result > unitCount - 1)
+ result = unitCount - 1;
+
+ double visibleWidth(airWidth);
+ NotationElementList::iterator j(i);
+ if (++j != staff->getViewElementList()->end()) {
+ visibleWidth = (*j)->getLayoutX() - (*i)->getLayoutX();
+ }
+ offset = (visibleWidth * result) / unitCount;
+ canvasX = el->getCanvasX() + offset;
+
+ result *= unit;
+ return result;
+ }
+
+ return 0;
+}
+
+int
+NoteInserter::getOttavaShift(Segment &segment, timeT time)
+{
+ // Find out whether we're in an ottava section.
+
+ int ottavaShift = 0;
+
+ for (Segment::iterator i = segment.findTime(time); ; --i) {
+
+ if (!segment.isBeforeEndMarker(i)) {
+ break;
+ }
+
+ if ((*i)->isa(Indication::EventType)) {
+ try {
+ Indication ind(**i);
+ if (ind.isOttavaType()) {
+ timeT endTime =
+ (*i)->getNotationAbsoluteTime() +
+ (*i)->getNotationDuration();
+ if (time < endTime) {
+ ottavaShift = ind.getOttavaShift();
+ }
+ break;
+ }
+ } catch (...) { }
+ }
+
+ if (i == segment.begin()) {
+ break;
+ }
+ }
+
+ return ottavaShift;
+}
+
+Event *
+NoteInserter::doAddCommand(Segment &segment, timeT time, timeT endTime,
+ const Note &note, int pitch, Accidental accidental)
+{
+ timeT noteEnd = time + note.getDuration();
+
+ // #1046934: make it possible to insert triplet at end of segment!
+ if (m_nParentView->isInTripletMode()) {
+ noteEnd = time + (note.getDuration() * 2 / 3);
+ }
+
+ if (time < segment.getStartTime() ||
+ endTime > segment.getEndMarkerTime() ||
+ noteEnd > segment.getEndMarkerTime()) {
+ return 0;
+ }
+
+ pitch += getOttavaShift(segment, time) * 12;
+
+ float targetSubordering = 0;
+ if (m_nParentView->isInGraceMode()) {
+ targetSubordering = m_targetSubordering;
+ }
+
+ NoteInsertionCommand *insertionCommand =
+ new NoteInsertionCommand
+ (segment, time, endTime, note, pitch, accidental,
+ (m_autoBeam && !m_nParentView->isInTripletMode() && !m_nParentView->isInGraceMode()) ?
+ NoteInsertionCommand::AutoBeamOn : NoteInsertionCommand::AutoBeamOff,
+ m_matrixInsertType && !m_nParentView->isInGraceMode() ?
+ NoteInsertionCommand::MatrixModeOn : NoteInsertionCommand::MatrixModeOff,
+ m_nParentView->isInGraceMode() ?
+ (m_nParentView->isInTripletMode() ?
+ NoteInsertionCommand::GraceAndTripletModesOn :
+ NoteInsertionCommand::GraceModeOn)
+ : NoteInsertionCommand::GraceModeOff,
+ targetSubordering,
+ m_defaultStyle);
+
+ KCommand *activeCommand = insertionCommand;
+
+ if (m_nParentView->isInTripletMode() && !m_nParentView->isInGraceMode()) {
+ Segment::iterator i(segment.findTime(time));
+ if (i != segment.end() &&
+ !(*i)->has(BaseProperties::BEAMED_GROUP_TUPLET_BASE)) {
+
+ KMacroCommand *command = new KMacroCommand(insertionCommand->name());
+
+ //## Attempted fix to bug reported on rg-user by SlowPic
+ //## <[email protected]> 28/02/2005 22:32:56 UTC: Triplet input error
+ //# HJJ: Comment out this attempt. It breaks the splitting of
+ //# the first bars into rests.
+ //## if ((*i)->isa(Note::EventRestType) &&
+ //## (*i)->getNotationDuration() > (note.getDuration() * 3)) {
+ // split the rest
+ command->addCommand(new RestInsertionCommand
+ (segment, time,
+ time + note.getDuration() * 2,
+ Note::getNearestNote(note.getDuration() * 2)));
+ //## }
+ //# These comments should probably be deleted.
+
+ command->addCommand(new TupletCommand
+ (segment, time, note.getDuration(),
+ 3, 2, true)); // #1046934: "has timing already"
+ command->addCommand(insertionCommand);
+ activeCommand = command;
+ }
+ }
+
+ m_nParentView->addCommandToHistory(activeCommand);
+
+ NOTATION_DEBUG << "NoteInserter::doAddCommand: accidental is "
+ << accidental << endl;
+
+ return insertionCommand->getLastInsertedEvent();
+}
+
+void NoteInserter::slotSetNote(Note::Type nt)
+{
+ m_noteType = nt;
+}
+
+void NoteInserter::slotSetDots(unsigned int dots)
+{
+ m_noteDots = dots;
+
+ KToggleAction *dotsAction = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("toggle_dot"));
+ if (dotsAction)
+ dotsAction->setChecked(dots > 0);
+}
+
+void NoteInserter::slotSetAccidental(Accidental accidental,
+ bool follow)
+{
+ NOTATION_DEBUG << "NoteInserter::setAccidental: accidental is "
+ << accidental << endl;
+ m_accidental = accidental;
+ m_followAccidental = follow;
+}
+
+void NoteInserter::slotNoAccidental()
+{
+ m_parentView->actionCollection()->action("no_accidental")->activate();
+}
+
+void NoteInserter::slotFollowAccidental()
+{
+ m_parentView->actionCollection()->action("follow_accidental")->activate();
+}
+
+void NoteInserter::slotSharp()
+{
+ m_parentView->actionCollection()->action("sharp_accidental")->activate();
+}
+
+void NoteInserter::slotFlat()
+{
+ m_parentView->actionCollection()->action("flat_accidental")->activate();
+}
+
+void NoteInserter::slotNatural()
+{
+ m_parentView->actionCollection()->action("natural_accidental")->activate();
+}
+
+void NoteInserter::slotDoubleSharp()
+{
+ m_parentView->actionCollection()->action("double_sharp_accidental")->activate();
+}
+
+void NoteInserter::slotDoubleFlat()
+{
+ m_parentView->actionCollection()->action("double_flat_accidental")->activate();
+}
+
+void NoteInserter::slotToggleDot()
+{
+ m_noteDots = (m_noteDots) ? 0 : 1;
+ Note note(m_noteType, m_noteDots);
+ QString actionName(NotationStrings::getReferenceName(note));
+ actionName.replace(QRegExp("-"), "_");
+ KAction *action = m_parentView->actionCollection()->action(actionName);
+ if (!action) {
+ std::cerr << "WARNING: No such action as " << actionName << std::endl;
+ } else {
+ action->activate();
+ }
+}
+
+void NoteInserter::slotToggleAutoBeam()
+{
+ m_autoBeam = !m_autoBeam;
+}
+
+void NoteInserter::slotEraseSelected()
+{
+ m_parentView->actionCollection()->action("erase")->activate();
+}
+
+void NoteInserter::slotSelectSelected()
+{
+ m_parentView->actionCollection()->action("select")->activate();
+}
+
+void NoteInserter::slotRestsSelected()
+{
+ Note note(m_noteType, m_noteDots);
+ QString actionName(NotationStrings::getReferenceName(note, true));
+ actionName.replace(QRegExp("-"), "_");
+ KAction *action = m_parentView->actionCollection()->action(actionName);
+ if (!action) {
+ std::cerr << "WARNING: No such action as " << actionName << std::endl;
+ } else {
+ action->activate();
+ }
+}
+
+const char* NoteInserter::m_actionsAccidental[][4] =
+{
+ { "No accidental", "1slotNoAccidental()", "no_accidental",
+ "accidental-none" },
+ { "Follow accidental", "1slotFollowAccidental()", "follow_accidental",
+ "accidental-follow" },
+ { "Sharp", "1slotSharp()", "sharp_accidental",
+ "accidental-sharp" },
+ { "Flat", "1slotFlat()", "flat_accidental",
+ "accidental-flat" },
+ { "Natural", "1slotNatural()", "natural_accidental",
+ "accidental-natural" },
+ { "Double sharp", "1slotDoubleSharp()", "double_sharp_accidental",
+ "accidental-doublesharp" },
+ { "Double flat", "1slotDoubleFlat()", "double_flat_accidental",
+ "accidental-doubleflat" }
+};
+
+const QString NoteInserter::ToolName = "noteinserter";
+
+}
+#include "NoteInserter.moc"
diff --git a/src/gui/editors/notation/NoteInserter.h b/src/gui/editors/notation/NoteInserter.h
new file mode 100644
index 0000000..cb46b38
--- /dev/null
+++ b/src/gui/editors/notation/NoteInserter.h
@@ -0,0 +1,166 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTEINSERTER_H_
+#define _RG_NOTEINSERTER_H_
+
+#include "base/NotationTypes.h"
+#include "NotationTool.h"
+#include "NotationElement.h"
+#include "NoteStyle.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class Segment;
+class NotationView;
+class Event;
+
+
+/**
+ * This tool will insert notes on mouse click events
+ */
+class NoteInserter : public NotationTool
+{
+ Q_OBJECT
+
+ friend class NotationToolBox;
+
+public:
+ ~NoteInserter();
+
+ virtual void handleLeftButtonPress(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement* el);
+
+ virtual int handleMouseMove(timeT time,
+ int height,
+ QMouseEvent*);
+
+ virtual void handleMouseRelease(timeT time,
+ int height,
+ QMouseEvent*);
+
+ virtual void ready();
+
+ Note getCurrentNote() {
+ return Note(m_noteType, m_noteDots);
+ }
+
+ /// Insert a note as if the user has clicked at the given time & pitch
+ void insertNote(Segment &segment,
+ timeT insertionTime,
+ int pitch,
+ Accidental accidental,
+ bool suppressPreview = false);
+
+ static const QString ToolName;
+
+public slots:
+ /// Set the type of note (quaver, breve...) which will be inserted
+ void slotSetNote(Note::Type);
+
+ /// Set the nb of dots the inserted note will have
+ void slotSetDots(unsigned int dots);
+
+ /// Set the accidental for the notes which will be inserted
+ void slotSetAccidental(Accidental, bool follow);
+
+protected:
+ NoteInserter(NotationView*);
+
+ /// this ctor is used by RestInserter
+ NoteInserter(const QString& menuName, NotationView*);
+
+ timeT getOffsetWithinRest(int staffNo,
+ const NotationElementList::iterator&,
+ double &canvasX);
+
+ int getOttavaShift(Segment &segment, timeT time);
+
+ virtual Event *doAddCommand(Segment &,
+ timeT time,
+ timeT endTime,
+ const Note &,
+ int pitch, Accidental);
+
+ virtual bool computeLocationAndPreview(QMouseEvent *e);
+ virtual void showPreview();
+ virtual void clearPreview();
+
+protected slots:
+ // RMB menu slots
+ void slotNoAccidental();
+ void slotFollowAccidental();
+ void slotSharp();
+ void slotFlat();
+ void slotNatural();
+ void slotDoubleSharp();
+ void slotDoubleFlat();
+ void slotToggleDot();
+ void slotToggleAutoBeam();
+
+ void slotEraseSelected();
+ void slotSelectSelected();
+ void slotRestsSelected();
+
+protected:
+ //--------------- Data members ---------------------------------
+
+ Note::Type m_noteType;
+ unsigned int m_noteDots;
+ bool m_autoBeam;
+ bool m_matrixInsertType;
+ NoteStyleName m_defaultStyle;
+
+ bool m_clickHappened;
+ timeT m_clickTime;
+ int m_clickSubordering;
+ int m_clickPitch;
+ int m_clickHeight;
+ int m_clickStaffNo;
+ double m_clickInsertX;
+ float m_targetSubordering;
+
+ Accidental m_accidental;
+ Accidental m_lastAccidental;
+ bool m_followAccidental;
+
+ static const char* m_actionsAccidental[][4];
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotePixmapFactory.cpp b/src/gui/editors/notation/NotePixmapFactory.cpp
new file mode 100644
index 0000000..c2a99ee
--- /dev/null
+++ b/src/gui/editors/notation/NotePixmapFactory.cpp
@@ -0,0 +1,3689 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include <cmath>
+#include "NotePixmapFactory.h"
+#include "misc/Debug.h"
+#include "base/NotationRules.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include <kconfig.h>
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/Exception.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "gui/editors/guitar/Fingering.h"
+#include "gui/editors/guitar/FingeringBox.h"
+#include "gui/editors/guitar/NoteSymbols.h"
+#include "gui/editors/notation/TrackHeader.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/PixmapFunctions.h"
+#include "gui/general/Spline.h"
+#include "gui/kdeext/KStartupLogo.h"
+#include "NotationStrings.h"
+#include "NotationView.h"
+#include "NoteCharacter.h"
+#include "NoteCharacterNames.h"
+#include "NoteFontFactory.h"
+#include "NoteFont.h"
+#include "NotePixmapParameters.h"
+#include "NotePixmapPainter.h"
+#include "NoteStyleFactory.h"
+#include "NoteStyle.h"
+#include <kglobal.h>
+#include <kmessagebox.h>
+#include <qbitmap.h>
+#include <qcolor.h>
+#include <qfile.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qpen.h>
+#include <qpixmap.h>
+#include <qpointarray.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <qwmatrix.h>
+
+
+namespace Rosegarden
+{
+
+using namespace Accidentals;
+
+static clock_t drawBeamsTime = 0;
+static clock_t makeNotesTime = 0;
+static int drawBeamsCount = 0;
+static int drawBeamsBeamCount = 0;
+
+class NotePixmapCache : public std::map<CharName, QCanvasPixmap*>
+{
+ // nothing to add -- just so we can predeclare it in the header
+};
+
+const char* const NotePixmapFactory::defaultSerifFontFamily = "Bitstream Vera Serif";
+const char* const NotePixmapFactory::defaultSansSerifFontFamily = "Bitstream Vera Sans";
+const char* const NotePixmapFactory::defaultTimeSigFontFamily = "Bitstream Vera Serif";
+
+NotePixmapFactory::NotePixmapFactory(std::string fontName, int size) :
+ m_selected(false),
+ m_shaded(false),
+ m_tupletCountFont(defaultSerifFontFamily, 8, QFont::Bold),
+ m_tupletCountFontMetrics(m_tupletCountFont),
+ m_textMarkFont(defaultSerifFontFamily, 8, QFont::Bold, true),
+ m_textMarkFontMetrics(m_textMarkFont),
+ m_fingeringFont(defaultSerifFontFamily, 8, QFont::Bold),
+ m_fingeringFontMetrics(m_fingeringFont),
+ m_timeSigFont(defaultTimeSigFontFamily, 8, QFont::Bold),
+ m_timeSigFontMetrics(m_timeSigFont),
+ m_bigTimeSigFont(defaultTimeSigFontFamily, 12, QFont::Normal),
+ m_bigTimeSigFontMetrics(m_bigTimeSigFont),
+ m_ottavaFont(defaultSerifFontFamily, 8, QFont::Normal, true),
+ m_ottavaFontMetrics(m_ottavaFont),
+ m_clefOttavaFont(defaultSerifFontFamily, 8, QFont::Normal),
+ m_clefOttavaFontMetrics(m_ottavaFont),
+ m_trackHeaderFont(defaultSansSerifFontFamily, 10, QFont::Normal),
+ m_trackHeaderFontMetrics(m_trackHeaderFont),
+ m_trackHeaderBoldFont(defaultSansSerifFontFamily, 10, QFont::Bold),
+ m_trackHeaderBoldFontMetrics(m_trackHeaderBoldFont),
+ m_generatedPixmap(0),
+ m_generatedMask(0),
+ m_generatedWidth( -1),
+ m_generatedHeight( -1),
+ m_inPrinterMethod(false),
+ m_p(new NotePixmapPainter()),
+ m_dottedRestCache(new NotePixmapCache)
+{
+ init(fontName, size);
+}
+
+NotePixmapFactory::NotePixmapFactory(const NotePixmapFactory &npf) :
+ m_selected(false),
+ m_shaded(false),
+ m_tupletCountFont(npf.m_tupletCountFont),
+ m_tupletCountFontMetrics(m_tupletCountFont),
+ m_textMarkFont(npf.m_textMarkFont),
+ m_textMarkFontMetrics(m_textMarkFont),
+ m_fingeringFont(npf.m_fingeringFont),
+ m_fingeringFontMetrics(m_fingeringFont),
+ m_timeSigFont(npf.m_timeSigFont),
+ m_timeSigFontMetrics(m_timeSigFont),
+ m_bigTimeSigFont(npf.m_bigTimeSigFont),
+ m_bigTimeSigFontMetrics(m_bigTimeSigFont),
+ m_ottavaFont(defaultSerifFontFamily, 8, QFont::Normal, true),
+ m_ottavaFontMetrics(m_ottavaFont),
+ m_clefOttavaFont(defaultSerifFontFamily, 8, QFont::Normal),
+ m_clefOttavaFontMetrics(m_ottavaFont),
+ m_trackHeaderFont(defaultSansSerifFontFamily, 10, QFont::Normal),
+ m_trackHeaderFontMetrics(m_trackHeaderFont),
+ m_trackHeaderBoldFont(defaultSansSerifFontFamily, 10, QFont::Bold),
+ m_trackHeaderBoldFontMetrics(m_trackHeaderBoldFont),
+ m_generatedPixmap(0),
+ m_generatedMask(0),
+ m_generatedWidth( -1),
+ m_generatedHeight( -1),
+ m_inPrinterMethod(false),
+ m_p(new NotePixmapPainter()),
+ m_dottedRestCache(new NotePixmapCache)
+{
+ init(npf.m_font->getName(), npf.m_font->getSize());
+}
+
+NotePixmapFactory &
+NotePixmapFactory::operator=(const NotePixmapFactory &npf)
+{
+ if (&npf != this) {
+ m_selected = npf.m_selected;
+ m_shaded = npf.m_shaded;
+ m_timeSigFont = npf.m_timeSigFont;
+ m_timeSigFontMetrics = QFontMetrics(m_timeSigFont);
+ m_bigTimeSigFont = npf.m_bigTimeSigFont;
+ m_bigTimeSigFontMetrics = QFontMetrics(m_bigTimeSigFont);
+ m_tupletCountFont = npf.m_tupletCountFont;
+ m_tupletCountFontMetrics = QFontMetrics(m_tupletCountFont);
+ m_textMarkFont = npf.m_textMarkFont;
+ m_textMarkFontMetrics = QFontMetrics(m_textMarkFont);
+ m_fingeringFont = npf.m_fingeringFont;
+ m_fingeringFontMetrics = QFontMetrics(m_fingeringFont);
+ m_ottavaFont = npf.m_ottavaFont;
+ m_ottavaFontMetrics = QFontMetrics(m_ottavaFont);
+ m_clefOttavaFont = npf.m_clefOttavaFont;
+ m_clefOttavaFontMetrics = QFontMetrics(m_clefOttavaFont);
+ m_trackHeaderFont = npf.m_trackHeaderFont;
+ m_trackHeaderFontMetrics = QFontMetrics(m_trackHeaderFont);
+ m_trackHeaderBoldFont = npf.m_trackHeaderBoldFont;
+ m_trackHeaderBoldFontMetrics = QFontMetrics(m_trackHeaderBoldFont);
+ init(npf.m_font->getName(), npf.m_font->getSize());
+ m_dottedRestCache->clear();
+ m_textFontCache.clear();
+ }
+ return *this;
+}
+
+void
+NotePixmapFactory::init(std::string fontName, int size)
+{
+ try {
+ m_style = NoteStyleFactory::getStyle(NoteStyleFactory::DefaultStyle);
+ } catch (NoteStyleFactory::StyleUnavailable u) {
+ KStartupLogo::hideIfStillThere();
+ KMessageBox::error(0, i18n(strtoqstr(u.getMessage())));
+ throw;
+ }
+
+ int origSize = size;
+
+ if (fontName != "") {
+ try {
+ if (size < 0)
+ size = NoteFontFactory::getDefaultSize(fontName);
+ m_font = NoteFontFactory::getFont(fontName, size);
+ } catch (Exception f) {
+ fontName = "";
+ // fall through
+ }
+ }
+
+ if (fontName == "") { // either because it was passed in or because read failed
+ try {
+ fontName = NoteFontFactory::getDefaultFontName();
+ size = origSize;
+ if (size < 0)
+ size = NoteFontFactory::getDefaultSize(fontName);
+ m_font = NoteFontFactory::getFont(fontName, size);
+ } catch (Exception f) { // already reported
+ throw;
+ }
+ }
+
+ // Resize the fonts, because the original constructor used point
+ // sizes only and we want pixels
+ QFont timeSigFont(defaultTimeSigFontFamily),
+ textFont(defaultSerifFontFamily);
+ KConfig* config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+
+ m_timeSigFont = config->readFontEntry("timesigfont", &timeSigFont);
+ m_timeSigFont.setBold(true);
+ m_timeSigFont.setPixelSize(size * 5 / 2);
+ m_timeSigFontMetrics = QFontMetrics(m_timeSigFont);
+
+ m_bigTimeSigFont = config->readFontEntry("timesigfont", &timeSigFont);
+ m_bigTimeSigFont.setPixelSize(size * 4 + 2);
+ m_bigTimeSigFontMetrics = QFontMetrics(m_bigTimeSigFont);
+
+ m_tupletCountFont = config->readFontEntry("textfont", &textFont);
+ m_tupletCountFont.setBold(true);
+ m_tupletCountFont.setPixelSize(size * 2);
+ m_tupletCountFontMetrics = QFontMetrics(m_tupletCountFont);
+
+ m_textMarkFont = config->readFontEntry("textfont", &textFont);
+ m_textMarkFont.setBold(true);
+ m_textMarkFont.setItalic(true);
+ m_textMarkFont.setPixelSize(size * 2);
+ m_textMarkFontMetrics = QFontMetrics(m_textMarkFont);
+
+ m_fingeringFont = config->readFontEntry("textfont", &textFont);
+ m_fingeringFont.setBold(true);
+ m_fingeringFont.setPixelSize(size * 5 / 3);
+ m_fingeringFontMetrics = QFontMetrics(m_fingeringFont);
+
+ m_ottavaFont = config->readFontEntry("textfont", &textFont);
+ m_ottavaFont.setPixelSize(size * 2);
+ m_ottavaFontMetrics = QFontMetrics(m_ottavaFont);
+
+ m_clefOttavaFont = config->readFontEntry("textfont", &textFont);
+ m_clefOttavaFont.setPixelSize(getLineSpacing() * 3 / 2);
+ m_clefOttavaFontMetrics = QFontMetrics(m_clefOttavaFont);
+
+ m_trackHeaderFont = config->readFontEntry("sansfont", &m_trackHeaderFont);
+ m_trackHeaderFont.setPixelSize(12);
+ m_trackHeaderFontMetrics = QFontMetrics(m_trackHeaderFont);
+
+ m_trackHeaderBoldFont = m_trackHeaderFont;
+ m_trackHeaderBoldFont.setBold(true);
+ m_trackHeaderBoldFontMetrics = QFontMetrics(m_trackHeaderBoldFont);
+}
+
+NotePixmapFactory::~NotePixmapFactory()
+{
+ delete m_p;
+ delete m_dottedRestCache;
+}
+
+std::string
+NotePixmapFactory::getFontName() const
+{
+ return m_font->getName();
+}
+
+int
+NotePixmapFactory::getSize() const
+{
+ return m_font->getSize();
+}
+
+QPixmap
+NotePixmapFactory::toQPixmap(QCanvasPixmap* cp)
+{
+ QPixmap p = *cp;
+ delete cp;
+ return p;
+}
+
+void
+NotePixmapFactory::dumpStats(std::ostream &s)
+{
+#ifdef DUMP_STATS
+ s << "NotePixmapFactory: total times since last stats dump:\n"
+ << "makeNotePixmap: "
+ << (makeNotesTime * 1000 / CLOCKS_PER_SEC) << "ms\n"
+ << "drawBeams: "
+ << (drawBeamsTime * 1000 / CLOCKS_PER_SEC) << "ms"
+ << " (drew " << drawBeamsCount << " individual points in " << drawBeamsBeamCount << " beams)"
+ << endl;
+ makeNotesTime = 0;
+ drawBeamsTime = 0;
+ drawBeamsCount = 0;
+ drawBeamsBeamCount = 0;
+#endif
+
+ (void)s; // avoid warnings
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeNotePixmap(const NotePixmapParameters &params)
+{
+ Profiler profiler("NotePixmapFactory::makeNotePixmap");
+ clock_t startTime = clock();
+
+ drawNoteAux(params, 0, 0, 0);
+
+ QPoint hotspot(m_left, m_above + m_noteBodyHeight / 2);
+
+ //#define ROSE_DEBUG_NOTE_PIXMAP_FACTORY
+#ifdef ROSE_DEBUG_NOTE_PIXMAP_FACTORY
+
+ m_p->painter().setPen(Qt::red);
+ m_p->painter().setBrush(Qt::red);
+
+ m_p->drawLine(0, 0, 0, m_generatedHeight - 1);
+ m_p->drawLine(m_generatedWidth - 1, 0,
+ m_generatedWidth - 1,
+ m_generatedHeight - 1);
+
+ {
+ int hsx = hotspot.x();
+ int hsy = hotspot.y();
+ m_p->drawLine(hsx - 2, hsy - 2, hsx + 2, hsy + 2);
+ m_p->drawLine(hsx - 2, hsy + 2, hsx + 2, hsy - 2);
+ }
+#endif
+
+ clock_t endTime = clock();
+ makeNotesTime += (endTime - startTime);
+
+ return makeCanvasPixmap(hotspot);
+}
+
+void
+NotePixmapFactory::drawNote(const NotePixmapParameters &params,
+ QPainter &painter, int x, int y)
+{
+ Profiler profiler("NotePixmapFactory::drawNote");
+ m_inPrinterMethod = true;
+ drawNoteAux(params, &painter, x, y);
+ m_inPrinterMethod = false;
+}
+
+void
+NotePixmapFactory::drawNoteAux(const NotePixmapParameters &params,
+ QPainter *painter, int x, int y)
+{
+ NoteFont::CharacterType charType = m_inPrinterMethod ? NoteFont::Printer : NoteFont::Screen;
+
+ bool drawFlag = params.m_drawFlag;
+
+ if (params.m_beamed)
+ drawFlag = false;
+
+ // A note pixmap is formed of note head, stem, flags,
+ // accidentals, dots and beams. Assume the note head first, then
+ // do the rest of the calculations left to right, ie accidentals,
+ // stem, flags, dots, beams
+
+ m_noteBodyWidth = getNoteBodyWidth(params.m_noteType);
+ m_noteBodyHeight = getNoteBodyHeight(params.m_noteType);
+
+ // Spacing surrounding the note head. For top and bottom, we
+ // adjust this according to the discrepancy between the nominal
+ // and actual heights of the note head pixmap. For left and
+ // right, we use the hotspot x coordinate of the head.
+ int temp;
+ if (!m_font->getHotspot(m_style->getNoteHeadCharName(params.m_noteType).first,
+ m_borderX, temp))
+ m_borderX = 0;
+
+ if (params.m_noteType == Note::Minim && params.m_stemGoesUp)
+ m_borderX++;
+ int actualNoteBodyHeight =
+ m_font->getHeight(m_style->getNoteHeadCharName(params.m_noteType).first);
+
+ m_left = m_right = m_borderX;
+ m_above = m_borderY = (actualNoteBodyHeight - m_noteBodyHeight) / 2;
+ m_below = (actualNoteBodyHeight - m_noteBodyHeight) - m_above;
+
+ // NOTATION_DEBUG << "actualNoteBodyHeight: " << actualNoteBodyHeight
+ // << ", noteBodyHeight: " << m_noteBodyHeight << ", borderX: "
+ // << m_borderX << ", borderY: "
+ // << m_borderY << endl;
+
+ bool isStemmed = m_style->hasStem(params.m_noteType);
+ int flagCount = m_style->getFlagCount(params.m_noteType);
+ int slashCount = params.m_slashes;
+ if (!slashCount)
+ slashCount = m_style->getSlashCount(params.m_noteType);
+
+ if (params.m_accidental != NoAccidental) {
+ makeRoomForAccidental(params.m_accidental,
+ params.m_cautionary,
+ params.m_accidentalShift,
+ params.m_accidentalExtra);
+ }
+
+ NoteCharacter dot(getCharacter(NoteCharacterNames::DOT, PlainColour, charType));
+ int dotWidth = dot.getWidth();
+ if (dotWidth < getNoteBodyWidth() / 2)
+ dotWidth = getNoteBodyWidth() / 2;
+
+ int stemLength = getStemLength(params);
+
+ if (params.m_marks.size() > 0) {
+ makeRoomForMarks(isStemmed, params, stemLength);
+ }
+
+ if (params.m_legerLines != 0) {
+ makeRoomForLegerLines(params);
+ }
+
+ if (slashCount > 0) {
+ m_left = std::max(m_left, m_noteBodyWidth / 2);
+ m_right = std::max(m_right, m_noteBodyWidth / 2);
+ }
+
+ if (params.m_tupletCount > 0) {
+ makeRoomForTuplingLine(params);
+ }
+
+ m_right = std::max(m_right, params.m_dots * dotWidth + dotWidth / 2);
+ if (params.m_dotShifted) {
+ m_right += m_noteBodyWidth;
+ }
+ if (params.m_onLine) {
+ m_above = std::max(m_above, dot.getHeight() / 2);
+ }
+
+ if (params.m_shifted) {
+ if (params.m_stemGoesUp) {
+ m_right += m_noteBodyWidth;
+ } else {
+ m_left = std::max(m_left, m_noteBodyWidth);
+ }
+ }
+
+ bool tieAbove = params.m_tieAbove;
+ if (!params.m_tiePositionExplicit) {
+ tieAbove = !params.m_stemGoesUp;
+ }
+
+ if (params.m_tied) {
+ m_right = std::max(m_right, params.m_tieLength);
+ if (!tieAbove) {
+ m_below = std::max(m_below, m_noteBodyHeight * 2);
+ } else {
+ m_above = std::max(m_above, m_noteBodyHeight * 2);
+ }
+ }
+
+ QPoint startPoint, endPoint;
+ if (isStemmed && params.m_drawStem) {
+ makeRoomForStemAndFlags(drawFlag ? flagCount : 0, stemLength, params,
+ startPoint, endPoint);
+ }
+
+ if (isStemmed && params.m_drawStem && params.m_beamed) {
+ makeRoomForBeams(params);
+ }
+
+ // for all other calculations we use the nominal note-body height
+ // (same as the gap between staff lines), but here we want to know
+ // if the pixmap itself is taller than that
+ /*!!!
+ int actualNoteBodyHeight = m_font->getHeight
+ (m_style->getNoteHeadCharName(params.m_noteType).first);
+ // - 2*m_origin.y();
+ if (actualNoteBodyHeight > m_noteBodyHeight) {
+ m_below = std::max(m_below, actualNoteBodyHeight - m_noteBodyHeight);
+ }
+ */
+ if (painter) {
+ painter->save();
+ m_p->beginExternal(painter);
+ // NOTATION_DEBUG << "Translate: (" << x << "," << y << ")" << endl;
+ painter->translate(x - m_left, y - m_above - m_noteBodyHeight / 2);
+ } else {
+ createPixmapAndMask(m_noteBodyWidth + m_left + m_right,
+ m_noteBodyHeight + m_above + m_below);
+ }
+
+ if (params.m_tupletCount > 0) {
+ drawTuplingLine(params);
+ }
+
+ if (isStemmed && params.m_drawStem && drawFlag) {
+ drawFlags(flagCount, params, startPoint, endPoint);
+ }
+
+ if (params.m_accidental != NoAccidental) {
+ drawAccidental(params.m_accidental, params.m_cautionary);
+ }
+
+ NoteStyle::CharNameRec charNameRec
+ (m_style->getNoteHeadCharName(params.m_noteType));
+ CharName charName = charNameRec.first;
+ bool inverted = charNameRec.second;
+ NoteCharacter body = getCharacter
+ (charName,
+ params.m_highlighted ? HighlightedColour :
+ params.m_quantized ? QuantizedColour :
+ params.m_trigger ? TriggerColour :
+ params.m_inRange ? PlainColour : OutRangeColour,
+ inverted);
+
+ QPoint bodyLocation(m_left - m_borderX,
+ m_above - m_borderY + getStaffLineThickness() / 2);
+ if (params.m_shifted) {
+ if (params.m_stemGoesUp) {
+ bodyLocation.rx() += m_noteBodyWidth;
+ } else {
+ bodyLocation.rx() -= m_noteBodyWidth - 1;
+ }
+ }
+
+ m_p->drawNoteCharacter(bodyLocation.x(), bodyLocation.y(), body);
+
+ if (params.m_dots > 0) {
+
+ int x = m_left + m_noteBodyWidth + dotWidth / 2;
+ int y = m_above + m_noteBodyHeight / 2 - dot.getHeight() / 2;
+
+ if (params.m_onLine)
+ y -= m_noteBodyHeight / 2;
+
+ if (params.m_shifted)
+ x += m_noteBodyWidth;
+ else if (params.m_dotShifted)
+ x += m_noteBodyWidth;
+
+ for (int i = 0; i < params.m_dots; ++i) {
+ m_p->drawNoteCharacter(x, y, dot);
+ x += dotWidth;
+ }
+ }
+
+ if (isStemmed && params.m_drawStem) {
+
+ if (flagCount > 0 && !drawFlag && params.m_beamed) {
+ drawBeams(endPoint, params, flagCount);
+ }
+
+ if (slashCount > 0) {
+ drawSlashes(startPoint, params, slashCount);
+ }
+
+ if (m_selected)
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ else
+ m_p->painter().setPen(Qt::black);
+
+ // If we draw stems after beams, instead of beams after stems,
+ // beam anti-aliasing won't damage stems but we have to shorten the
+ // stems slightly first so that the stems don't extend all the way
+ // through the beam into the anti-aliased region on the
+ // other side of the beam that faces away from the note-heads.
+ int shortening;
+ if (flagCount > 0 && !drawFlag && params.m_beamed)
+ shortening = 2;
+ else
+ shortening = 0;
+ drawStem(params, startPoint, endPoint, shortening);
+ }
+
+ if (params.m_marks.size() > 0) {
+ drawMarks(isStemmed, params, stemLength);
+ }
+
+ if (params.m_legerLines != 0) {
+ drawLegerLines(params);
+ }
+
+ if (params.m_tied) {
+ drawTie(tieAbove, params.m_tieLength, dotWidth * params.m_dots);
+ }
+
+ if (painter) {
+ painter->restore();
+ }
+}
+
+
+QCanvasPixmap*
+NotePixmapFactory::makeNoteHaloPixmap(const NotePixmapParameters &params)
+{
+ int nbh0 = getNoteBodyHeight();
+ int nbh = getNoteBodyHeight(params.m_noteType);
+ int nbw0 = getNoteBodyHeight();
+ int nbw = getNoteBodyWidth(params.m_noteType);
+
+ createPixmapAndMask(nbw + nbw0, nbh + nbh0);
+ drawNoteHalo(0, 0, nbw + nbw0, nbh + nbh0);
+
+ return makeCanvasPixmap(QPoint(nbw0 / 2, nbh0));
+}
+
+
+void
+NotePixmapFactory::drawNoteHalo(int x, int y, int w, int h) {
+
+ m_p->painter().setPen(QPen(QColor(GUIPalette::CollisionHaloHue,
+ GUIPalette::CollisionHaloSaturation,
+ 255, QColor::Hsv), 1));
+ m_p->painter().setBrush(QColor(GUIPalette::CollisionHaloHue,
+ GUIPalette::CollisionHaloSaturation,
+ 255, QColor::Hsv));
+ m_p->drawEllipse(x, y, w, h);
+}
+
+
+
+int
+NotePixmapFactory::getStemLength(const NotePixmapParameters &params) const
+{
+ if (params.m_beamed && params.m_stemLength >= 0) {
+ return params.m_stemLength;
+ }
+
+ int stemLength = getStemLength();
+
+ int flagCount = m_style->getFlagCount(params.m_noteType);
+ int slashCount = params.m_slashes;
+ bool stemUp = params.m_stemGoesUp;
+ int nbh = m_noteBodyHeight;
+
+ if (flagCount > 2) {
+ stemLength += getLineSpacing() * (flagCount - 2);
+ }
+
+ int width = 0, height = 0;
+
+ if (flagCount > 0) {
+
+ if (!stemUp)
+ stemLength += nbh / 2;
+
+ if (m_font->getDimensions(m_style->getFlagCharName(flagCount),
+ width, height)) {
+
+ stemLength = std::max(stemLength, height);
+
+ } else if (m_font->getDimensions(m_style->getPartialFlagCharName(true),
+ width, height) ||
+ m_font->getDimensions(m_style->getPartialFlagCharName(false),
+ width, height)) {
+
+ unsigned int flagSpace = m_noteBodyHeight;
+ (void)m_font->getFlagSpacing(flagSpace);
+
+ stemLength = std::max(stemLength,
+ height + (flagCount - 1) * (int)flagSpace);
+ }
+ }
+
+ if (slashCount > 3 && flagCount < 3) {
+ stemLength += (slashCount - 3) * (nbh / 2);
+ }
+
+ if (params.m_stemLength >= 0) {
+ if (flagCount == 0)
+ return params.m_stemLength;
+ stemLength = std::max(stemLength, params.m_stemLength);
+ }
+
+ return stemLength;
+}
+
+void
+NotePixmapFactory::makeRoomForAccidental(Accidental a,
+ bool cautionary, int shift, bool extra)
+{
+ // General observation: where we're only using a character to
+ // determine its dimensions, we should (for the moment) just
+ // request it in screen mode, because it may be quicker and we
+ // don't need to render it, and the dimensions are the same.
+ NoteCharacter ac
+ (m_font->getCharacter(m_style->getAccidentalCharName(a)));
+
+ QPoint ah(m_font->getHotspot(m_style->getAccidentalCharName(a)));
+
+ m_left += ac.getWidth() + (m_noteBodyWidth / 4 - m_borderX);
+
+ if (shift > 0) {
+ if (extra) {
+ // The extra flag indicates that the first shift is to get
+ // out of the way of a note head, thus has to move
+ // possibly further, or at least a different amount. So
+ // replace the first shift with a different one.
+ --shift;
+ m_left += m_noteBodyWidth - m_noteBodyWidth / 5;
+ }
+ if (shift > 0) {
+ // The amount we shift for each accidental is the greater
+ // of the probable shift for that accidental and the
+ // probable shift for a sharp, on the assumption (usually
+ // true in classical notation) that the sharp is the
+ // widest accidental and that we may have other
+ // accidentals possibly including sharps on other notes in
+ // this chord that we can't know about here.
+ int step = ac.getWidth() - ah.x();
+ if (a != Accidentals::Sharp) {
+ NoteCharacter acSharp
+ (m_font->getCharacter(m_style->getAccidentalCharName
+ (Accidentals::Sharp)));
+ QPoint ahSharp
+ (m_font->getHotspot(m_style->getAccidentalCharName
+ (Accidentals::Sharp)));
+ step = std::max(step, acSharp.getWidth() - ahSharp.x());
+ }
+ m_left += shift * step;
+ }
+ }
+
+ if (cautionary)
+ m_left += m_noteBodyWidth;
+
+ int above = ah.y() - m_noteBodyHeight / 2;
+ int below = (ac.getHeight() - ah.y()) -
+ (m_noteBodyHeight - m_noteBodyHeight / 2); // subtract in case it's odd
+
+ if (above > 0)
+ m_above = std::max(m_above, above);
+ if (below > 0)
+ m_below = std::max(m_below, below);
+}
+
+void
+NotePixmapFactory::drawAccidental(Accidental a, bool cautionary)
+{
+ NoteCharacter ac = getCharacter
+ (m_style->getAccidentalCharName(a), PlainColour, false);
+
+ QPoint ah(m_font->getHotspot(m_style->getAccidentalCharName(a)));
+
+ int ax = 0;
+
+ if (cautionary) {
+ ax += m_noteBodyWidth / 2;
+ int bl = ac.getHeight() * 2 / 3;
+ int by = m_above + m_noteBodyHeight / 2 - bl / 2;
+ drawBracket(bl, true, false, m_noteBodyWidth*3 / 8, by);
+ drawBracket(bl, false, false, ac.getWidth() + m_noteBodyWidth*5 / 8, by);
+ }
+
+ m_p->drawNoteCharacter(ax, m_above + m_noteBodyHeight / 2 - ah.y(), ac);
+}
+
+void
+NotePixmapFactory::makeRoomForMarks(bool isStemmed,
+ const NotePixmapParameters &params,
+ int stemLength)
+{
+ int height = 0, width = 0;
+ int gap = m_noteBodyHeight / 5 + 1;
+
+ std::vector<Mark> normalMarks = params.getNormalMarks();
+ std::vector<Mark> aboveMarks = params.getAboveMarks();
+
+ for (std::vector<Mark>::iterator i = normalMarks.begin();
+ i != normalMarks.end(); ++i) {
+
+ if (!Marks::isTextMark(*i)) {
+
+ NoteCharacter character(m_font->getCharacter(m_style->getMarkCharName(*i)));
+ height += character.getHeight() + gap;
+ if (character.getWidth() > width)
+ width = character.getWidth();
+
+ } else {
+ // Inefficient to do this here _and_ in drawMarks, but
+ // text marks are not all that common
+ QString text = strtoqstr(Marks::getTextFromMark(*i));
+ QRect bounds = m_textMarkFontMetrics.boundingRect(text);
+ height += bounds.height() + gap;
+ if (bounds.width() > width)
+ width = bounds.width();
+ }
+ }
+
+ if (height > 0) {
+ if (isStemmed && params.m_stemGoesUp) {
+ m_below += height + 1;
+ } else {
+ m_above += height + 1;
+ }
+ }
+
+ height = 0;
+
+ if (params.m_safeVertDistance > 0 && !aboveMarks.empty()) {
+ m_above = std::max(m_above, params.m_safeVertDistance);
+ }
+
+ for (std::vector<Mark>::iterator i = aboveMarks.begin();
+ i != aboveMarks.end(); ++i) {
+
+ if (!Marks::isFingeringMark(*i)) {
+
+ Mark m(*i);
+
+ if (m == Marks::TrillLine)
+ m = Marks::LongTrill;
+
+ if (m == Marks::LongTrill) {
+ m_right = std::max(m_right, params.m_width);
+ }
+
+ NoteCharacter character(m_font->getCharacter(m_style->getMarkCharName(m)));
+ height += character.getHeight() + gap;
+ if (character.getWidth() > width)
+ width = character.getWidth();
+
+ } else {
+
+ // Inefficient to do this here _and_ in drawMarks
+ QString text = strtoqstr(Marks::getFingeringFromMark(*i));
+ QRect bounds = m_fingeringFontMetrics.boundingRect(text);
+ height += bounds.height() + gap + 3;
+ if (bounds.width() > width)
+ width = bounds.width();
+ }
+ }
+
+ if (height > 0) {
+ if (isStemmed && params.m_stemGoesUp && params.m_safeVertDistance == 0) {
+ m_above += stemLength + height + 1;
+ } else {
+ m_above += height + 1;
+ }
+ }
+
+ m_left = std::max(m_left, width / 2 - m_noteBodyWidth / 2);
+ m_right = std::max(m_right, width / 2 - m_noteBodyWidth / 2);
+}
+
+void
+NotePixmapFactory::drawMarks(bool isStemmed,
+ const NotePixmapParameters &params,
+ int stemLength)
+{
+ int gap = m_noteBodyHeight / 5 + 1;
+ int dy = gap;
+
+ std::vector<Mark> normalMarks = params.getNormalMarks();
+ std::vector<Mark> aboveMarks = params.getAboveMarks();
+
+ bool normalMarksAreAbove = !(isStemmed && params.m_stemGoesUp);
+
+ for (std::vector<Mark>::iterator i = normalMarks.begin();
+ i != normalMarks.end(); ++i) {
+
+ if (!Marks::isTextMark(*i)) {
+
+ NoteCharacter character = getCharacter
+ (m_style->getMarkCharName(*i), PlainColour,
+ !normalMarksAreAbove);
+
+ int x = m_left + m_noteBodyWidth / 2 - character.getWidth() / 2;
+ int y = (normalMarksAreAbove ?
+ (m_above - dy - character.getHeight() - 1) :
+ (m_above + m_noteBodyHeight + m_borderY * 2 + dy));
+
+ m_p->drawNoteCharacter(x, y, character);
+ dy += character.getHeight() + gap;
+
+ } else {
+
+ QString text = strtoqstr(Marks::getTextFromMark(*i));
+ QRect bounds = m_textMarkFontMetrics.boundingRect(text);
+
+ m_p->painter().setFont(m_textMarkFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(m_textMarkFont);
+
+ int x = m_left + m_noteBodyWidth / 2 - bounds.width() / 2;
+ int y = (normalMarksAreAbove ?
+ (m_above - dy - 3) :
+ (m_above + m_noteBodyHeight + m_borderY * 2 + dy + bounds.height() + 1));
+
+ m_p->drawText(x, y, text);
+ dy += bounds.height() + gap;
+ }
+ }
+
+ if (!normalMarksAreAbove)
+ dy = gap;
+ if (params.m_safeVertDistance > 0) {
+ if (normalMarksAreAbove) {
+ dy = std::max(dy, params.m_safeVertDistance);
+ } else {
+ dy = params.m_safeVertDistance;
+ }
+ } else if (isStemmed && params.m_stemGoesUp) {
+ dy += stemLength;
+ }
+
+ for (std::vector<Mark>::iterator i = aboveMarks.begin();
+ i != aboveMarks.end(); ++i) {
+
+ if (m_selected)
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ else
+ m_p->painter().setPen(Qt::black);
+ if (!Marks::isFingeringMark(*i)) {
+
+ int x = m_left + m_noteBodyWidth / 2;
+ int y = m_above - dy - 1;
+
+ if (*i != Marks::TrillLine) {
+
+ NoteCharacter character
+ (getCharacter
+ (m_style->getMarkCharName(*i), PlainColour,
+ false));
+
+ x -= character.getWidth() / 2;
+ y -= character.getHeight();
+
+ m_p->drawNoteCharacter(x, y, character);
+
+ y += character.getHeight() / 2;
+ x += character.getWidth();
+
+ dy += character.getHeight() + gap;
+
+ } else {
+
+ NoteCharacter character
+ (getCharacter
+ (m_style->getMarkCharName(Marks::Trill), PlainColour,
+ false));
+ y -= character.getHeight() / 2;
+ dy += character.getHeight() + gap;
+ }
+
+ if (*i == Marks::LongTrill ||
+ *i == Marks::TrillLine) {
+ NoteCharacter extension;
+ if (getCharacter(NoteCharacterNames::TRILL_LINE, extension,
+ PlainColour, false)) {
+ x += extension.getHotspot().x();
+ while (x < m_left + params.m_width - extension.getWidth()) {
+ x -= extension.getHotspot().x();
+ m_p->drawNoteCharacter(x, y, extension);
+ x += extension.getWidth();
+ }
+ }
+ if (*i == Marks::TrillLine)
+ dy += extension.getHeight() + gap;
+ }
+
+ } else {
+ QString text = strtoqstr(Marks::getFingeringFromMark(*i));
+ QRect bounds = m_fingeringFontMetrics.boundingRect(text);
+
+ m_p->painter().setFont(m_fingeringFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(m_fingeringFont);
+
+ int x = m_left + m_noteBodyWidth / 2 - bounds.width() / 2;
+ int y = m_above - dy - 3;
+
+ m_p->drawText(x, y, text);
+ dy += bounds.height() + gap;
+ }
+ }
+}
+
+void
+NotePixmapFactory::makeRoomForLegerLines(const NotePixmapParameters &params)
+{
+ if (params.m_legerLines < 0 || params.m_restOutsideStave) {
+ m_above = std::max(m_above,
+ (m_noteBodyHeight + 1) *
+ ( -params.m_legerLines / 2));
+ }
+ if (params.m_legerLines > 0 || params.m_restOutsideStave) {
+ m_below = std::max(m_below,
+ (m_noteBodyHeight + 1) *
+ (params.m_legerLines / 2));
+ }
+ if (params.m_legerLines != 0) {
+ m_left = std::max(m_left, m_noteBodyWidth / 5 + 1);
+ m_right = std::max(m_right, m_noteBodyWidth / 5 + 1);
+ }
+ if (params.m_restOutsideStave) {
+ m_above += 1;
+ m_left = std::max(m_left, m_noteBodyWidth * 3 + 1);
+ m_right = std::max(m_right, m_noteBodyWidth * 3 + 1);
+ }
+}
+
+void
+NotePixmapFactory::drawLegerLines(const NotePixmapParameters &params)
+{
+ int x0, x1, y;
+
+ if (params.m_legerLines == 0)
+ return ;
+
+ if (params.m_restOutsideStave) {
+ if (m_selected)
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ else
+ m_p->painter().setPen(Qt::black);
+ }
+ x0 = m_left - m_noteBodyWidth / 5 - 1;
+ x1 = m_left + m_noteBodyWidth + m_noteBodyWidth / 5 /* + 1 */;
+
+ if (params.m_shifted) {
+ if (params.m_stemGoesUp) {
+ x0 += m_noteBodyWidth;
+ x1 += m_noteBodyWidth;
+ } else {
+ x0 -= m_noteBodyWidth;
+ x1 -= m_noteBodyWidth;
+ }
+ }
+
+ int offset = m_noteBodyHeight + getStaffLineThickness();
+ int legerLines = params.m_legerLines;
+ bool below = (legerLines < 0);
+
+ if (below) {
+ legerLines = -legerLines;
+ offset = -offset;
+ }
+
+ if (params.m_restOutsideStave)
+ y = m_above;
+ else {
+ if (!below) { // note above staff
+ if (legerLines % 2) { // note is between lines
+ y = m_above + m_noteBodyHeight;
+ } else { // note is on a line
+ y = m_above + m_noteBodyHeight / 2 - getStaffLineThickness() / 2;
+ }
+ } else { // note below staff
+ if (legerLines % 2) { // note is between lines
+ y = m_above - getStaffLineThickness();
+ } else { // note is on a line
+ y = m_above + m_noteBodyHeight / 2;
+ }
+ }
+ }
+ if (params.m_restOutsideStave) {
+ NOTATION_DEBUG << "draw leger lines: " << legerLines << " lines, below "
+ << below
+ << ", note body height " << m_noteBodyHeight
+ << ", thickness " << getLegerLineThickness()
+ << " (staff line " << getStaffLineThickness() << ")"
+ << ", offset " << offset << endl;
+ }
+
+ // NOTATION_DEBUG << "draw leger lines: " << legerLines << " lines, below "
+ // << below
+ // << ", note body height " << m_noteBodyHeight
+ // << ", thickness " << getLegerLineThickness()
+ // << " (staff line " << getStaffLineThickness() << ")"
+ // << ", offset " << offset << endl;
+
+ // bool first = true;
+
+ if (getLegerLineThickness() > getStaffLineThickness()) {
+ y -= (getLegerLineThickness() - getStaffLineThickness() + 1) / 2;
+ }
+
+ for (int i = legerLines - 1; i >= 0; --i) {
+ if (i % 2) {
+ // NOTATION_DEBUG << "drawing leger line at y = " << y << endl;
+ for (int j = 0; j < getLegerLineThickness(); ++j) {
+ m_p->drawLine(x0, y + j, x1, y + j);
+ }
+ y += offset;
+ // if (first) {
+ // x0 += getStemThickness();
+ // x1 -= getStemThickness();
+ // first = false;
+ // }
+ }
+ }
+}
+
+void
+NotePixmapFactory::makeRoomForStemAndFlags(int flagCount, int stemLength,
+ const NotePixmapParameters &params,
+ QPoint &s0, QPoint &s1)
+{
+ // The coordinates we set in s0 and s1 are relative to (m_above, m_left)
+
+ if (params.m_stemGoesUp) {
+ m_above = std::max
+ (m_above, stemLength - m_noteBodyHeight / 2);
+ } else {
+ m_below = std::max
+ (m_below, stemLength - m_noteBodyHeight / 2 + 1);
+ }
+
+ if (flagCount > 0) {
+ if (params.m_stemGoesUp) {
+ int width = 0, height = 0;
+ if (!m_font->getDimensions
+ (m_style->getFlagCharName(flagCount), width, height)) {
+ width = m_font->getWidth(m_style->getPartialFlagCharName(false));
+ }
+ m_right += width;
+ }
+ }
+
+ unsigned int stemThickness = getStemThickness();
+
+ NoteStyle::HFixPoint hfix;
+ NoteStyle::VFixPoint vfix;
+ m_style->getStemFixPoints(params.m_noteType, hfix, vfix);
+
+ switch (hfix) {
+
+ case NoteStyle::Normal:
+ case NoteStyle::Reversed:
+ if (params.m_stemGoesUp ^ (hfix == NoteStyle::Reversed)) {
+ s0.setX(m_noteBodyWidth - stemThickness);
+ } else {
+ s0.setX(0);
+ }
+ break;
+
+ case NoteStyle::Central:
+ if (params.m_stemGoesUp ^ (hfix == NoteStyle::Reversed)) {
+ s0.setX(m_noteBodyWidth / 2 + 1);
+ } else {
+ s0.setX(m_noteBodyWidth / 2);
+ }
+ break;
+ }
+
+ switch (vfix) {
+
+ case NoteStyle::Near:
+ case NoteStyle::Far:
+ if (params.m_stemGoesUp ^ (vfix == NoteStyle::Far)) {
+ s0.setY(0);
+ } else {
+ s0.setY(m_noteBodyHeight);
+ }
+ if (vfix == NoteStyle::Near) {
+ stemLength -= m_noteBodyHeight / 2;
+ } else {
+ stemLength += m_noteBodyHeight / 2;
+ }
+ break;
+
+ case NoteStyle::Middle:
+ if (params.m_stemGoesUp) {
+ s0.setY(m_noteBodyHeight * 3 / 8);
+ } else {
+ s0.setY(m_noteBodyHeight * 5 / 8);
+ }
+ stemLength -= m_noteBodyHeight / 8;
+ break;
+ }
+
+ if (params.m_stemGoesUp) {
+ s1.setY(s0.y() - stemLength + getStaffLineThickness());
+ } else {
+ s1.setY(s0.y() + stemLength);
+ }
+
+ s1.setX(s0.x());
+}
+
+void
+NotePixmapFactory::drawFlags(int flagCount,
+ const NotePixmapParameters &params,
+ const QPoint &, const QPoint &s1)
+{
+ if (flagCount < 1)
+ return ;
+
+ NoteCharacter flagChar;
+ bool found = getCharacter(m_style->getFlagCharName(flagCount),
+ flagChar,
+ PlainColour,
+ !params.m_stemGoesUp);
+
+ if (!found) {
+
+ // Handle fonts that don't have all the flags in separate characters
+
+ found = getCharacter(m_style->getPartialFlagCharName(false),
+ flagChar,
+ PlainColour,
+ !params.m_stemGoesUp);
+
+ if (!found) {
+ std::cerr << "Warning: NotePixmapFactory::drawFlags: No way to draw note with " << flagCount << " flags in this font!?" << std::endl;
+ return ;
+ }
+
+ QPoint hotspot = flagChar.getHotspot();
+
+ NoteCharacter oneFlagChar;
+ bool foundOne =
+ (flagCount > 1 ?
+ getCharacter(m_style->getPartialFlagCharName(true),
+ oneFlagChar,
+ PlainColour,
+ !params.m_stemGoesUp) : false);
+
+ unsigned int flagSpace = m_noteBodyHeight;
+ (void)m_font->getFlagSpacing(flagSpace);
+
+ for (int flag = 0; flag < flagCount; ++flag) {
+
+ // use flag_1 in preference to flag_0 for the final flag, so
+ // as to end with a flourish
+ if (flag == flagCount - 1 && foundOne)
+ flagChar = oneFlagChar;
+
+ int y = m_above + s1.y();
+ if (params.m_stemGoesUp)
+ y += flag * flagSpace;
+ else
+ y -= (flag * flagSpace) + flagChar.getHeight();
+
+ if (!m_inPrinterMethod) {
+
+ m_p->end();
+
+ // Super-slow
+
+ PixmapFunctions::drawPixmapMasked(*m_generatedPixmap,
+ *m_generatedMask,
+ m_left + s1.x() - hotspot.x(),
+ y,
+ *flagChar.getPixmap());
+
+ m_p->begin(m_generatedPixmap, m_generatedMask);
+
+ } else {
+
+ // No problem with mask here
+ m_p->drawNoteCharacter(m_left + s1.x() - hotspot.x(),
+ y,
+ flagChar);
+ }
+ }
+
+ } else { // the normal case
+
+ QPoint hotspot = flagChar.getHotspot();
+
+ int y = m_above + s1.y();
+ if (!params.m_stemGoesUp)
+ y -= flagChar.getHeight();
+
+ m_p->drawNoteCharacter(m_left + s1.x() - hotspot.x(), y, flagChar);
+ }
+}
+
+void
+NotePixmapFactory::drawStem(const NotePixmapParameters &params,
+ const QPoint &s0, const QPoint &s1,
+ int shortening)
+{
+ if (params.m_stemGoesUp)
+ shortening = -shortening;
+ for (int i = 0; i < getStemThickness(); ++i) {
+ m_p->drawLine(m_left + s0.x() + i, m_above + s0.y(),
+ m_left + s1.x() + i, m_above + s1.y() - shortening);
+ }
+}
+
+void
+NotePixmapFactory::makeRoomForBeams(const NotePixmapParameters &params)
+{
+ int beamSpacing = (int)(params.m_width * params.m_gradient);
+
+ if (params.m_stemGoesUp) {
+
+ beamSpacing = -beamSpacing;
+ if (beamSpacing < 0)
+ beamSpacing = 0;
+ m_above += beamSpacing + 2;
+
+ // allow a bit extra in case the h fixpoint is non-normal
+ m_right = std::max(m_right, params.m_width + m_noteBodyWidth);
+
+ } else {
+
+ if (beamSpacing < 0)
+ beamSpacing = 0;
+ m_below += beamSpacing + 2;
+
+ m_right = std::max(m_right, params.m_width);
+ }
+}
+
+void
+NotePixmapFactory::drawShallowLine(int x0, int y0, int x1, int y1,
+ int thickness, bool smooth)
+{
+ if (!smooth || m_inPrinterMethod || (y0 == y1)) {
+
+ if (!m_inPrinterMethod) {
+ if (m_selected)
+ m_p->painter().setBrush(GUIPalette::getColour(GUIPalette::SelectedElement));
+ else
+ m_p->painter().setBrush(Qt::black);
+ }
+ if (thickness < 4) {
+ for (int i = 0; i < thickness; ++i) {
+ m_p->drawLine(x0, y0 + i, x1, y1 + i);
+ }
+ } else {
+ Profiler profiler("NotePixmapFactory::drawShallowLine(polygon)");
+ QPointArray qp(4);
+ qp.setPoint(0, x0, y0);
+ qp.setPoint(1, x0, y0 + thickness);
+ qp.setPoint(2, x1, y1 + thickness);
+ qp.setPoint(3, x1, y1);
+ m_p->drawPolygon(qp);
+ }
+
+ return ;
+ }
+
+ Profiler profiler("NotePixmapFactory::drawShallowLine(points)");
+
+ int dv = y1 - y0;
+ int dh = x1 - x0;
+
+ static std::vector<QColor> colours, selectedColours;
+ if (colours.size() == 0) {
+ int h, s, v;
+ QColor c = GUIPalette::getColour(GUIPalette::SelectedElement);
+ c.hsv(&h, &s, &v);
+ for (int step = 0; step < 256; step += (step == 0 ? 63 : 64)) {
+ colours.push_back(QColor( -1, 0, step, QColor::Hsv));
+ selectedColours.push_back(QColor(h, 255 - step, v, QColor::Hsv));
+ }
+ }
+
+ int cx = x0, cy = y0;
+
+ int inc = 1;
+
+ if (dv < 0) {
+ dv = -dv;
+ inc = -1;
+ }
+
+ int g = 2 * dv - dh;
+ int dg1 = 2 * (dv - dh);
+ int dg2 = 2 * dv;
+
+ int segment = (dg2 - dg1) / 4;
+
+ while (cx < x1) {
+
+ if (g > 0) {
+ g += dg1;
+ cy += inc;
+ } else {
+ g += dg2;
+ }
+
+ int quartile = segment ? ((dg2 - g) / segment) : 0;
+ if (quartile < 0)
+ quartile = 0;
+ if (quartile > 3)
+ quartile = 3;
+ if (inc > 0)
+ quartile = 4 - quartile;
+ /*
+ NOTATION_DEBUG
+ << "x = " << cx << ", y = " << cy
+ << ", g = " << g << ", dg1 = " << dg1 << ", dg2 = " << dg2
+ << ", seg = " << segment << ", q = " << quartile << endl;
+ */
+ // I don't know enough about Qt to be sure of this, but I
+ // suspect this may be some of the most inefficient code ever
+ // written:
+
+ int off = 0;
+
+ if (m_selected) {
+ m_p->painter().setPen(selectedColours[quartile]);
+ } else {
+ m_p->painter().setPen(colours[quartile]);
+ }
+
+ m_p->drawPoint(cx, cy);
+ drawBeamsCount ++;
+
+ if (thickness > 1) {
+ if (m_selected) {
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ } else {
+ m_p->painter().setPen(Qt::black);
+ }
+ }
+
+ while (++off < thickness) {
+ m_p->drawPoint(cx, cy + off);
+ drawBeamsCount ++;
+ }
+
+ if (m_selected) {
+ m_p->painter().setPen(selectedColours[4 - quartile]);
+ } else {
+ m_p->painter().setPen(colours[4 - quartile]);
+ }
+
+ m_p->drawPoint(cx, cy + off);
+ drawBeamsCount ++;
+
+ ++cx;
+ }
+
+ m_p->painter().setPen(Qt::black);
+}
+
+void
+NotePixmapFactory::drawBeams(const QPoint &s1,
+ const NotePixmapParameters &params,
+ int beamCount)
+{
+ clock_t startTime = clock();
+
+ // draw beams: first we draw all the beams common to both ends of
+ // the section, then we draw beams for those that appear at the
+ // end only
+
+ int startY = m_above + s1.y(), startX = m_left + s1.x();
+ int commonBeamCount = std::min(beamCount, params.m_nextBeamCount);
+
+ unsigned int thickness;
+ (void)m_font->getBeamThickness(thickness);
+
+ int width = params.m_width;
+ double grad = params.m_gradient;
+ bool smooth = m_font->isSmooth();
+ int spacing = getLineSpacing();
+
+ int sign = (params.m_stemGoesUp ? 1 : -1);
+
+ if (!params.m_stemGoesUp)
+ startY -= thickness;
+
+ if (!smooth)
+ startY -= sign;
+ else if (grad > -0.01 && grad < 0.01)
+ startY -= sign;
+
+ if (m_inPrinterMethod) {
+ startX += getStemThickness() / 2;
+ }
+
+ for (int j = 0; j < commonBeamCount; ++j) {
+ int y = sign * j * spacing;
+ drawShallowLine(startX, startY + y, startX + width,
+ startY + (int)(width*grad) + y,
+ thickness, smooth);
+ drawBeamsBeamCount ++;
+ }
+
+ int partWidth = width / 3;
+ if (partWidth < 2)
+ partWidth = 2;
+ else if (partWidth > m_noteBodyWidth)
+ partWidth = m_noteBodyWidth;
+
+ if (params.m_thisPartialBeams) {
+ for (int j = commonBeamCount; j < beamCount; ++j) {
+ int y = sign * j * spacing;
+ drawShallowLine(startX, startY + y, startX + partWidth,
+ startY + (int)(partWidth*grad) + y,
+ thickness, smooth);
+ drawBeamsBeamCount ++;
+ }
+ }
+
+ if (params.m_nextPartialBeams) {
+ startX += width - partWidth;
+ startY += (int)((width - partWidth) * grad);
+
+ for (int j = commonBeamCount; j < params.m_nextBeamCount; ++j) {
+ int y = sign * j * spacing;
+ drawShallowLine(startX, startY + y, startX + partWidth,
+ startY + (int)(partWidth*grad) + y,
+ thickness, smooth);
+ drawBeamsBeamCount ++;
+ }
+ }
+
+ clock_t endTime = clock();
+ drawBeamsTime += (endTime - startTime);
+}
+
+void
+NotePixmapFactory::drawSlashes(const QPoint &s0,
+ const NotePixmapParameters &params,
+ int slashCount)
+{
+ unsigned int thickness;
+ (void)m_font->getBeamThickness(thickness);
+ thickness = thickness * 3 / 4;
+ if (thickness < 1)
+ thickness = 1;
+
+ int gap = thickness - 1;
+ if (gap < 1)
+ gap = 1;
+
+ bool smooth = m_font->isSmooth();
+
+ int width = m_noteBodyWidth * 4 / 5;
+ int sign = (params.m_stemGoesUp ? -1 : 1);
+
+ int offset =
+ (slashCount == 1 ? m_noteBodyHeight * 2 :
+ slashCount == 2 ? m_noteBodyHeight * 3 / 2 :
+ m_noteBodyHeight);
+ int y = m_above + s0.y() + sign * (offset + thickness / 2);
+
+ for (int i = 0; i < slashCount; ++i) {
+ int yoff = width / 2;
+ drawShallowLine(m_left + s0.x() - width / 2, y + yoff / 2,
+ m_left + s0.x() + width / 2 + getStemThickness(), y - yoff / 2,
+ thickness, smooth);
+ y += sign * (thickness + gap);
+ }
+}
+
+void
+NotePixmapFactory::makeRoomForTuplingLine(const NotePixmapParameters &params)
+{
+ int lineSpacing =
+ (int)(params.m_tuplingLineWidth * params.m_tuplingLineGradient);
+ int th = m_tupletCountFontMetrics.height();
+
+ if (params.m_tuplingLineY < 0) {
+
+ lineSpacing = -lineSpacing;
+ if (lineSpacing < 0)
+ lineSpacing = 0;
+ m_above = std::max(m_above, -params.m_tuplingLineY + th / 2);
+ m_above += lineSpacing + 1;
+
+ } else {
+
+ if (lineSpacing < 0)
+ lineSpacing = 0;
+ m_below = std::max(m_below, params.m_tuplingLineY + th / 2);
+ m_below += lineSpacing + 1;
+ }
+
+ m_right = std::max(m_right, params.m_tuplingLineWidth);
+}
+
+void
+NotePixmapFactory::drawTuplingLine(const NotePixmapParameters &params)
+{
+ int thickness = getStaffLineThickness() * 3 / 2;
+ int countSpace = thickness * 2;
+
+ QString count;
+ count.setNum(params.m_tupletCount);
+ QRect cr = m_tupletCountFontMetrics.boundingRect(count);
+
+ int tlw = params.m_tuplingLineWidth;
+ int indent = m_noteBodyWidth / 2;
+
+ if (tlw < (cr.width() + countSpace * 2 + m_noteBodyWidth * 2)) {
+ tlw += m_noteBodyWidth - 1;
+ indent = 0;
+ }
+
+ int w = (tlw - cr.width()) / 2 - countSpace;
+
+ int startX = m_left + indent;
+ int endX = startX + w;
+
+ int startY = params.m_tuplingLineY + m_above + getLineSpacing() / 2;
+ int endY = startY + (int)(params.m_tuplingLineGradient * w);
+
+ if (startY == endY)
+ ++thickness;
+
+ int tickOffset = getLineSpacing() / 2;
+ if (params.m_tuplingLineY >= 0)
+ tickOffset = -tickOffset;
+
+ // NOTATION_DEBUG << "adjusted params.m_tuplingLineWidth = "
+ // << tlw
+ // << ", cr.width = " << cr.width()
+ // << ", tickOffset = " << tickOffset << endl;
+ // NOTATION_DEBUG << "line: (" << startX << "," << startY << ") -> ("
+ // << endX << "," << endY << ")" << endl;
+
+ bool smooth = m_font->isSmooth();
+
+ if (!params.m_tuplingLineFollowsBeam) {
+ m_p->drawLine(startX, startY, startX, startY + tickOffset);
+ drawShallowLine(startX, startY, endX, endY, thickness, smooth);
+ }
+
+ m_p->painter().setFont(m_tupletCountFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(m_tupletCountFont);
+
+ int textX = endX + countSpace;
+ int textY = endY + cr.height() / 2;
+ // NOTATION_DEBUG << "text: (" << textX << "," << textY << ")" << endl;
+
+ m_p->drawText(textX, textY, count);
+
+ startX += tlw - w;
+ endX = startX + w;
+
+ startY += (int)(params.m_tuplingLineGradient * (tlw - w));
+ endY = startY + (int)(params.m_tuplingLineGradient * w);
+
+ // NOTATION_DEBUG << "line: (" << startX << "," << startY << ") -> ("
+ // << endX << "," << endY << ")" << endl;
+
+ if (!params.m_tuplingLineFollowsBeam) {
+ drawShallowLine(startX, startY, endX, endY, thickness, smooth);
+ m_p->drawLine(endX, endY, endX, endY + tickOffset);
+ }
+}
+
+void
+NotePixmapFactory::drawTie(bool above, int length, int shift)
+{
+#ifdef NASTY_OLD_FLAT_TIE_CODE
+
+ int tieThickness = getStaffLineThickness() * 2;
+ int tieCurve = m_font->getSize() * 2 / 3;
+ int height = tieCurve + tieThickness;
+ int x = m_left + m_noteBodyWidth;
+ int y = (above ? m_above - height - tieCurve / 2 :
+ m_above + m_noteBodyHeight + tieCurve / 2 + 1);
+ int i;
+
+ length -= m_noteBodyWidth;
+ if (length < tieCurve * 2)
+ length = tieCurve * 2;
+ if (length < m_noteBodyWidth * 3) {
+ length += m_noteBodyWidth - 2;
+ x -= m_noteBodyWidth / 2 - 1;
+ }
+
+ for (i = 0; i < tieThickness; ++i) {
+
+ if (above) {
+
+ m_p->drawArc
+ (x, y + i, tieCurve*2, tieCurve*2, 90*16, 70*16);
+
+ m_p->drawLine
+ (x + tieCurve, y + i, x + length - tieCurve - 2, y + i);
+
+ m_p->drawArc
+ (x + length - 2*tieCurve - 1, y + i,
+ tieCurve*2, tieCurve*2, 20*16, 70*16);
+
+ } else {
+
+ m_p->drawArc
+ (x, y + i - tieCurve, tieCurve*2, tieCurve*2, 200*16, 70*16);
+
+ m_p->drawLine
+ (x + tieCurve, y + height - i - 1,
+ x + length - tieCurve - 2, y + height - i - 1);
+
+ m_p->drawArc
+ (x + length - 2*tieCurve - 1, y + i - tieCurve,
+ tieCurve*2, tieCurve*2, 270*16, 70*16);
+ }
+ }
+#else
+
+ int origLength = length;
+
+ int x = m_left + m_noteBodyWidth + m_noteBodyWidth / 4 + shift;
+ length = origLength - m_noteBodyWidth - m_noteBodyWidth / 3 - shift;
+
+ // if the length is short, move the tie a bit closer to both notes
+ if (length < m_noteBodyWidth*2) {
+ x = m_left + m_noteBodyWidth + shift;
+ length = origLength - m_noteBodyWidth - shift;
+ }
+
+ if (length < m_noteBodyWidth) {
+ length = m_noteBodyWidth;
+ }
+
+ // We can't request a smooth slur here, because that always involves
+ // creating a new pixmap
+
+ QPoint hotspot;
+ drawSlurAux(length, 0, above, false, true, false, hotspot,
+ &m_p->painter(),
+ x,
+ above ? m_above : m_above + m_noteBodyHeight);
+ // above ? m_above - m_noteBodyHeight/2 :
+ // m_above + m_noteBodyHeight + m_noteBodyHeight/2);
+
+#endif
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeRestPixmap(const NotePixmapParameters &params)
+{
+ Profiler profiler("NotePixmapFactory::makeRestPixmap");
+
+ CharName charName(m_style->getRestCharName(params.m_noteType,
+ params.m_restOutsideStave));
+ // Check whether the font has the glyph for this charName;
+ // if not, substitute a rest-on-stave glyph for a rest-outside-stave glyph,
+ // and vice-versa.
+ NoteCharacter character;
+ if (!getCharacter(charName, character, PlainColour, false))
+ charName = m_style->getRestCharName(params.m_noteType,
+ !params.m_restOutsideStave);
+
+ bool encache = false;
+
+ if (params.m_tupletCount == 0 && !m_selected && !m_shaded &&
+ !params.m_restOutsideStave) {
+
+ if (params.m_dots == 0) {
+ return getCharacter(charName, PlainColour, false).getCanvasPixmap();
+ } else {
+ NotePixmapCache::iterator ci(m_dottedRestCache->find(charName));
+ if (ci != m_dottedRestCache->end())
+ return new QCanvasPixmap
+ (*ci->second, QPoint(ci->second->offsetX(),
+ ci->second->offsetY()));
+ else
+ encache = true;
+ }
+ }
+
+ QPoint hotspot(m_font->getHotspot(charName));
+ drawRestAux(params, hotspot, 0, 0, 0);
+
+ QCanvasPixmap* canvasMap = makeCanvasPixmap(hotspot);
+ if (encache) {
+ m_dottedRestCache->insert(std::pair<CharName, QCanvasPixmap*>
+ (charName, new QCanvasPixmap
+ (*canvasMap, hotspot)));
+ }
+ return canvasMap;
+}
+
+void
+NotePixmapFactory::drawRest(const NotePixmapParameters &params,
+ QPainter &painter, int x, int y)
+{
+ Profiler profiler("NotePixmapFactory::drawRest");
+ m_inPrinterMethod = true;
+ QPoint hotspot; // unused
+ drawRestAux(params, hotspot, &painter, x, y);
+ m_inPrinterMethod = false;
+}
+
+void
+NotePixmapFactory::drawRestAux(const NotePixmapParameters &params,
+ QPoint &hotspot, QPainter *painter, int x, int y)
+{
+ CharName charName(m_style->getRestCharName(params.m_noteType,
+ params.m_restOutsideStave));
+ NoteCharacter character = getCharacter(charName,
+ params.m_quantized ? QuantizedColour :
+ PlainColour,
+ false);
+
+ NoteCharacter dot = getCharacter(NoteCharacterNames::DOT, PlainColour, false);
+
+ int dotWidth = dot.getWidth();
+ if (dotWidth < getNoteBodyWidth() / 2)
+ dotWidth = getNoteBodyWidth() / 2;
+
+ m_above = m_left = 0;
+ m_below = dot.getHeight() / 2; // for dotted shallow rests like semibreve
+ m_right = dotWidth / 2 + dotWidth * params.m_dots;
+ m_noteBodyWidth = character.getWidth();
+ m_noteBodyHeight = character.getHeight();
+
+ if (params.m_tupletCount)
+ makeRoomForTuplingLine(params);
+
+ // we'll adjust this for tupling line after drawing rest character:
+ hotspot = m_font->getHotspot(charName);
+
+ if (params.m_restOutsideStave &&
+ (charName == NoteCharacterNames::MULTI_REST ||
+ charName == NoteCharacterNames::MULTI_REST_ON_STAFF)) {
+ makeRoomForLegerLines(params);
+ }
+ if (painter) {
+ painter->save();
+ m_p->beginExternal(painter);
+ painter->translate(x - m_left, y - m_above - hotspot.y());
+ } else {
+ createPixmapAndMask(m_noteBodyWidth + m_left + m_right,
+ m_noteBodyHeight + m_above + m_below);
+ }
+
+ m_p->drawNoteCharacter(m_left, m_above, character);
+
+ if (params.m_tupletCount)
+ drawTuplingLine(params);
+
+ hotspot.setX(m_left);
+ hotspot.setY(m_above + hotspot.y());
+
+ int restY = hotspot.y() - dot.getHeight() - getStaffLineThickness();
+ if (params.m_noteType == Note::Semibreve ||
+ params.m_noteType == Note::Breve) {
+ restY += getLineSpacing();
+ }
+
+ for (int i = 0; i < params.m_dots; ++i) {
+ int x = m_left + m_noteBodyWidth + i * dotWidth + dotWidth / 2;
+ m_p->drawNoteCharacter(x, restY, dot);
+ }
+
+ if (params.m_restOutsideStave &&
+ (charName == NoteCharacterNames::MULTI_REST ||
+ charName == NoteCharacterNames::MULTI_REST_ON_STAFF)) {
+ drawLegerLines(params);
+ }
+
+ if (painter) {
+ painter->restore();
+ }
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeClefPixmap(const Clef &clef)
+{
+ Profiler profiler("NotePixmapFactory::makeClefPixmap");
+ NoteCharacter plain = getCharacter(m_style->getClefCharName(clef),
+ PlainColour, false);
+
+ int oct = clef.getOctaveOffset();
+ if (oct == 0)
+ return plain.getCanvasPixmap();
+
+ // fix #1522784 and use 15 rather than 16 for double octave offset
+ int adjustedOctave = (8 * (oct < 0 ? -oct : oct));
+ if (adjustedOctave > 8)
+ adjustedOctave--;
+ else if (adjustedOctave < 8)
+ adjustedOctave++;
+
+ QString text = QString("%1").arg(adjustedOctave);
+ QRect rect = m_clefOttavaFontMetrics.boundingRect(text);
+
+ createPixmapAndMask(plain.getWidth(),
+ plain.getHeight() + rect.height());
+
+ if (m_selected) {
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ }
+
+ m_p->drawNoteCharacter(0, oct < 0 ? 0 : rect.height(), plain);
+
+ m_p->painter().setFont(m_clefOttavaFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(m_clefOttavaFont);
+
+ m_p->drawText(plain.getWidth() / 2 - rect.width() / 2,
+ oct < 0 ? plain.getHeight() + rect.height() - 1 :
+ rect.height(), text);
+
+ m_p->painter().setPen(Qt::black);
+ QPoint hotspot(plain.getHotspot());
+ if (oct > 0) hotspot.setY(hotspot.y() + rect.height());
+ return makeCanvasPixmap(hotspot, true);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makePedalDownPixmap()
+{
+ return getCharacter(NoteCharacterNames::PEDAL_MARK, PlainColour, false)
+ .getCanvasPixmap();
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makePedalUpPixmap()
+{
+ return getCharacter(NoteCharacterNames::PEDAL_UP_MARK, PlainColour, false)
+ .getCanvasPixmap();
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeUnknownPixmap()
+{
+ Profiler profiler("NotePixmapFactory::makeUnknownPixmap");
+ return getCharacter(NoteCharacterNames::UNKNOWN, PlainColour, false)
+ .getCanvasPixmap();
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeToolbarPixmap(const char *name, bool menuSize)
+{
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QString fileBase = pixmapDir + "/toolbar/";
+ if (menuSize) fileBase += "menu-";
+ fileBase += name;
+ if (QFile(fileBase + ".png").exists()) {
+ return new QCanvasPixmap(fileBase + ".png");
+ } else if (QFile(fileBase + ".xpm").exists()) {
+ return new QCanvasPixmap(fileBase + ".xpm");
+ } else if (menuSize) {
+ return makeToolbarPixmap(name, false);
+ } else {
+ // this will fail, but we don't want to return a null pointer
+ return new QCanvasPixmap(fileBase + ".png");
+ }
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeNoteMenuPixmap(timeT duration,
+ timeT &errorReturn)
+{
+ Note nearestNote = Note::getNearestNote(duration);
+ bool triplet = false;
+ errorReturn = 0;
+
+ if (nearestNote.getDuration() != duration) {
+ Note tripletNote = Note::getNearestNote(duration * 3 / 2);
+ if (tripletNote.getDuration() == duration * 3 / 2) {
+ nearestNote = tripletNote;
+ triplet = true;
+ } else {
+ errorReturn = duration - nearestNote.getDuration();
+ }
+ }
+
+ QString noteName = NotationStrings::getReferenceName(nearestNote);
+ if (triplet)
+ noteName = "3-" + noteName;
+ noteName = "menu-" + noteName;
+ return makeToolbarPixmap(noteName);
+}
+
+QCanvasPixmap *
+NotePixmapFactory::makeMarkMenuPixmap(Mark mark)
+{
+ if (mark == Marks::Sforzando ||
+ mark == Marks::Rinforzando) {
+ return makeToolbarPixmap(mark.c_str());
+ } else {
+ NoteFont *font = 0;
+ try {
+ font = NoteFontFactory::getFont
+ (NoteFontFactory::getDefaultFontName(), 6);
+ } catch (Exception) {
+ font = NoteFontFactory::getFont
+ (NoteFontFactory::getDefaultFontName(),
+ NoteFontFactory::getDefaultSize(NoteFontFactory::getDefaultFontName()));
+ }
+ NoteCharacter character = font->getCharacter
+ (NoteStyleFactory::getStyle(NoteStyleFactory::DefaultStyle)->
+ getMarkCharName(mark));
+ return character.getCanvasPixmap();
+ }
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeKeyPixmap(const Key &key,
+ const Clef &clef,
+ Key previousKey)
+{
+ Profiler profiler("NotePixmapFactory::makeKeyPixmap");
+
+ std::vector<int> ah0 = previousKey.getAccidentalHeights(clef);
+ std::vector<int> ah1 = key.getAccidentalHeights(clef);
+
+ int cancelCount = 0;
+ if (key.isSharp() != previousKey.isSharp())
+ cancelCount = ah0.size();
+ else if (ah1.size() < ah0.size())
+ cancelCount = ah0.size() - ah1.size();
+
+ CharName keyCharName;
+ if (key.isSharp())
+ keyCharName = NoteCharacterNames::SHARP;
+ else
+ keyCharName = NoteCharacterNames::FLAT;
+
+ NoteCharacter keyCharacter;
+ NoteCharacter cancelCharacter;
+
+ keyCharacter = getCharacter(keyCharName, PlainColour, false);
+ if (cancelCount > 0) {
+ cancelCharacter = getCharacter(NoteCharacterNames::NATURAL, PlainColour, false);
+ }
+
+ int x = 0;
+ int lw = getLineSpacing();
+ int keyDelta = keyCharacter.getWidth() - keyCharacter.getHotspot().x();
+
+ int cancelDelta = 0;
+ int between = 0;
+ if (cancelCount > 0) {
+ cancelDelta = cancelCharacter.getWidth() + cancelCharacter.getWidth() / 3;
+ between = cancelCharacter.getWidth();
+ }
+
+ createPixmapAndMask(keyDelta * ah1.size() + cancelDelta * cancelCount + between +
+ keyCharacter.getWidth() / 4, lw * 8 + 1);
+
+ if (key.isSharp() != previousKey.isSharp()) {
+
+ // cancellation first
+
+ for (int i = 0; i < cancelCount; ++i) {
+
+ int h = ah0[ah0.size() - cancelCount + i];
+ int y = (lw * 2) + ((8 - h) * lw) / 2 - cancelCharacter.getHotspot().y();
+
+ m_p->drawNoteCharacter(x, y, cancelCharacter);
+
+ x += cancelDelta;
+ }
+
+ if (cancelCount > 0) {
+ x += between;
+ }
+ }
+
+ for (unsigned int i = 0; i < ah1.size(); ++i) {
+
+ int h = ah1[i];
+ int y = (lw * 2) + ((8 - h) * lw) / 2 - keyCharacter.getHotspot().y();
+
+ m_p->drawNoteCharacter(x, y, keyCharacter);
+
+ x += keyDelta;
+ }
+
+ if (key.isSharp() == previousKey.isSharp()) {
+
+ // cancellation afterwards
+
+ if (cancelCount > 0) {
+ x += between;
+ }
+
+ for (int i = 0; i < cancelCount; ++i) {
+
+ int h = ah0[ah0.size() - cancelCount + i];
+ int y = (lw * 2) + ((8 - h) * lw) / 2 - cancelCharacter.getHotspot().y();
+
+ m_p->drawNoteCharacter(x, y, cancelCharacter);
+
+ x += cancelDelta;
+ }
+ }
+
+ return makeCanvasPixmap(m_pointZero);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeClefDisplayPixmap(const Clef &clef)
+{
+ QCanvasPixmap* clefPixmap = makeClefPixmap(clef);
+
+ int lw = getLineSpacing();
+ int width = clefPixmap->width() + 6 * getNoteBodyWidth();
+
+ createPixmapAndMask(width, lw * 10 + 1);
+
+ int h = clef.getAxisHeight();
+ int y = (lw * 3) + ((8 - h) * lw) / 2;
+ int x = 3 * getNoteBodyWidth();
+ m_p->drawPixmap(x, y - clefPixmap->offsetY(), *clefPixmap);
+
+ for (h = 0; h <= 8; h += 2) {
+ y = (lw * 3) + ((8 - h) * lw) / 2;
+ m_p->drawLine(x / 2, y, m_generatedWidth - x / 2 - 1, y);
+ }
+
+ delete clefPixmap;
+
+ return makeCanvasPixmap(m_pointZero);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeKeyDisplayPixmap(const Key &key, const Clef &clef)
+{
+ std::vector<int> ah = key.getAccidentalHeights(clef);
+
+ CharName charName = (key.isSharp() ?
+ NoteCharacterNames::SHARP :
+ NoteCharacterNames::FLAT);
+
+ QCanvasPixmap* clefPixmap = makeClefPixmap(clef);
+ QPixmap accidentalPixmap(*m_font->getCharacter(charName).getPixmap());
+ QPoint hotspot(m_font->getHotspot(charName));
+
+ int lw = getLineSpacing();
+ int delta = accidentalPixmap.width() - hotspot.x();
+ int maxDelta = getAccidentalWidth(Sharp);
+ int width = clefPixmap->width() + 5 * maxDelta + 7 * maxDelta;
+ int x = clefPixmap->width() + 5 * maxDelta / 2;
+
+ createPixmapAndMask(width, lw * 10 + 1);
+
+ int h = clef.getAxisHeight();
+ int y = (lw * 3) + ((8 - h) * lw) / 2;
+ m_p->drawPixmap(2 * maxDelta, y - clefPixmap->offsetY(), *clefPixmap);
+
+ for (unsigned int i = 0; i < ah.size(); ++i) {
+
+ h = ah[i];
+ y = (lw * 3) + ((8 - h) * lw) / 2 - hotspot.y();
+
+ m_p->drawPixmap(x, y, accidentalPixmap);
+
+ x += delta;
+ }
+
+ for (h = 0; h <= 8; h += 2) {
+ y = (lw * 3) + ((8 - h) * lw) / 2;
+ m_p->drawLine(maxDelta, y, m_generatedWidth - 2*maxDelta - 1, y);
+ }
+
+ delete clefPixmap;
+ return makeCanvasPixmap(m_pointZero);
+}
+
+int
+NotePixmapFactory::getClefAndKeyWidth(const Key &key, const Clef &clef)
+{
+ std::vector<int> ah = key.getAccidentalHeights(clef);
+ Accidental accidental = key.isSharp() ? Sharp : Flat;
+ NoteCharacter plain = getCharacter(m_style->getClefCharName(clef),
+ PlainColour, false);
+
+ int clefWidth = plain.getWidth();
+ int accWidth = getAccidentalWidth(accidental);
+ int maxDelta = getAccidentalWidth(Sharp);
+
+ int width = clefWidth + 2 * maxDelta + ah.size() * accWidth;
+
+ return width;
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeTrackHeaderPixmap(
+ int width, int height, TrackHeader *header)
+{
+
+ height -= 4; // Make room to the label frame :
+ // 4 = 2 * (margin + lineWidth)
+
+ createPixmapAndMask(width, height);
+
+ int lw = getLineSpacing();
+ int h;
+ QColor colour;
+ int maxDelta = getAccidentalWidth(Sharp);
+
+ // Staff Y position inside the whole header
+ int offset = (height - 10 * lw -1) / 2;
+
+ // Draw staff lines
+ m_p->painter().setPen(QPen(Qt::black, getStaffLineThickness()));
+ for (h = 0; h <= 8; h += 2) {
+ int y = (lw * 3) + ((8 - h) * lw) / 2;
+ m_p->drawLine(maxDelta/2, y + offset, m_generatedWidth - maxDelta/2, y + offset);
+ }
+
+ if (header->isAClefToDraw()) {
+ const Clef &clef = header->getClef();
+ // TODO : use colours from GUIPalette
+ colour = header->isClefInconsistent() ? Qt::red : Qt::black;
+
+ int hue, sat, val;
+ colour.getHsv(&hue, &sat, &val);
+ NoteCharacter clefChar = m_font->getCharacterColoured
+ (m_style->getClefCharName(clef),
+ hue, val, NoteFont::Screen, false);
+
+ // Draw clef
+ h = clef.getAxisHeight();
+ int y = (lw * 3) + ((8 - h) * lw) / 2;
+ m_p->drawNoteCharacter(maxDelta,
+ y - clefChar.getHotspot().y() + offset, clefChar);
+
+ // If necessary, write 8 or 15 above or under the clef
+ int oct = clef.getOctaveOffset();
+ if (oct != 0) {
+
+ int adjustedOctave = (8 * (oct < 0 ? -oct : oct));
+ if (adjustedOctave > 8)
+ adjustedOctave--;
+ else if (adjustedOctave < 8)
+ adjustedOctave++;
+
+ QString text = QString("%1").arg(adjustedOctave);
+ QRect rect = m_clefOttavaFontMetrics.boundingRect(text);
+
+ m_p->painter().setPen(colour);
+
+ m_p->painter().setFont(m_clefOttavaFont);
+ // m_p->maskPainter().setFont(m_clefOttavaFont);
+ int xpos = maxDelta + clefChar.getWidth() / 2 - rect.width() / 2;
+ int ypos = y - clefChar.getHotspot().y() + offset
+ + (oct < 0 ? clefChar.getHeight() + rect.height() - 1 : - rect.height() / 3);
+ m_p->drawText(xpos, ypos, text);
+ }
+
+ // TODO : use colours from GUIPalette
+ colour = header->isKeyInconsistent() ? Qt::red : Qt::black;
+
+
+ // Draw the key signature if any
+
+ const Key &key = header->getKey();
+ std::vector<int> ah = key.getAccidentalHeights(clef);
+
+ CharName charName = key.isSharp() ?
+ NoteCharacterNames::SHARP :
+ NoteCharacterNames::FLAT;
+
+ colour.getHsv(&hue, &sat, &val);
+ NoteCharacter accident = m_font->getCharacterColoured(charName,
+ hue, val, NoteFont::Screen, false);
+
+ QPoint hotspot(m_font->getHotspot(charName));
+ int delta = accident.getWidth() - hotspot.x();
+
+ int x = clefChar.getWidth() + maxDelta;
+ for (unsigned int i = 0; i < ah.size(); ++i) {
+ h = ah[i];
+ y = (lw * 3) + ((8 - h) * lw) / 2 - hotspot.y() + offset;
+ m_p->drawNoteCharacter(x, y, accident);
+
+ x += delta;
+ }
+
+ }
+
+ m_p->painter().setFont(m_trackHeaderFont);
+ // m_p->maskPainter().setFont(m_trackHeaderFont);
+
+ QString text;
+ QString textLine;
+
+ int charHeight = m_trackHeaderFontMetrics.height();
+ int charWidth = m_trackHeaderFontMetrics.maxWidth();
+
+ const QString transposeText = header->getTransposeText();
+ QRect bounds = m_trackHeaderBoldFontMetrics.boundingRect(transposeText);
+ int transposeWidth = bounds.width();
+
+
+ // Write upper text (track name and track label)
+
+ m_p->painter().setPen(Qt::black);
+ text = header->getUpperText();
+ int numberOfTextLines = header->getNumberOfTextLines();
+
+ for (int l=1; l<=numberOfTextLines; l++) {
+ int upperTextY = charHeight + (l - 1) * getTrackHeaderTextLineSpacing();
+ if (l == numberOfTextLines) {
+ int transposeSpace = transposeWidth ? transposeWidth + charWidth / 4 : 0;
+ textLine = getOneLine(text, width - transposeSpace - charWidth / 2);
+ if (!text.isEmpty()) {
+ // String too long : cut it and replace last character by dots
+ int len = textLine.length();
+ if (len > 1) textLine.replace(len - 1, 1, i18n("..."));
+ }
+ } else {
+ textLine = getOneLine(text, width - charWidth / 2);
+ }
+ if (textLine.isEmpty()) break;
+ m_p->drawText(charWidth / 4, upperTextY, textLine);
+ }
+
+
+ // Write transposition text
+
+ // TODO : use colours from GUIPalette
+ colour = header->isTransposeInconsistent() ? Qt::red : Qt::black;
+ m_p->painter().setFont(m_trackHeaderBoldFont);
+ // m_p->maskPainter().setFont(m_trackHeaderBoldFont);
+ m_p->painter().setPen(colour);
+
+ m_p->drawText(width - transposeWidth - charWidth / 4,
+ charHeight
+ + (numberOfTextLines - 1) * getTrackHeaderTextLineSpacing(),
+ transposeText);
+
+
+ // Write lower text (segment label)
+
+ // TODO : use colours from GUIPalette
+ colour = header->isLabelInconsistent() ? Qt::red : Qt::black;
+ m_p->painter().setFont(m_trackHeaderFont);
+ // m_p->maskPainter().setFont(m_trackHeaderFont);
+
+ m_p->painter().setPen(colour);
+ text = header->getLowerText();
+
+ for (int l=1; l<=numberOfTextLines; l++) {
+ int lowerTextY = m_generatedHeight - 4 // -4 : adjust
+ - (numberOfTextLines - l) * getTrackHeaderTextLineSpacing();
+
+ QString textLine = getOneLine(text, width - charWidth / 2);
+ if (textLine.isEmpty()) break;
+
+ if ((l == numberOfTextLines) && !text.isEmpty()) {
+ // String too long : cut it and replace last character by dots
+ int len = textLine.length();
+ if (len > 1) textLine.replace(len - 1, 1, i18n("..."));
+ }
+
+ m_p->drawText(charWidth / 4, lowerTextY, textLine);
+ }
+
+ return makeCanvasPixmap(m_pointZero, true);
+}
+
+int
+NotePixmapFactory::getTrackHeaderNTL(int height)
+{
+ int clefMaxHeight = 12 * getLineSpacing();
+ int textLineHeight = getTrackHeaderTextLineSpacing();
+ int numberOfLines = ((height - clefMaxHeight) / 2) / textLineHeight;
+ return (numberOfLines > 0) ? numberOfLines : 1;
+}
+
+int
+NotePixmapFactory::getTrackHeaderTextWidth(QString str)
+{
+ QRect bounds = m_trackHeaderFontMetrics.boundingRect(str);
+ return bounds.width();
+}
+
+int
+NotePixmapFactory::getTrackHeaderTextLineSpacing()
+{
+ // 3/2 is some arbitrary line spacing
+ return m_trackHeaderFont.pixelSize() * 3 / 2;
+}
+
+QString
+NotePixmapFactory::getOneLine(QString &text, int width)
+{
+ QString str;
+ int n;
+
+ // Immediately stop if string is empty or only contains white spaces ...
+ if (text.stripWhiteSpace().isEmpty()) return QString("");
+
+ // ... or if width is too small.
+ if (width < m_trackHeaderFontMetrics.boundingRect(text.left(1)).width())
+ return QString("");
+
+ // Get a first approx. string length
+ int totalLength = text.length();
+ n = totalLength * width / getTrackHeaderTextWidth(text) + 1;
+ if (n > totalLength) n = totalLength;
+
+ // Verify string size is less than width then correct it if necessary
+ while (((getTrackHeaderTextWidth(text.left(n))) > width) && n) n--;
+
+ if (n == 0) {
+ str = text;
+ text = QString("");
+ } else {
+ str = text.left(n);
+ text.remove(0, n);
+ }
+
+ return str;
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makePitchDisplayPixmap(int p, const Clef &clef,
+ bool useSharps)
+{
+ NotationRules rules;
+
+ Pitch pitch(p);
+ Accidental accidental(pitch.getAccidental(useSharps));
+ NotePixmapParameters params(Note::Crotchet, 0, accidental);
+
+ QCanvasPixmap* clefPixmap = makeClefPixmap(clef);
+
+ int lw = getLineSpacing();
+ int width = getClefWidth(Clef::Bass) + 10 * getNoteBodyWidth();
+
+ int h = pitch.getHeightOnStaff(clef, useSharps);
+ params.setStemGoesUp(rules.isStemUp(h));
+
+ if (h < -1)
+ params.setStemLength(lw * (4 - h) / 2);
+ else if (h > 9)
+ params.setStemLength(lw * (h - 4) / 2);
+ if (h > 8)
+ params.setLegerLines(h - 8);
+ else if (h < 0)
+ params.setLegerLines(h);
+
+ params.setIsOnLine(h % 2 == 0);
+ params.setSelected(m_selected);
+
+ QCanvasPixmap *notePixmap = makeNotePixmap(params);
+
+ int pixmapHeight = lw * 12 + 1;
+ int yoffset = lw * 3;
+ if (h > 12) {
+ pixmapHeight += 6 * lw;
+ yoffset += 6 * lw;
+ } else if (h < -4) {
+ pixmapHeight += 6 * lw;
+ }
+
+ createPixmapAndMask(width, pixmapHeight);
+
+ int x =
+ getClefWidth(Clef::Bass) + 5 * getNoteBodyWidth() -
+ getAccidentalWidth(accidental);
+ int y = yoffset + ((8 - h) * lw) / 2 - notePixmap->offsetY();
+ m_p->drawPixmap(x, y, *notePixmap);
+
+ h = clef.getAxisHeight();
+ x = 3 * getNoteBodyWidth();
+ y = yoffset + ((8 - h) * lw) / 2;
+ m_p->drawPixmap(x, y - clefPixmap->offsetY(), *clefPixmap);
+
+ for (h = 0; h <= 8; h += 2) {
+ y = yoffset + ((8 - h) * lw) / 2;
+ m_p->drawLine(x / 2, y, m_generatedWidth - x / 2, y);
+ }
+
+ delete clefPixmap;
+ delete notePixmap;
+
+ return makeCanvasPixmap(m_pointZero);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makePitchDisplayPixmap(int p, const Clef &clef,
+ int octave, int step)
+{
+ NotationRules rules;
+
+ Pitch pitch(step, octave, p, 0);
+ Accidental accidental = pitch.getDisplayAccidental(Key("C major"));
+ NotePixmapParameters params(Note::Crotchet, 0, accidental);
+
+ QCanvasPixmap* clefPixmap = makeClefPixmap(clef);
+
+ int lw = getLineSpacing();
+ int width = getClefWidth(Clef::Bass) + 10 * getNoteBodyWidth();
+
+ int h = pitch.getHeightOnStaff
+ (clef,
+ Key("C major"));
+ params.setStemGoesUp(rules.isStemUp(h));
+
+ if (h < -1)
+ params.setStemLength(lw * (4 - h) / 2);
+ else if (h > 9)
+ params.setStemLength(lw * (h - 4) / 2);
+ if (h > 8)
+ params.setLegerLines(h - 8);
+ else if (h < 0)
+ params.setLegerLines(h);
+
+ params.setIsOnLine(h % 2 == 0);
+ params.setSelected(m_selected);
+
+ QCanvasPixmap *notePixmap = makeNotePixmap(params);
+
+ int pixmapHeight = lw * 12 + 1;
+ int yoffset = lw * 3;
+ if (h > 12) {
+ pixmapHeight += 6 * lw;
+ yoffset += 6 * lw;
+ } else if (h < -4) {
+ pixmapHeight += 6 * lw;
+ }
+
+ createPixmapAndMask(width, pixmapHeight);
+
+ int x =
+ getClefWidth(Clef::Bass) + 5 * getNoteBodyWidth() -
+ getAccidentalWidth(accidental);
+ int y = yoffset + ((8 - h) * lw) / 2 - notePixmap->offsetY();
+ m_p->drawPixmap(x, y, *notePixmap);
+
+ h = clef.getAxisHeight();
+ x = 3 * getNoteBodyWidth();
+ y = yoffset + ((8 - h) * lw) / 2;
+ m_p->drawPixmap(x, y - clefPixmap->offsetY(), *clefPixmap);
+
+ for (h = 0; h <= 8; h += 2) {
+ y = yoffset + ((8 - h) * lw) / 2;
+ m_p->drawLine(x / 2, y, m_generatedWidth - x / 2, y);
+ }
+
+ delete clefPixmap;
+ delete notePixmap;
+
+ return makeCanvasPixmap(m_pointZero);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeHairpinPixmap(int length, bool isCrescendo)
+{
+ Profiler profiler("NotePixmapFactory::makeHairpinPixmap");
+ drawHairpinAux(length, isCrescendo, 0, 0, 0);
+ return makeCanvasPixmap(QPoint(0, m_generatedHeight / 2));
+}
+
+void
+NotePixmapFactory::drawHairpin(int length, bool isCrescendo,
+ QPainter &painter, int x, int y)
+{
+ Profiler profiler("NotePixmapFactory::drawHairpin");
+ m_inPrinterMethod = true;
+ drawHairpinAux(length, isCrescendo, &painter, x, y);
+ m_inPrinterMethod = false;
+}
+
+void
+NotePixmapFactory::drawHairpinAux(int length, bool isCrescendo,
+ QPainter *painter, int x, int y)
+{
+ int nbh = getNoteBodyHeight();
+ int nbw = getNoteBodyWidth();
+
+ int height = (int)(((double)nbh / (double)(nbw * 40)) * length) + nbh;
+ int thickness = getStaffLineThickness() * 3 / 2;
+
+ // NOTATION_DEBUG << "NotePixmapFactory::makeHairpinPixmap: mapped length " << length << " to height " << height << " (nbh = " << nbh << ", nbw = " << nbw << ")" << endl;
+
+ if (height < nbh)
+ height = nbh;
+ if (height > nbh*2)
+ height = nbh * 2;
+
+ height += thickness - 1;
+
+ if (painter) {
+ painter->save();
+ m_p->beginExternal(painter);
+ painter->translate(x, y - height / 2);
+ } else {
+ createPixmapAndMask(length, height);
+ }
+
+ if (m_selected) {
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ }
+
+ int left = 1, right = length - 2 * nbw / 3 + 1;
+
+ bool smooth = m_font->isSmooth();
+
+ if (isCrescendo) {
+ drawShallowLine(left, height / 2 - 1,
+ right, height - thickness - 1, thickness, smooth);
+ drawShallowLine(left, height / 2 - 1, right, 0, thickness, smooth);
+ } else {
+ drawShallowLine(left, 0, right, height / 2 - 1, thickness, smooth);
+ drawShallowLine(left, height - thickness - 1,
+ right, height / 2 - 1, thickness, smooth);
+ }
+
+ m_p->painter().setPen(Qt::black);
+
+ if (painter) {
+ painter->restore();
+ }
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeSlurPixmap(int length, int dy, bool above, bool phrasing)
+{
+ Profiler profiler("NotePixmapFactory::makeSlurPixmap");
+
+ //!!! could remove "height > 5" requirement if we did a better job of
+ // sizing so that any horizontal part was rescaled down to exactly
+ // 1 pixel wide instead of blurring
+ bool smooth = m_font->isSmooth() && getNoteBodyHeight() > 5;
+ QPoint hotspot;
+ if (length < getNoteBodyWidth()*2)
+ length = getNoteBodyWidth() * 2;
+ drawSlurAux(length, dy, above, smooth, false, phrasing, hotspot, 0, 0, 0);
+
+ m_p->end();
+
+ if (smooth) {
+
+ QImage i = m_generatedPixmap->convertToImage();
+ if (i.depth() == 1)
+ i = i.convertDepth(32);
+ i = i.smoothScale(i.width() / 2, i.height() / 2);
+
+ delete m_generatedPixmap;
+ delete m_generatedMask;
+ QPixmap newPixmap(i);
+ QCanvasPixmap *p = new QCanvasPixmap(newPixmap, hotspot);
+ p->setMask(PixmapFunctions::generateMask(newPixmap,
+ Qt::white.rgb()));
+ return p;
+
+ } else {
+
+ QCanvasPixmap *p = new QCanvasPixmap(*m_generatedPixmap, hotspot);
+ p->setMask(PixmapFunctions::generateMask(*m_generatedPixmap,
+ Qt::white.rgb()));
+ delete m_generatedPixmap;
+ delete m_generatedMask;
+ return p;
+ }
+}
+
+void
+NotePixmapFactory::drawSlur(int length, int dy, bool above, bool phrasing,
+ QPainter &painter, int x, int y)
+{
+ Profiler profiler("NotePixmapFactory::drawSlur");
+ QPoint hotspot;
+ m_inPrinterMethod = true;
+ if (length < getNoteBodyWidth()*2)
+ length = getNoteBodyWidth() * 2;
+ drawSlurAux(length, dy, above, false, false, phrasing, hotspot, &painter, x, y);
+ m_inPrinterMethod = false;
+}
+
+void
+NotePixmapFactory::drawSlurAux(int length, int dy, bool above,
+ bool smooth, bool flat, bool phrasing,
+ QPoint &hotspot, QPainter *painter, int x, int y)
+{
+ QWMatrix::TransformationMode mode = QWMatrix::transformationMode();
+ QWMatrix::setTransformationMode(QWMatrix::Points);
+
+ int thickness = getStaffLineThickness() * 2;
+ if (phrasing)
+ thickness = thickness * 3 / 4;
+ int nbh = getNoteBodyHeight(), nbw = getNoteBodyWidth();
+
+ // Experiment with rotating the painter rather than the control points.
+ double theta = 0;
+ bool rotate = false;
+ if (dy != 0) {
+ // We have opposite (dy) and adjacent (length).
+ theta = atan(double(dy) / double(length)) * 180.0 / M_PI;
+ // NOTATION_DEBUG << "slur: dy is " << dy << ", length " << length << ", rotating through " << theta << endl;
+ rotate = true;
+ }
+
+ // draw normal slur for very slopey phrasing slur:
+ if (theta < -5 || theta > 5)
+ phrasing = false;
+
+ int y0 = 0, my = 0;
+
+ float noteLengths = float(length) / nbw;
+ if (noteLengths < 1)
+ noteLengths = 1;
+
+ my = int(0 - nbh * sqrt(noteLengths) / 2);
+ if (flat)
+ my = my * 2 / 3;
+ else if (phrasing)
+ my = my * 3 / 4;
+ if (!above)
+ my = -my;
+
+ bool havePixmap = false;
+ QPoint topLeft, bottomRight;
+
+ if (smooth)
+ thickness += 2;
+
+ for (int i = 0; i < thickness; ++i) {
+
+ Spline::PointList pl;
+
+ if (!phrasing) {
+ pl.push_back(QPoint(length / 6, my));
+ pl.push_back(QPoint(length - length / 6, my));
+ } else {
+ pl.push_back(QPoint(abs(my) / 4, my / 3));
+ pl.push_back(QPoint(length / 6, my));
+
+ if (theta > 1) {
+ pl.push_back(QPoint(length * 3 / 8, my * 3 / 2));
+ } else if (theta < -1) {
+ pl.push_back(QPoint(length * 5 / 8, my * 3 / 2));
+ } else {
+ pl.push_back(QPoint(length / 2, my * 4 / 3));
+ }
+
+ pl.push_back(QPoint(length - length / 6, my));
+ pl.push_back(QPoint(length - abs(my) / 4, my / 3));
+ }
+
+ Spline::PointList *polyPoints = Spline::calculate
+ (QPoint(0, y0), QPoint(length - 1, y0), pl, topLeft, bottomRight);
+
+ if (!havePixmap) {
+ int width = bottomRight.x() - topLeft.x();
+ int height = bottomRight.y() - topLeft.y() + thickness - 1 + abs(dy);
+ hotspot = QPoint(0, -topLeft.y() + (dy < 0 ? -dy : 0));
+
+ // NOTATION_DEBUG << "slur: bottomRight (" << bottomRight.x() << "," << bottomRight.y() << "), topLeft (" << topLeft.x() << "," << topLeft.y() << "), width " << width << ", height " << height << ", hotspot (" << hotspot.x() << "," << hotspot.y() << "), dy " << dy << ", thickness " << thickness << endl;
+
+ if (painter) {
+
+ // This conditional is because we're also called with
+ // a painter arg from non-printer drawTie. It's a big
+ // hack.
+
+ if (m_inPrinterMethod) {
+ painter->save();
+ m_p->beginExternal(painter);
+ painter->translate(x, y);
+ if (rotate)
+ painter->rotate(theta);
+ } else {
+ m_p->painter().save();
+ m_p->maskPainter().save();
+ m_p->painter().translate(x, y);
+ m_p->maskPainter().translate(x, y);
+ if (rotate) {
+ m_p->painter().rotate(theta);
+ m_p->maskPainter().rotate(theta);
+ }
+ }
+
+ } else {
+ createPixmapAndMask(smooth ? width*2 + 1 : width,
+ smooth ? height*2 + thickness*2 : height + thickness,
+ width, height);
+
+ QWMatrix m;
+ if (smooth)
+ m.translate(2 * hotspot.x(), 2 * hotspot.y());
+ else
+ m.translate(hotspot.x(), hotspot.y());
+ m.rotate(theta);
+ m_p->painter().setWorldMatrix(m);
+ m_p->maskPainter().setWorldMatrix(m);
+ }
+
+ if (m_selected)
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ else if (m_shaded) {
+ m_p->painter().setPen(Qt::gray);
+ }
+ havePixmap = true;
+ }
+ /*
+ for (int j = 0; j < pl.size(); ++j) {
+ if (smooth) {
+ m_p->drawPoint(pl[j].x()*2, pl[j].y()*2);
+ } else {
+ m_p->drawPoint(pl[j].x(), pl[j].y());
+ }
+ }
+ */
+ int ppc = polyPoints->size();
+ QPointArray qp(ppc);
+
+ for (int j = 0; j < ppc; ++j) {
+ qp.setPoint(j, (*polyPoints)[j].x(), (*polyPoints)[j].y());
+ }
+
+ delete polyPoints;
+
+ if (!smooth || (i > 0 && i < thickness - 1)) {
+ if (smooth) {
+ for (int j = 0; j < ppc; ++j) {
+ qp.setPoint(j, qp.point(j).x()*2, qp.point(j).y()*2);
+ }
+ m_p->drawPolyline(qp);
+ for (int j = 0; j < ppc; ++j) {
+ qp.setPoint(j, qp.point(j).x(), qp.point(j).y() + 1);
+ }
+ m_p->drawPolyline(qp);
+ } else {
+ m_p->drawPolyline(qp);
+ }
+ }
+
+ if (above) {
+ ++my;
+ if (i % 2)
+ ++y0;
+ } else {
+ --my;
+ if (i % 2)
+ --y0;
+ }
+ }
+
+ if (m_selected) {
+ m_p->painter().setPen(Qt::black);
+ }
+
+ QWMatrix::setTransformationMode(mode);
+
+ if (painter) {
+ painter->restore();
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().restore();
+ }
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeOttavaPixmap(int length, int octavesUp)
+{
+ Profiler profiler("NotePixmapFactory::makeOttavaPixmap");
+ m_inPrinterMethod = false;
+ drawOttavaAux(length, octavesUp, 0, 0, 0);
+ return makeCanvasPixmap(QPoint(0, m_generatedHeight - 1));
+}
+
+void
+NotePixmapFactory::drawOttava(int length, int octavesUp,
+ QPainter &painter, int x, int y)
+{
+ Profiler profiler("NotePixmapFactory::drawOttava");
+ m_inPrinterMethod = true;
+ drawOttavaAux(length, octavesUp, &painter, x, y);
+ m_inPrinterMethod = false;
+}
+
+void
+NotePixmapFactory::drawOttavaAux(int length, int octavesUp,
+ QPainter *painter, int x, int y)
+{
+ int height = m_ottavaFontMetrics.height();
+ int backpedal = 0;
+ QString label;
+ QRect r;
+
+ if (octavesUp == 2 || octavesUp == -2) {
+ label = "15ma ";
+ backpedal = m_ottavaFontMetrics.width("15") / 2;
+ } else {
+ label = "8va ";
+ backpedal = m_ottavaFontMetrics.width("8") / 2;
+ }
+
+ int width = length + backpedal;
+
+ if (painter) {
+ painter->save();
+ m_p->beginExternal(painter);
+ painter->translate(x - backpedal, y - height);
+ } else {
+ NOTATION_DEBUG << "NotePixmapFactory::drawOttavaAux: making pixmap and mask " << width << "x" << height << endl;
+ createPixmapAndMask(width, height);
+ }
+
+ int thickness = getStemThickness();
+ QPen pen(Qt::black, thickness, Qt::DotLine);
+
+ if (m_selected) {
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ pen.setColor(GUIPalette::getColour(GUIPalette::SelectedElement));
+ } else if (m_shaded) {
+ m_p->painter().setPen(Qt::gray);
+ pen.setColor(Qt::gray);
+ }
+
+ m_p->painter().setFont(m_ottavaFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(m_ottavaFont);
+
+ m_p->drawText(0, m_ottavaFontMetrics.ascent(), label);
+
+ m_p->painter().setPen(pen);
+ // if (!m_inPrinterMethod) m_p->maskPainter().setPen(pen);
+
+ int x0 = m_ottavaFontMetrics.width(label) + thickness;
+ int x1 = width - thickness;
+ int y0 = m_ottavaFontMetrics.ascent() * 2 / 3 - thickness / 2;
+ int y1 = (octavesUp < 0 ? 0 : m_ottavaFontMetrics.ascent());
+
+ NOTATION_DEBUG << "NotePixmapFactory::drawOttavaAux: drawing " << x0 << "," << y0 << " to " << x1 << "," << y0 << ", thickness " << thickness << endl;
+
+ m_p->drawLine(x0, y0, x1, y0);
+
+ pen.setStyle(Qt::SolidLine);
+ m_p->painter().setPen(pen);
+ // if (!m_inPrinterMethod) m_p->maskPainter().setPen(pen);
+
+ NOTATION_DEBUG << "NotePixmapFactory::drawOttavaAux: drawing " << x1 << "," << y0 << " to " << x1 << "," << y1 << ", thickness " << thickness << endl;
+
+ m_p->drawLine(x1, y0, x1, y1);
+
+ m_p->painter().setPen(QPen());
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setPen(QPen());
+
+ if (painter) {
+ painter->restore();
+ }
+}
+
+void
+NotePixmapFactory::drawBracket(int length, bool left, bool curly, int x, int y)
+{
+ // curly mode not yet implemented
+
+ int thickness = getStemThickness() * 2;
+
+ int m1 = length / 6;
+ int m2 = length - length / 6 - 1;
+
+ int off0 = 0, moff = 0;
+
+ int nbh = getNoteBodyHeight(), nbw = getNoteBodyWidth();
+ float noteLengths = float(length) / nbw;
+ if (noteLengths < 1)
+ noteLengths = 1;
+ moff = int(nbh * sqrt(noteLengths) / 2);
+ moff = moff * 2 / 3;
+
+ if (left)
+ moff = -moff;
+
+ QPoint topLeft, bottomRight;
+
+ for (int i = 0; i < thickness; ++i) {
+
+ Spline::PointList pl;
+ pl.push_back(QPoint((int)moff, m1));
+ pl.push_back(QPoint((int)moff, m2));
+ /*
+ NOTATION_DEBUG << "bracket spline controls: " << moff << "," << m1
+ << ", " << moff << "," << m2 << "; end points "
+ << off0 << ",0, " << off0 << "," << length-1
+ << endl;
+ */
+ Spline::PointList *polyPoints = Spline::calculate
+ (QPoint(off0, 0), QPoint(off0, length - 1), pl, topLeft, bottomRight);
+
+ int ppc = polyPoints->size();
+ QPointArray qp(ppc);
+ /*
+ NOTATION_DEBUG << "bracket spline polypoints: " << endl;
+ for (int j = 0; j < ppc; ++j) {
+ NOTATION_DEBUG << (*polyPoints)[j].x() << "," << (*polyPoints)[j].y() << endl;
+ }
+ */
+
+ for (int j = 0; j < ppc; ++j) {
+ qp.setPoint(j, x + (*polyPoints)[j].x(), y + (*polyPoints)[j].y());
+ }
+
+ delete polyPoints;
+
+ m_p->drawPolyline(qp);
+
+ if (!left) {
+ ++moff;
+ if (i % 2)
+ ++off0;
+ } else {
+ --moff;
+ if (i % 2)
+ --off0;
+ }
+ }
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeTimeSigPixmap(const TimeSignature& sig)
+{
+ Profiler profiler("NotePixmapFactory::makeTimeSigPixmap");
+
+ if (sig.isCommon()) {
+
+ NoteCharacter character;
+
+ CharName charName;
+ if (sig.getNumerator() == 2) {
+ charName = NoteCharacterNames::CUT_TIME;
+ } else {
+ charName = NoteCharacterNames::COMMON_TIME;
+ }
+
+ if (getCharacter(charName, character, PlainColour, false)) {
+ createPixmapAndMask(character.getWidth(), character.getHeight());
+ m_p->drawNoteCharacter(0, 0, character);
+ return makeCanvasPixmap(QPoint(0, character.getHeight() / 2));
+ }
+
+ QString c("c");
+ QRect r = m_bigTimeSigFontMetrics.boundingRect(c);
+
+ int dy = getLineSpacing() / 4;
+ createPixmapAndMask(r.width(), r.height() + dy*2);
+
+ if (m_selected) {
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ } else if (m_shaded) {
+ m_p->painter().setPen(Qt::gray);
+ }
+
+ m_p->painter().setFont(m_bigTimeSigFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(m_bigTimeSigFont);
+
+ m_p->drawText(0, r.height() + dy, c);
+
+ if (sig.getNumerator() == 2) { // cut common
+
+ int x = r.width() * 3 / 5 - getStemThickness();
+
+ for (int i = 0; i < getStemThickness() * 2; ++i, ++x) {
+ m_p->drawLine(x, 0, x, r.height() + dy*2 - 1);
+ }
+ }
+
+ m_p->painter().setPen(Qt::black);
+ return makeCanvasPixmap(QPoint(0, r.height() / 2 + dy));
+
+ } else {
+
+ int numerator = sig.getNumerator(),
+ denominator = sig.getDenominator();
+
+ QString numS, denomS;
+
+ numS.setNum(numerator);
+ denomS.setNum(denominator);
+
+ NoteCharacter character;
+ if (getCharacter(m_style->getTimeSignatureDigitName(0), character,
+ PlainColour, false)) {
+
+ // if the 0 digit exists, we assume 1-9 also all exist
+ // and all have the same width
+
+ int numW = character.getWidth() * numS.length();
+ int denomW = character.getWidth() * denomS.length();
+
+ int width = std::max(numW, denomW);
+ int height = getLineSpacing() * 4 - getStaffLineThickness();
+
+ createPixmapAndMask(width, height);
+
+ for (unsigned int i = 0; i < numS.length(); ++i) {
+ int x = width - (width - numW) / 2 - (i + 1) * character.getWidth();
+ int y = height / 4 - (character.getHeight() / 2);
+ NoteCharacter charCharacter = getCharacter
+ (m_style->getTimeSignatureDigitName(numerator % 10),
+ PlainColour, false);
+ m_p->drawNoteCharacter(x, y, charCharacter);
+ numerator /= 10;
+ }
+
+ for (unsigned int i = 0; i < denomS.length(); ++i) {
+ int x = width - (width - denomW) / 2 - (i + 1) * character.getWidth();
+ int y = height - height / 4 - (character.getHeight() / 2);
+ NoteCharacter charCharacter = getCharacter
+ (m_style->getTimeSignatureDigitName(denominator % 10),
+ PlainColour, false);
+ m_p->drawNoteCharacter(x, y, charCharacter);
+ denominator /= 10;
+ }
+
+ return makeCanvasPixmap(QPoint(0, height / 2));
+ }
+
+ QRect numR = m_timeSigFontMetrics.boundingRect(numS);
+ QRect denomR = m_timeSigFontMetrics.boundingRect(denomS);
+ int width = std::max(numR.width(), denomR.width()) + 2;
+ int x;
+
+ createPixmapAndMask(width, denomR.height() * 2 + getNoteBodyHeight());
+
+ if (m_selected) {
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ } else if (m_shaded) {
+ m_p->painter().setPen(Qt::gray);
+ }
+
+ m_p->painter().setFont(m_timeSigFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(m_timeSigFont);
+
+ x = (width - numR.width()) / 2 - 1;
+ m_p->drawText(x, denomR.height(), numS);
+
+ x = (width - denomR.width()) / 2 - 1;
+ m_p->drawText(x, denomR.height() * 2 + (getNoteBodyHeight() / 2) - 1, denomS);
+
+ m_p->painter().setPen(Qt::black);
+
+ return makeCanvasPixmap(QPoint(0, denomR.height() +
+ (getNoteBodyHeight() / 4) - 1),
+ true);
+ }
+}
+
+int NotePixmapFactory::getTimeSigWidth(const TimeSignature &sig) const
+{
+ if (sig.isCommon()) {
+
+ QRect r(m_bigTimeSigFontMetrics.boundingRect("c"));
+ return r.width() + 2;
+
+ } else {
+
+ int numerator = sig.getNumerator(),
+ denominator = sig.getDenominator();
+
+ QString numS, denomS;
+
+ numS.setNum(numerator);
+ denomS.setNum(denominator);
+
+ QRect numR = m_timeSigFontMetrics.boundingRect(numS);
+ QRect denomR = m_timeSigFontMetrics.boundingRect(denomS);
+ int width = std::max(numR.width(), denomR.width()) + 2;
+
+ return width;
+ }
+}
+
+QFont
+NotePixmapFactory::getTextFont(const Text &text) const
+{
+ std::string type(text.getTextType());
+ TextFontCache::iterator i = m_textFontCache.find(type.c_str());
+ if (i != m_textFontCache.end())
+ return i->second;
+
+ /*
+ * Text types:
+ *
+ * UnspecifiedType: Nothing known, use small roman
+ * StaffName: Large roman, to left of start of staff
+ * ChordName: Not normally shown in score, use small roman
+ * KeyName: Not normally shown in score, use small roman
+ * Lyric: Small roman, below staff and dynamic texts
+ * Chord: Small bold roman, above staff
+ * Dynamic: Small italic, below staff
+ * Direction: Large roman, above staff (by barline?)
+ * LocalDirection: Small bold italic, below staff (by barline?)
+ * Tempo: Large bold roman, above staff
+ * LocalTempo: Small bold roman, above staff
+ * Annotation: Very small sans-serif, in a yellow box
+ * LilyPondDirective: Very small sans-serif, in a green box
+ */
+
+ int weight = QFont::Normal;
+ bool italic = false;
+ bool large = false;
+ bool tiny = false;
+ bool serif = true;
+
+ if (type == Text::Tempo ||
+ type == Text::LocalTempo ||
+ type == Text::LocalDirection ||
+ type == Text::Chord) {
+ weight = QFont::Bold;
+ }
+
+ if (type == Text::Dynamic ||
+ type == Text::LocalDirection) {
+ italic = true;
+ }
+
+ if (type == Text::StaffName ||
+ type == Text::Direction ||
+ type == Text::Tempo) {
+ large = true;
+ }
+
+ if (type == Text::Annotation ||
+ type == Text::LilyPondDirective) {
+ serif = false;
+ tiny = true;
+ }
+
+ KConfig* config = kapp->config();
+
+ QFont textFont;
+
+ if (serif) {
+ textFont = QFont(defaultSerifFontFamily);
+ textFont = config->readFontEntry("textfont", &textFont);
+ } else {
+ textFont = QFont(defaultSansSerifFontFamily);
+ textFont = config->readFontEntry("sansfont", &textFont);
+ }
+
+ textFont.setStyleStrategy(QFont::StyleStrategy(QFont::PreferDefault |
+ QFont::PreferMatch));
+
+ int size;
+ if (large)
+ size = (getLineSpacing() * 7) / 2;
+ else if (tiny)
+ size = (getLineSpacing() * 4) / 3;
+ else if (serif)
+ size = (getLineSpacing() * 2);
+ else
+ size = (getLineSpacing() * 3) / 2;
+
+ textFont.setPixelSize(size);
+ textFont.setStyleHint(serif ? QFont::Serif : QFont::SansSerif);
+ textFont.setWeight(weight);
+ textFont.setItalic(italic);
+
+ NOTATION_DEBUG << "NotePixmapFactory::getTextFont: requested size " << size
+ << " for type " << type << endl;
+
+ NOTATION_DEBUG << "NotePixmapFactory::getTextFont: returning font '"
+ << textFont.toString() << "' for type " << type.c_str()
+ << " text : " << text.getText().c_str() << endl;
+
+ m_textFontCache[type.c_str()] = textFont;
+ return textFont;
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeTextPixmap(const Text &text)
+{
+ Profiler profiler("NotePixmapFactory::makeTextPixmap");
+
+ std::string type(text.getTextType());
+
+ if (type == Text::Annotation ||
+ type == Text::LilyPondDirective) {
+ return makeAnnotationPixmap(text, (type == Text::LilyPondDirective));
+ }
+
+ drawTextAux(text, 0, 0, 0);
+ return makeCanvasPixmap(QPoint(2, 2), true);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeGuitarChordPixmap(const Guitar::Fingering &fingering,
+ int x,
+ int y)
+{
+ using namespace Guitar;
+ Profiler profiler("NotePixmapFactory::makeGuitarChordPixmap");
+
+ int guitarChordWidth = getLineSpacing() * 6;
+ int guitarChordHeight = getLineSpacing() * 6;
+
+ createPixmapAndMask(guitarChordWidth, guitarChordHeight);
+
+ if (m_selected) {
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ m_p->painter().setBrush(GUIPalette::getColour(GUIPalette::SelectedElement));
+ } else {
+ m_p->painter().setPen(Qt::black);
+ m_p->painter().setBrush(Qt::black);
+ }
+
+ Guitar::NoteSymbols ns(Guitar::Fingering::DEFAULT_NB_STRINGS, FingeringBox::DEFAULT_NB_DISPLAYED_FRETS);
+ Guitar::NoteSymbols::drawFingeringPixmap(fingering, ns, &(m_p->painter()));
+
+ return makeCanvasPixmap(QPoint (x, y), true);
+}
+
+void
+NotePixmapFactory::drawText(const Text &text,
+ QPainter &painter, int x, int y)
+{
+ Profiler profiler("NotePixmapFactory::drawText");
+
+ // NOTATION_DEBUG << "NotePixmapFactory::drawText() " << text.getText().c_str()
+ // << " - type : " << text.getTextType().c_str() << endl;
+
+ std::string type(text.getTextType());
+
+ if (type == Text::Annotation ||
+ type == Text::LilyPondDirective) {
+ QCanvasPixmap *map = makeAnnotationPixmap(text, (type == Text::LilyPondDirective));
+ painter.drawPixmap(x, y, *map);
+ return ;
+ }
+
+ m_inPrinterMethod = true;
+ drawTextAux(text, &painter, x, y);
+ m_inPrinterMethod = false;
+}
+
+void
+NotePixmapFactory::drawTextAux(const Text &text,
+ QPainter *painter, int x, int y)
+{
+ QString s(strtoqstr(text.getText()));
+ QFont textFont(getTextFont(text));
+ QFontMetrics textMetrics(textFont);
+
+ int offset = 2;
+ int width = textMetrics.width(s) + 2 * offset;
+ int height = textMetrics.height() + 2 * offset;
+
+ if (painter) {
+ painter->save();
+ m_p->beginExternal(painter);
+ painter->translate(x - offset, y - offset);
+ } else {
+ createPixmapAndMask(width, height);
+ }
+
+ if (m_selected)
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ else if (m_shaded)
+ m_p->painter().setPen(Qt::gray);
+
+ m_p->painter().setFont(textFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(textFont);
+
+ m_p->drawText(offset, textMetrics.ascent() + offset, s);
+
+ m_p->painter().setPen(Qt::black);
+
+ if (painter) {
+ painter->restore();
+ }
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeAnnotationPixmap(const Text &text)
+{
+ return makeAnnotationPixmap(text, false);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeAnnotationPixmap(const Text &text, const bool isLilyPondDirective)
+{
+ QString s(strtoqstr(text.getText()));
+
+ QFont textFont(getTextFont(text));
+ QFontMetrics textMetrics(textFont);
+
+ int annotationWidth = getLineSpacing() * 16;
+ int annotationHeight = getLineSpacing() * 6;
+
+ int topGap = getLineSpacing() / 4 + 1;
+ int bottomGap = getLineSpacing() / 3 + 1;
+ int sideGap = getLineSpacing() / 4 + 1;
+
+ QRect r = textMetrics.boundingRect
+ (0, 0, annotationWidth, annotationHeight, Qt::WordBreak, s);
+
+ int pixmapWidth = r.width() + sideGap * 2;
+ int pixmapHeight = r.height() + topGap + bottomGap;
+
+ createPixmapAndMask(pixmapWidth, pixmapHeight);
+
+ if (m_selected)
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ else if (m_shaded)
+ m_p->painter().setPen(Qt::gray);
+
+ m_p->painter().setFont(textFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(textFont);
+
+ if (isLilyPondDirective) {
+ m_p->painter().setBrush(GUIPalette::getColour(GUIPalette::TextLilyPondDirectiveBackground));
+ } else {
+ m_p->painter().setBrush(GUIPalette::getColour(GUIPalette::TextAnnotationBackground));
+ }
+
+ m_p->drawRect(0, 0, pixmapWidth, pixmapHeight);
+
+ m_p->painter().setBrush(Qt::black);
+ m_p->painter().drawText(QRect(sideGap, topGap,
+ annotationWidth + sideGap,
+ pixmapHeight - bottomGap),
+ Qt::WordBreak, s);
+
+ /* unnecessary following the rectangle draw
+ m_pm.drawText(QRect(sideGap, topGap,
+ annotationWidth + sideGap, annotationHeight + topGap),
+ Qt::WordBreak, s);
+ */
+
+ return makeCanvasPixmap(QPoint(0, 0));
+}
+
+void
+NotePixmapFactory::createPixmapAndMask(int width, int height,
+ int maskWidth, int maskHeight)
+{
+ if (maskWidth < 0)
+ maskWidth = width;
+ if (maskHeight < 0)
+ maskHeight = height;
+
+ m_generatedWidth = width;
+ m_generatedHeight = height;
+ m_generatedPixmap = new QPixmap(width, height);
+ m_generatedMask = new QBitmap(maskWidth, maskHeight);
+
+ static unsigned long total = 0;
+ total += width * height;
+// NOTATION_DEBUG << "createPixmapAndMask: " << width << "x" << height << " (" << (width*height) << " px, " << total << " total)" << endl;
+
+ // clear up pixmap and mask
+ m_generatedPixmap->fill();
+ m_generatedMask->fill(Qt::color0);
+
+ // initiate painting
+ m_p->begin(m_generatedPixmap, m_generatedMask);
+
+ m_p->painter().setPen(Qt::black);
+ m_p->painter().setBrush(Qt::black);
+ m_p->maskPainter().setPen(Qt::white);
+ m_p->maskPainter().setBrush(Qt::white);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeCanvasPixmap(QPoint hotspot, bool generateMask)
+{
+ m_p->end();
+
+ QCanvasPixmap* p = new QCanvasPixmap(*m_generatedPixmap, hotspot);
+
+ if (generateMask) {
+ p->setMask(PixmapFunctions::generateMask(*p));
+ } else {
+ p->setMask(*m_generatedMask);
+ }
+
+ delete m_generatedPixmap;
+ delete m_generatedMask;
+ return p;
+}
+
+NoteCharacter
+NotePixmapFactory::getCharacter(CharName name, ColourType type, bool inverted)
+{
+ NoteCharacter ch;
+ getCharacter(name, ch, type, inverted);
+ return ch;
+}
+
+bool
+NotePixmapFactory::getCharacter(CharName name, NoteCharacter &ch,
+ ColourType type, bool inverted)
+{
+ NoteFont::CharacterType charType =
+ m_inPrinterMethod ? NoteFont::Printer : NoteFont::Screen;
+
+ if (m_selected) {
+ return m_font->getCharacterColoured
+ (name,
+ GUIPalette::SelectedElementHue,
+ GUIPalette::SelectedElementMinValue,
+ ch, charType, inverted);
+ }
+
+ if (m_shaded) {
+ return m_font->getCharacterShaded(name, ch, charType, inverted);
+ }
+
+ switch (type) {
+
+ case PlainColour:
+ return m_font->getCharacter(name, ch, charType, inverted);
+
+ case QuantizedColour:
+ return m_font->getCharacterColoured
+ (name,
+ GUIPalette::QuantizedNoteHue,
+ GUIPalette::QuantizedNoteMinValue,
+ ch, charType, inverted);
+
+ case HighlightedColour:
+ return m_font->getCharacterColoured
+ (name,
+ GUIPalette::HighlightedElementHue,
+ GUIPalette::HighlightedElementMinValue,
+ ch, charType, inverted);
+
+ case TriggerColour:
+ return m_font->getCharacterColoured
+ (name,
+ GUIPalette::TriggerNoteHue,
+ GUIPalette::TriggerNoteMinValue,
+ ch, charType, inverted);
+
+ case OutRangeColour:
+ return m_font->getCharacterColoured
+ (name,
+ GUIPalette::OutRangeNoteHue,
+ GUIPalette::OutRangeNoteMinValue,
+ ch, charType, inverted);
+ }
+
+ return m_font->getCharacter(name, ch, charType, inverted);
+}
+
+QPoint
+NotePixmapFactory::m_pointZero;
+
+
+int NotePixmapFactory::getNoteBodyWidth(Note::Type type)
+const
+{
+ CharName charName(m_style->getNoteHeadCharName(type).first);
+ int hx, hy;
+ if (!m_font->getHotspot(charName, hx, hy))
+ hx = 0;
+ return m_font->getWidth(charName) - hx * 2;
+}
+
+int NotePixmapFactory::getNoteBodyHeight(Note::Type )
+const
+{
+ // this is by definition
+ return m_font->getSize();
+}
+
+int NotePixmapFactory::getLineSpacing() const
+{
+ return m_font->getSize() + getStaffLineThickness();
+}
+
+int NotePixmapFactory::getAccidentalWidth(const Accidental &a,
+ int shift, bool extraShift) const
+{
+ if (a == Accidentals::NoAccidental)
+ return 0;
+ int w = m_font->getWidth(m_style->getAccidentalCharName(a));
+ if (!shift)
+ return w;
+ else {
+ int sw = w;
+ if (extraShift) {
+ --shift;
+ w += getNoteBodyWidth() + getStemThickness();
+ }
+ w += shift *
+ (sw - m_font->getHotspot(m_style->getAccidentalCharName(a)).x());
+ }
+ return w;
+}
+
+int NotePixmapFactory::getAccidentalHeight(const Accidental &a) const
+{
+ return m_font->getHeight(m_style->getAccidentalCharName(a));
+}
+
+int NotePixmapFactory::getStemLength() const
+{
+ unsigned int l = 1;
+ (void)m_font->getStemLength(l);
+ return l;
+}
+
+int NotePixmapFactory::getStemThickness() const
+{
+ unsigned int i = 1;
+ (void)m_font->getStemThickness(i);
+ return i;
+}
+
+int NotePixmapFactory::getStaffLineThickness() const
+{
+ unsigned int i;
+ (void)m_font->getStaffLineThickness(i);
+ return i;
+}
+
+int NotePixmapFactory::getLegerLineThickness() const
+{
+ unsigned int i;
+ (void)m_font->getLegerLineThickness(i);
+ return i;
+}
+
+int NotePixmapFactory::getDotWidth() const
+{
+ return m_font->getWidth(NoteCharacterNames::DOT);
+}
+
+int NotePixmapFactory::getClefWidth(const Clef &clef) const
+{
+ return m_font->getWidth(m_style->getClefCharName(clef.getClefType()));
+}
+
+int NotePixmapFactory::getBarMargin() const
+{
+ return getNoteBodyWidth() * 2;
+}
+
+int NotePixmapFactory::getRestWidth(const Note &restType) const
+{
+ return m_font->getWidth(m_style->getRestCharName(restType.getNoteType(),
+ false)) // small inaccuracy!
+ + (restType.getDots() * getDotWidth());
+}
+
+int NotePixmapFactory::getKeyWidth(const Key &key,
+ Key previousKey) const
+{
+ std::vector<int> ah0 = previousKey.getAccidentalHeights(Clef());
+ std::vector<int> ah1 = key.getAccidentalHeights(Clef());
+
+ int cancelCount = 0;
+ if (key.isSharp() != previousKey.isSharp())
+ cancelCount = ah0.size();
+ else if (ah1.size() < ah0.size())
+ cancelCount = ah0.size() - ah1.size();
+
+ CharName keyCharName;
+ if (key.isSharp())
+ keyCharName = NoteCharacterNames::SHARP;
+ else
+ keyCharName = NoteCharacterNames::FLAT;
+
+ NoteCharacter keyCharacter;
+ NoteCharacter cancelCharacter;
+
+ keyCharacter = m_font->getCharacter(keyCharName);
+ if (cancelCount > 0) {
+ cancelCharacter = m_font->getCharacter(NoteCharacterNames::NATURAL);
+ }
+
+ //int x = 0;
+ //int lw = getLineSpacing();
+ int keyDelta = keyCharacter.getWidth() - keyCharacter.getHotspot().x();
+
+ int cancelDelta = 0;
+ int between = 0;
+ if (cancelCount > 0) {
+ cancelDelta = cancelCharacter.getWidth() + cancelCharacter.getWidth() / 3;
+ between = cancelCharacter.getWidth();
+ }
+
+ return (keyDelta * ah1.size() + cancelDelta * cancelCount + between +
+ keyCharacter.getWidth() / 4);
+}
+
+int NotePixmapFactory::getTextWidth(const Text &text) const
+{
+ QFontMetrics metrics(getTextFont(text));
+ return metrics.boundingRect(strtoqstr(text.getText())).width() + 4;
+}
+
+}
diff --git a/src/gui/editors/notation/NotePixmapFactory.h b/src/gui/editors/notation/NotePixmapFactory.h
new file mode 100644
index 0000000..14b4773
--- /dev/null
+++ b/src/gui/editors/notation/NotePixmapFactory.h
@@ -0,0 +1,358 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTEPIXMAPFACTORY_H_
+#define _RG_NOTEPIXMAPFACTORY_H_
+
+#include "base/NotationTypes.h"
+#include <map>
+#include "NoteCharacter.h"
+#include <string>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include "base/Event.h"
+#include "gui/editors/notation/NoteCharacterNames.h"
+
+
+class QPainter;
+class QCanvasPixmap;
+class QBitmap;
+
+
+namespace Rosegarden
+{
+
+namespace Guitar { class Fingering; }
+
+class TimeSignature;
+class Text;
+class NoteStyle;
+class NotePixmapParameters;
+class NoteFont;
+class NotePixmapPainter;
+class NotePixmapCache;
+class Clef;
+class TrackHeader;
+
+/**
+ * Generates QCanvasPixmaps for various notation items.
+ */
+
+class NotePixmapFactory
+{
+public:
+ NotePixmapFactory(std::string fontName = "", int size = -1);
+ NotePixmapFactory(const NotePixmapFactory &);
+ NotePixmapFactory &operator=(const NotePixmapFactory &);
+ ~NotePixmapFactory();
+
+ std::string getFontName() const;
+ int getSize() const;
+
+ void setSelected(bool selected) { m_selected = selected; }
+ bool isSelected() const { return m_selected; }
+
+ void setShaded(bool shaded) { m_shaded = shaded; }
+ bool isShaded() const { return m_shaded; }
+
+ void setNoteStyle(NoteStyle *style) { m_style = style; }
+ const NoteStyle *getNoteStyle() const { return m_style; }
+
+ // Display methods -- create canvas pixmaps:
+
+ QCanvasPixmap* makeNotePixmap(const NotePixmapParameters &parameters);
+ QCanvasPixmap* makeRestPixmap(const NotePixmapParameters &parameters);
+ QCanvasPixmap* makeClefPixmap(const Clef &clef);
+ QCanvasPixmap* makeKeyPixmap(const Key &key,
+ const Clef &clef,
+ Key previousKey =
+ Key::DefaultKey);
+ QCanvasPixmap* makeTimeSigPixmap(const TimeSignature& sig);
+ QCanvasPixmap* makeHairpinPixmap(int length, bool isCrescendo);
+ QCanvasPixmap* makeSlurPixmap(int length, int dy, bool above, bool phrasing);
+ QCanvasPixmap* makeOttavaPixmap(int length, int octavesUp);
+ QCanvasPixmap* makePedalDownPixmap();
+ QCanvasPixmap* makePedalUpPixmap();
+ QCanvasPixmap* makeUnknownPixmap();
+ QCanvasPixmap* makeTextPixmap(const Text &text);
+ QCanvasPixmap* makeGuitarChordPixmap(const Guitar::Fingering &fingering,
+ int x, int y);
+
+ QCanvasPixmap* makeNoteHaloPixmap(const NotePixmapParameters &parameters);
+
+ // Printing methods -- draw direct to a paint device:
+
+ void drawNote(const NotePixmapParameters &parameters,
+ QPainter &painter, int x, int y);
+ void drawRest(const NotePixmapParameters &parameters,
+ QPainter &painter, int x, int y);
+ void drawHairpin(int length, bool isCrescendo,
+ QPainter &painter, int x, int y);
+ void drawSlur(int length, int dy, bool above, bool phrasing,
+ QPainter &painter, int x, int y);
+ void drawOttava(int length, int octavesUp,
+ QPainter &painter, int x, int y);
+ void drawText(const Text &text,
+ QPainter &painter, int x, int y);
+
+ // Other support methods for producing pixmaps for other contexts:
+
+ static QCanvasPixmap *makeToolbarPixmap(const char *name,
+ bool menuSize = false);
+ static QCanvasPixmap *makeNoteMenuPixmap(timeT duration,
+ timeT &errorReturn);
+ static QCanvasPixmap *makeMarkMenuPixmap(Mark);
+
+ QCanvasPixmap* makePitchDisplayPixmap(int pitch,
+ const Clef &clef,
+ bool useSharps);
+ QCanvasPixmap* makePitchDisplayPixmap(int pitch,
+ const Clef &clef,
+ int octave,
+ int step);
+ QCanvasPixmap* makeClefDisplayPixmap(const Clef &clef);
+ QCanvasPixmap* makeKeyDisplayPixmap(const Key &key,
+ const Clef &clef);
+
+ QCanvasPixmap* makeTrackHeaderPixmap(int width, int height,
+ TrackHeader *header);
+
+ // Bounding box and other geometry methods:
+
+ int getNoteBodyWidth (Note::Type =
+ Note::Crotchet) const;
+
+ int getNoteBodyHeight(Note::Type =
+ Note::Crotchet) const;
+
+ int getAccidentalWidth (const Accidental &,
+ int shift = 0, bool extra = false) const;
+ int getAccidentalHeight(const Accidental &) const;
+
+ int getLineSpacing() const;
+ int getStemLength() const;
+ int getStemThickness() const;
+ int getStaffLineThickness() const;
+ int getLegerLineThickness() const;
+ int getDotWidth() const;
+ int getBarMargin() const;
+
+ int getClefWidth(const Clef &clef) const;
+ int getTimeSigWidth(const TimeSignature &timesig) const;
+ int getRestWidth(const Note &restType) const;
+ int getKeyWidth(const Key &key,
+ Key previousKey = Key::DefaultKey) const;
+ int getTextWidth(const Text &text) const;
+
+ /**
+ * Returns the width of clef and key signature drawn in a track header.
+ */
+ int getClefAndKeyWidth(const Key &key, const Clef &clef);
+
+ /**
+ * Returns the Number of Text Lines that can be written at top and bottom
+ * of a track header.
+ * The parameter is the track header height.
+ * Always returns a value >= 1.
+ */
+ int getTrackHeaderNTL(int height);
+
+ /**
+ * Returns the width of a text string written in a track header.
+ */
+ int getTrackHeaderTextWidth(QString str);
+
+ /**
+ * Returns the spacing of a text lines written in a track header.
+ */
+ int getTrackHeaderTextLineSpacing();
+
+ /**
+ * Returns from the beginning of "text" a string of horizontal size
+ * "width" (when written with m_trackHeaderFont) and removes it
+ * from "text".
+ */
+ QString getOneLine(QString &text, int width);
+
+
+ /**
+ * We need this function because as of Qt 3.1, QCanvasPixmap
+ * is no longer copyable by value, while QPixmap still is.
+ *
+ * So all the makeXXPixmap are now returning QCanvasPixmap*
+ * instead of QCanvasPixmap, but we need an easy way to
+ * convert them to QPixmap, since we use them that
+ * way quite often (to generate toolbar button icons for instance).
+ */
+ static QPixmap toQPixmap(QCanvasPixmap*);
+ static void dumpStats(std::ostream &);
+
+
+ static const char* const defaultSerifFontFamily;
+ static const char* const defaultSansSerifFontFamily;
+ static const char* const defaultTimeSigFontFamily;
+
+
+protected:
+ void init(std::string fontName, int size);
+ void initMaybe() { if (!m_font) init("", -1); }
+
+ void drawNoteAux(const NotePixmapParameters &parameters,
+ QPainter *painter, int x, int y);
+ void drawRestAux(const NotePixmapParameters &parameters, QPoint &hotspot,
+ QPainter *painter, int x, int y);
+ void drawHairpinAux(int length, bool isCrescendo,
+ QPainter *painter, int x, int y);
+ void drawSlurAux(int length, int dy, bool above, bool smooth, bool tie, bool phrasing,
+ QPoint &hotspot,
+ QPainter *painter, int x, int y);
+ void drawOttavaAux(int length, int octavesUp,
+ QPainter *painter, int x, int y);
+ void drawTextAux(const Text &text,
+ QPainter *painter, int x, int y);
+
+ int getStemLength(const NotePixmapParameters &) const;
+
+ void makeRoomForAccidental(Accidental, bool cautionary, int shift, bool extra);
+ void drawAccidental(Accidental, bool cautionary);
+
+ void makeRoomForMarks(bool isStemmed, const NotePixmapParameters &params, int stemLength);
+ void drawMarks(bool isStemmed, const NotePixmapParameters &params, int stemLength);
+
+ void makeRoomForLegerLines(const NotePixmapParameters &params);
+ void drawLegerLines(const NotePixmapParameters &params);
+
+ void makeRoomForStemAndFlags(int flagCount, int stemLength,
+ const NotePixmapParameters &params,
+ QPoint &startPoint, QPoint &endPoint);
+ void drawFlags(int flagCount, const NotePixmapParameters &params,
+ const QPoint &startPoint, const QPoint &endPoint);
+ void drawStem(const NotePixmapParameters &params,
+ const QPoint &startPoint, const QPoint &endPoint,
+ int shortening);
+
+ void makeRoomForBeams(const NotePixmapParameters &params);
+ void drawBeams(const QPoint &, const NotePixmapParameters &params,
+ int beamCount);
+
+ void drawSlashes(const QPoint &, const NotePixmapParameters &params,
+ int slashCount);
+
+ void makeRoomForTuplingLine(const NotePixmapParameters &params);
+ void drawTuplingLine(const NotePixmapParameters &params);
+
+ void drawShallowLine(int x0, int y0, int x1, int y1, int thickness,
+ bool smooth);
+ void drawTie(bool above, int length, int shift);
+
+ void drawBracket(int length, bool left, bool curly, int x, int y);
+
+ QFont getTextFont(const Text &text) const;
+
+ QCanvasPixmap* makeAnnotationPixmap(const Text &text);
+ QCanvasPixmap* makeAnnotationPixmap(const Text &text, const bool isLilyPondDirective);
+
+ void createPixmapAndMask(int width, int height,
+ int maskWidth = -1,
+ int maskHeight = -1);
+ QCanvasPixmap* makeCanvasPixmap(QPoint hotspot, bool generateMask = false);
+
+ enum ColourType {
+ PlainColour,
+ QuantizedColour,
+ HighlightedColour,
+ TriggerColour,
+ OutRangeColour
+ };
+
+ /// draws selected/shaded status from m_selected/m_shaded:
+ NoteCharacter getCharacter(CharName name, ColourType type, bool inverted);
+
+ /// draws selected/shaded status from m_selected/m_shaded:
+ bool getCharacter(CharName name, NoteCharacter &ch, ColourType type, bool inverted);
+
+ void drawNoteHalo(int x, int y, int w, int h);
+
+ //--------------- Data members ---------------------------------
+
+ NoteFont *m_font;
+ NoteStyle *m_style;
+ bool m_selected;
+ bool m_shaded;
+
+ int m_noteBodyWidth, m_noteBodyHeight;
+ int m_left, m_right, m_above, m_below;
+ int m_borderX, m_borderY;
+
+ QFont m_tupletCountFont;
+ QFontMetrics m_tupletCountFontMetrics;
+
+ QFont m_textMarkFont;
+ QFontMetrics m_textMarkFontMetrics;
+
+ QFont m_fingeringFont;
+ QFontMetrics m_fingeringFontMetrics;
+
+ QFont m_timeSigFont;
+ QFontMetrics m_timeSigFontMetrics;
+
+ QFont m_bigTimeSigFont;
+ QFontMetrics m_bigTimeSigFontMetrics;
+
+ QFont m_ottavaFont;
+ QFontMetrics m_ottavaFontMetrics;
+
+ QFont m_clefOttavaFont;
+ QFontMetrics m_clefOttavaFontMetrics;
+
+ QFont m_trackHeaderFont;
+ QFontMetrics m_trackHeaderFontMetrics;
+
+ QFont m_trackHeaderBoldFont;
+ QFontMetrics m_trackHeaderBoldFontMetrics;
+
+ QPixmap *m_generatedPixmap;
+ QBitmap *m_generatedMask;
+
+ int m_generatedWidth;
+ int m_generatedHeight;
+ bool m_inPrinterMethod;
+
+ NotePixmapPainter *m_p;
+
+ mutable NotePixmapCache *m_dottedRestCache;
+
+ typedef std::map<const char *, QFont> TextFontCache;
+ mutable TextFontCache m_textFontCache;
+
+ static QPoint m_pointZero;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotePixmapPainter.h b/src/gui/editors/notation/NotePixmapPainter.h
new file mode 100644
index 0000000..ed9d541
--- /dev/null
+++ b/src/gui/editors/notation/NotePixmapPainter.h
@@ -0,0 +1,148 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTEPIXMAPPAINTER_H_
+#define _RG_NOTEPIXMAPPAINTER_H_
+
+#include <qpainter.h>
+
+namespace Rosegarden {
+
+class NotePixmapPainter
+{
+ // Just a trivial class that instructs two painters to do the
+ // same thing (one for the pixmap, one for the mask). We only
+ // duplicate those methods we actually use in NotePixmapFactory
+
+public:
+ NotePixmapPainter() :
+ m_painter(&m_myPainter) { }
+
+ void beginExternal(QPainter *painter) {
+
+ m_externalPainter = painter;
+ m_useMask = false;
+
+ painter->setPen(QPen(Qt::black, 1, Qt::SolidLine,
+ Qt::RoundCap, Qt::RoundJoin));
+
+ if (m_externalPainter) {
+ m_painter = m_externalPainter;
+ } else {
+ m_painter = &m_myPainter;
+ }
+ }
+
+ bool begin(QPaintDevice *device, QPaintDevice *mask = 0, bool unclipped = false) {
+
+ m_externalPainter = 0;
+
+ if (mask) {
+ m_useMask = true;
+ m_maskPainter.begin(mask, unclipped);
+ } else {
+ m_useMask = false;
+ }
+
+ m_painter = &m_myPainter;
+ return m_painter->begin(device, unclipped);
+ }
+
+ bool end() {
+ if (m_useMask) m_maskPainter.end();
+ return m_painter->end();
+ }
+
+ QPainter &painter() {
+ return *m_painter;
+ }
+
+ QPainter &maskPainter() {
+ return m_maskPainter;
+ }
+
+ void drawPoint(int x, int y) {
+ m_painter->drawPoint(x, y);
+ if (m_useMask) m_maskPainter.drawPoint(x, y);
+ }
+
+ void drawLine(int x1, int y1, int x2, int y2) {
+ m_painter->drawLine(x1, y1, x2, y2);
+ if (m_useMask) m_maskPainter.drawLine(x1, y1, x2, y2);
+ }
+
+ void drawRect(int x, int y, int w, int h) {
+ m_painter->drawRect(x, y, w, h);
+ if (m_useMask) m_maskPainter.drawRect(x, y, w, h);
+ }
+
+ void drawArc(int x, int y, int w, int h, int a, int alen) {
+ m_painter->drawArc(x, y, w, h, a, alen);
+ if (m_useMask) m_maskPainter.drawArc(x, y, w, h, a, alen);
+ }
+
+ void drawPolygon(const QPointArray &a, bool winding = false,
+ int index = 0, int n = -1) {
+ m_painter->drawPolygon(a, winding, index, n);
+ if (m_useMask) m_maskPainter.drawPolygon(a, winding, index, n);
+ }
+
+ void drawPolyline(const QPointArray &a, int index = 0, int n = -1) {
+ m_painter->drawPolyline(a, index, n);
+ if (m_useMask) m_maskPainter.drawPolyline(a, index, n);
+ }
+
+ void drawPixmap(int x, int y, const QPixmap &pm,
+ int sx = 0, int sy = 0, int sw = -1, int sh = -1) {
+ m_painter->drawPixmap(x, y, pm, sx, sy, sw, sh);
+ if (m_useMask) m_maskPainter.drawPixmap(x, y, *(pm.mask()), sx, sy, sw, sh);
+ }
+
+ void drawText(int x, int y, const QString &string) {
+ m_painter->drawText(x, y, string);
+ if (m_useMask) m_maskPainter.drawText(x, y, string);
+ }
+
+ void drawNoteCharacter(int x, int y, const NoteCharacter &character) {
+ character.draw(m_painter, x, y);
+ if (m_useMask) character.drawMask(&m_maskPainter, x, y);
+ }
+
+ void drawEllipse(int x, int y, int w, int h) {
+ m_painter->drawEllipse(x, y, w, h);
+ if (m_useMask) m_maskPainter.drawEllipse(x, y, w, h);
+ }
+
+private:
+ bool m_useMask;
+ QPainter m_myPainter;
+ QPainter m_maskPainter;
+ QPainter *m_externalPainter;
+ QPainter *m_painter;
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotePixmapParameters.cpp b/src/gui/editors/notation/NotePixmapParameters.cpp
new file mode 100644
index 0000000..b6dd7fb
--- /dev/null
+++ b/src/gui/editors/notation/NotePixmapParameters.cpp
@@ -0,0 +1,151 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NotePixmapParameters.h"
+
+#include "base/NotationTypes.h"
+
+
+namespace Rosegarden
+{
+
+NotePixmapParameters::NotePixmapParameters(Note::Type noteType,
+ int dots,
+ Accidental accidental) :
+ m_noteType(noteType),
+ m_dots(dots),
+ m_accidental(accidental),
+ m_cautionary(false),
+ m_shifted(false),
+ m_dotShifted(false),
+ m_accidentalShift(0),
+ m_drawFlag(true),
+ m_drawStem(true),
+ m_stemGoesUp(true),
+ m_stemLength( -1),
+ m_legerLines(0),
+ m_slashes(0),
+ m_selected(false),
+ m_highlighted(false),
+ m_quantized(false),
+ m_trigger(false),
+ m_onLine(false),
+ m_safeVertDistance(0),
+ m_restOutsideStave(false),
+ m_beamed(false),
+ m_nextBeamCount(0),
+ m_thisPartialBeams(false),
+ m_nextPartialBeams(false),
+ m_width(1),
+ m_gradient(0.0),
+ m_tupletCount(0),
+ m_tuplingLineY(0),
+ m_tuplingLineWidth(0),
+ m_tuplingLineGradient(0.0),
+ m_tied(false),
+ m_tieLength(0),
+ m_tiePositionExplicit(false),
+ m_tieAbove(false),
+ m_inRange(true)
+{
+ // nothing else
+}
+
+NotePixmapParameters::~NotePixmapParameters()
+{
+ // nothing to see here
+}
+
+void
+NotePixmapParameters::setMarks(const std::vector<Mark> &marks)
+{
+ m_marks.clear();
+ for (unsigned int i = 0; i < marks.size(); ++i)
+ m_marks.push_back(marks[i]);
+}
+
+void
+NotePixmapParameters::removeMarks()
+{
+ m_marks.clear();
+}
+
+std::vector<Rosegarden::Mark>
+NotePixmapParameters::getNormalMarks() const
+{
+ std::vector<Mark> marks;
+
+ for (std::vector<Mark>::const_iterator mi = m_marks.begin();
+ mi != m_marks.end(); ++mi) {
+
+ if (*mi == Marks::Pause ||
+ *mi == Marks::UpBow ||
+ *mi == Marks::DownBow ||
+ *mi == Marks::Trill ||
+ *mi == Marks::LongTrill ||
+ *mi == Marks::TrillLine ||
+ *mi == Marks::Turn ||
+ Marks::isFingeringMark(*mi))
+ continue;
+
+ marks.push_back(*mi);
+ }
+
+ return marks;
+}
+
+std::vector<Rosegarden::Mark>
+NotePixmapParameters::getAboveMarks() const
+{
+ std::vector<Mark> marks;
+
+ // fingerings before other marks
+
+ for (std::vector<Mark>::const_iterator mi = m_marks.begin();
+ mi != m_marks.end(); ++mi) {
+
+ if (Marks::isFingeringMark(*mi)) {
+ marks.push_back(*mi);
+ }
+ }
+
+ for (std::vector<Mark>::const_iterator mi = m_marks.begin();
+ mi != m_marks.end(); ++mi) {
+
+ if (*mi == Marks::Pause ||
+ *mi == Marks::UpBow ||
+ *mi == Marks::DownBow ||
+ *mi == Marks::Trill ||
+ *mi == Marks::LongTrill ||
+ *mi == Marks::TrillLine ||
+ *mi == Marks::Turn) {
+ marks.push_back(*mi);
+ }
+ }
+
+ return marks;
+}
+
+}
diff --git a/src/gui/editors/notation/NotePixmapParameters.h b/src/gui/editors/notation/NotePixmapParameters.h
new file mode 100644
index 0000000..f7bfee7
--- /dev/null
+++ b/src/gui/editors/notation/NotePixmapParameters.h
@@ -0,0 +1,161 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTEPIXMAPPARAMETERS_H_
+#define _RG_NOTEPIXMAPPARAMETERS_H_
+
+#include "base/NotationTypes.h"
+#include <vector>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class NotePixmapParameters
+{
+public:
+ NotePixmapParameters(Note::Type noteType,
+ int dots,
+ Accidental accidental =
+ Accidentals::NoAccidental);
+ ~NotePixmapParameters();
+
+ void setNoteType(Note::Type type) { m_noteType = type; }
+ void setDots(int dots) { m_dots = dots; }
+ void setAccidental(Accidental acc) { m_accidental = acc; }
+
+ void setAccidentalCautionary(bool cautionary) { m_cautionary = cautionary; }
+ void setNoteHeadShifted(bool shifted) { m_shifted = shifted; }
+ void setNoteDotShifted(bool shifted) { m_dotShifted = shifted; }
+ void setAccidentalShift(int shift) { m_accidentalShift = shift; }
+ void setAccExtraShift(bool extra) { m_accidentalExtra = extra; }
+
+ void setDrawFlag(bool df) { m_drawFlag = df; }
+ void setDrawStem(bool ds) { m_drawStem = ds; }
+ void setStemGoesUp(bool up) { m_stemGoesUp = up; }
+ void setStemLength(int length) { m_stemLength = length; }
+ void setLegerLines(int lines) { m_legerLines = lines; }
+ void setSlashes(int slashes) { m_slashes = slashes; }
+ void setRestOutside(bool os) { m_restOutsideStave = os; }
+
+ void setSelected(bool selected) { m_selected = selected; }
+ void setHighlighted(bool highlighted) { m_highlighted = highlighted;}
+ void setQuantized(bool quantized) { m_quantized = quantized; }
+ void setTrigger(bool trigger) { m_trigger = trigger; }
+ void setIsOnLine(bool isOnLine) { m_onLine = isOnLine; }
+ void setSafeVertDistance(int safe) { m_safeVertDistance = safe; }
+
+ void setBeamed(bool beamed) { m_beamed = beamed; }
+ void setNextBeamCount(int tc) { m_nextBeamCount = tc; }
+ void setThisPartialBeams(bool pt) { m_thisPartialBeams = pt; }
+ void setNextPartialBeams(bool pt) { m_nextPartialBeams = pt; }
+ void setWidth(int width) { m_width = width; }
+ void setGradient(double gradient) { m_gradient = gradient; }
+
+ void setTupletCount(int count) { m_tupletCount = count; }
+ void setTuplingLineY(int y) { m_tuplingLineY = y; }
+ void setTuplingLineWidth(int width) { m_tuplingLineWidth = width; }
+ void setTuplingLineGradient(double g) { m_tuplingLineGradient = g; }
+ void setTuplingLineFollowsBeam(bool b){ m_tuplingLineFollowsBeam = b; }
+
+ void setTied(bool tied) { m_tied = tied; }
+ void setTieLength(int tieLength) { m_tieLength = tieLength; }
+
+ void setTiePosition(bool expl, bool above) {
+ m_tiePositionExplicit = expl;
+ m_tieAbove = above;
+ }
+
+ void setMarks(const std::vector<Mark> &marks);
+ void removeMarks();
+
+ void setInRange(bool inRange) { m_inRange = inRange; }
+
+ std::vector<Mark> getNormalMarks() const;
+ std::vector<Mark> getAboveMarks() const; // bowings, pause etc
+
+
+private:
+ friend class NotePixmapFactory;
+
+ //--------------- Data members ---------------------------------
+
+ Note::Type m_noteType;
+ int m_dots;
+ Accidental m_accidental;
+
+ bool m_cautionary;
+ bool m_shifted;
+ bool m_dotShifted;
+ int m_accidentalShift;
+ bool m_accidentalExtra;
+ bool m_drawFlag;
+ bool m_drawStem;
+ bool m_stemGoesUp;
+ int m_stemLength;
+ int m_legerLines;
+ int m_slashes;
+ bool m_selected;
+ bool m_highlighted;
+ bool m_quantized;
+ bool m_trigger;
+ bool m_onLine;
+ int m_safeVertDistance;
+ bool m_restOutsideStave;
+
+ bool m_beamed;
+ int m_nextBeamCount;
+ bool m_thisPartialBeams;
+ bool m_nextPartialBeams;
+ int m_width;
+ double m_gradient;
+
+ int m_tupletCount;
+ int m_tuplingLineY;
+ int m_tuplingLineWidth;
+ double m_tuplingLineGradient;
+ bool m_tuplingLineFollowsBeam;
+
+ bool m_tied;
+ int m_tieLength;
+ bool m_tiePositionExplicit;
+ bool m_tieAbove;
+
+ bool m_inRange;
+
+ std::vector<Mark> m_marks;
+};
+
+
+class NotePixmapPainter;
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteStyle.cpp b/src/gui/editors/notation/NoteStyle.cpp
new file mode 100644
index 0000000..0b3332d
--- /dev/null
+++ b/src/gui/editors/notation/NoteStyle.cpp
@@ -0,0 +1,485 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NoteStyle.h"
+
+#include "base/NotationTypes.h"
+#include "base/PropertyName.h"
+#include "NoteCharacterNames.h"
+#include "NoteStyleFactory.h"
+
+
+namespace Rosegarden
+{
+
+NoteStyle::~NoteStyle()
+{
+ // nothing
+}
+
+const NoteStyle::NoteHeadShape NoteStyle::AngledOval = "angled oval";
+
+const NoteStyle::NoteHeadShape NoteStyle::LevelOval = "level oval";
+
+const NoteStyle::NoteHeadShape NoteStyle::Breve = "breve";
+
+const NoteStyle::NoteHeadShape NoteStyle::Cross = "cross";
+
+const NoteStyle::NoteHeadShape NoteStyle::TriangleUp = "triangle up";
+
+const NoteStyle::NoteHeadShape NoteStyle::TriangleDown = "triangle down";
+
+const NoteStyle::NoteHeadShape NoteStyle::Diamond = "diamond";
+
+const NoteStyle::NoteHeadShape NoteStyle::Rectangle = "rectangle";
+
+const NoteStyle::NoteHeadShape NoteStyle::Number = "number";
+
+const NoteStyle::NoteHeadShape NoteStyle::CustomCharName = "custom character";
+
+
+
+NoteStyle::NoteHeadShape
+
+NoteStyle::getShape(Note::Type type)
+{
+ NoteDescriptionMap::iterator i = m_notes.find(type);
+ if (i == m_notes.end()) {
+ if (m_baseStyle)
+ return m_baseStyle->getShape(type);
+ std::cerr
+ << "WARNING: NoteStyle::getShape: No shape defined for note type "
+ << type << ", defaulting to AngledOval" << std::endl;
+ return AngledOval;
+ }
+
+ return i->second.shape;
+}
+
+bool
+NoteStyle::isFilled(Note::Type type)
+{
+ NoteDescriptionMap::iterator i = m_notes.find(type);
+ if (i == m_notes.end()) {
+ if (m_baseStyle)
+ return m_baseStyle->isFilled(type);
+ std::cerr
+ << "WARNING: NoteStyle::isFilled: No definition for note type "
+ << type << ", defaulting to true" << std::endl;
+ return true;
+ }
+
+ return i->second.filled;
+}
+
+bool
+NoteStyle::hasStem(Note::Type type)
+{
+ NoteDescriptionMap::iterator i = m_notes.find(type);
+ if (i == m_notes.end()) {
+ if (m_baseStyle)
+ return m_baseStyle->hasStem(type);
+ std::cerr
+ << "WARNING: NoteStyle::hasStem: No definition for note type "
+ << type << ", defaulting to true" << std::endl;
+ return true;
+ }
+
+ return i->second.stem;
+}
+
+int
+NoteStyle::getFlagCount(Note::Type type)
+{
+ NoteDescriptionMap::iterator i = m_notes.find(type);
+ if (i == m_notes.end()) {
+ if (m_baseStyle)
+ return m_baseStyle->getFlagCount(type);
+ std::cerr
+ << "WARNING: NoteStyle::getFlagCount: No definition for note type "
+ << type << ", defaulting to 0" << std::endl;
+ return 0;
+ }
+
+ return i->second.flags;
+}
+
+int
+NoteStyle::getSlashCount(Note::Type type)
+{
+ NoteDescriptionMap::iterator i = m_notes.find(type);
+ if (i == m_notes.end()) {
+ if (m_baseStyle)
+ return m_baseStyle->getSlashCount(type);
+ std::cerr
+ << "WARNING: NoteStyle::getSlashCount: No definition for note type "
+ << type << ", defaulting to 0" << std::endl;
+ return 0;
+ }
+
+ return i->second.slashes;
+}
+
+void
+NoteStyle::getStemFixPoints(Note::Type type,
+ HFixPoint &hfix, VFixPoint &vfix)
+{
+ NoteDescriptionMap::iterator i = m_notes.find(type);
+ if (i == m_notes.end()) {
+ if (m_baseStyle) {
+ m_baseStyle->getStemFixPoints(type, hfix, vfix);
+ return ;
+ }
+ std::cerr
+ << "WARNING: NoteStyle::getStemFixPoints: "
+ << "No definition for note type " << type
+ << ", defaulting to (Normal,Middle)" << std::endl;
+ hfix = Normal;
+ vfix = Middle;
+ return ;
+ }
+
+ hfix = i->second.hfix;
+ vfix = i->second.vfix;
+}
+
+NoteStyle::CharNameRec
+
+NoteStyle::getNoteHeadCharName(Note::Type type)
+{
+ NoteDescriptionMap::iterator i = m_notes.find(type);
+ if (i == m_notes.end()) {
+ if (m_baseStyle)
+ return m_baseStyle->getNoteHeadCharName(type);
+ std::cerr
+ << "WARNING: NoteStyle::getNoteHeadCharName: No definition for note type "
+ << type << ", defaulting to NOTEHEAD_BLACK" << std::endl;
+ return CharNameRec(NoteCharacterNames::NOTEHEAD_BLACK, false);
+ }
+
+ const NoteDescription &desc(i->second);
+ CharName name = NoteCharacterNames::UNKNOWN;
+ bool inverted = false;
+
+ if (desc.shape == AngledOval) {
+
+ name = desc.filled ? NoteCharacterNames::NOTEHEAD_BLACK
+ : NoteCharacterNames::VOID_NOTEHEAD;
+
+ } else if (desc.shape == LevelOval) {
+
+ if (desc.filled) {
+ std::cerr << "WARNING: NoteStyle::getNoteHeadCharName: No filled level oval head" << std::endl;
+ }
+ name = NoteCharacterNames::WHOLE_NOTE;
+
+ } else if (desc.shape == Breve) {
+
+ if (desc.filled) {
+ std::cerr << "WARNING: NoteStyle::getNoteHeadCharName: No filled breve head" << std::endl;
+ }
+ name = NoteCharacterNames::BREVE;
+
+ } else if (desc.shape == Cross) {
+
+ name = desc.filled ? NoteCharacterNames::X_NOTEHEAD
+ : NoteCharacterNames::CIRCLE_X_NOTEHEAD;
+
+ } else if (desc.shape == TriangleUp) {
+
+ name = desc.filled ? NoteCharacterNames::TRIANGLE_NOTEHEAD_UP_BLACK
+ : NoteCharacterNames::TRIANGLE_NOTEHEAD_UP_WHITE;
+
+ } else if (desc.shape == TriangleDown) {
+
+ name = desc.filled ? NoteCharacterNames::TRIANGLE_NOTEHEAD_UP_BLACK
+ : NoteCharacterNames::TRIANGLE_NOTEHEAD_UP_WHITE;
+ inverted = true;
+
+ } else if (desc.shape == Diamond) {
+
+ name = desc.filled ? NoteCharacterNames::SEMIBREVIS_BLACK
+ : NoteCharacterNames::SEMIBREVIS_WHITE;
+
+ } else if (desc.shape == Rectangle) {
+
+ name = desc.filled ? NoteCharacterNames::SQUARE_NOTEHEAD_BLACK
+ : NoteCharacterNames::SQUARE_NOTEHEAD_WHITE;
+
+ } else if (desc.shape == Number) {
+
+ std::cerr << "WARNING: NoteStyle::getNoteHeadCharName: Number not yet implemented" << std::endl;
+ name = NoteCharacterNames::UNKNOWN; //!!!
+
+ } else if (desc.shape == CustomCharName) {
+
+ name = desc.charName;
+
+ } else {
+
+ name = NoteCharacterNames::UNKNOWN;
+ }
+
+ return CharNameRec(name, inverted);
+}
+
+CharName
+NoteStyle::getAccidentalCharName(const Accidental &a)
+{
+ if (a == Accidentals::Sharp)
+ return NoteCharacterNames::SHARP;
+ else if (a == Accidentals::Flat)
+ return NoteCharacterNames::FLAT;
+ else if (a == Accidentals::Natural)
+ return NoteCharacterNames::NATURAL;
+ else if (a == Accidentals::DoubleSharp)
+ return NoteCharacterNames::DOUBLE_SHARP;
+ else if (a == Accidentals::DoubleFlat)
+ return NoteCharacterNames::DOUBLE_FLAT;
+ return NoteCharacterNames::UNKNOWN;
+}
+
+CharName
+NoteStyle::getMarkCharName(const Mark &mark)
+{
+ if (mark == Marks::Accent)
+ return NoteCharacterNames::ACCENT;
+ else if (mark == Marks::Tenuto)
+ return NoteCharacterNames::TENUTO;
+ else if (mark == Marks::Staccato)
+ return NoteCharacterNames::STACCATO;
+ else if (mark == Marks::Staccatissimo)
+ return NoteCharacterNames::STACCATISSIMO;
+ else if (mark == Marks::Marcato)
+ return NoteCharacterNames::MARCATO;
+ else if (mark == Marks::Trill)
+ return NoteCharacterNames::TRILL;
+ else if (mark == Marks::LongTrill)
+ return NoteCharacterNames::TRILL;
+ else if (mark == Marks::TrillLine)
+ return NoteCharacterNames::TRILL_LINE;
+ else if (mark == Marks::Turn)
+ return NoteCharacterNames::TURN;
+ else if (mark == Marks::Pause)
+ return NoteCharacterNames::FERMATA;
+ else if (mark == Marks::UpBow)
+ return NoteCharacterNames::UP_BOW;
+ else if (mark == Marks::DownBow)
+ return NoteCharacterNames::DOWN_BOW;
+ else if (mark == Marks::Mordent)
+ return NoteCharacterNames::MORDENT;
+ else if (mark == Marks::MordentInverted)
+ return NoteCharacterNames::MORDENT_INVERTED;
+ else if (mark == Marks::MordentLong)
+ return NoteCharacterNames::MORDENT_LONG;
+ else if (mark == Marks::MordentLongInverted)
+ return NoteCharacterNames::MORDENT_LONG_INVERTED;
+ // Things like "sf" and "rf" are generated from text fonts
+ return NoteCharacterNames::UNKNOWN;
+}
+
+CharName
+NoteStyle::getClefCharName(const Clef &clef)
+{
+ std::string clefType(clef.getClefType());
+
+ if (clefType == Clef::Bass || clefType == Clef::Varbaritone || clefType == Clef::Subbass) {
+ return NoteCharacterNames::F_CLEF;
+ } else if (clefType == Clef::Treble || clefType == Clef::French) {
+ return NoteCharacterNames::G_CLEF;
+ } else {
+ return NoteCharacterNames::C_CLEF;
+ }
+}
+
+CharName
+NoteStyle::getRestCharName(Note::Type type, bool restOutsideStave)
+{
+ switch (type) {
+ case Note::Hemidemisemiquaver:
+ return NoteCharacterNames::SIXTY_FOURTH_REST;
+ case Note::Demisemiquaver:
+ return NoteCharacterNames::THIRTY_SECOND_REST;
+ case Note::Semiquaver:
+ return NoteCharacterNames::SIXTEENTH_REST;
+ case Note::Quaver:
+ return NoteCharacterNames::EIGHTH_REST;
+ case Note::Crotchet:
+ return NoteCharacterNames::QUARTER_REST;
+ case Note::Minim:
+ return restOutsideStave ?
+ NoteCharacterNames::HALF_REST
+ : NoteCharacterNames::HALF_REST_ON_STAFF;
+ case Note::Semibreve:
+ return restOutsideStave ?
+ NoteCharacterNames::WHOLE_REST
+ : NoteCharacterNames::WHOLE_REST_ON_STAFF;
+ case Note::Breve:
+ return restOutsideStave ?
+ NoteCharacterNames::MULTI_REST
+ : NoteCharacterNames::MULTI_REST_ON_STAFF;
+ default:
+ return NoteCharacterNames::UNKNOWN;
+ }
+}
+
+CharName
+NoteStyle::getPartialFlagCharName(bool final)
+{
+ if (final)
+ return NoteCharacterNames::FLAG_PARTIAL_FINAL;
+ else
+ return NoteCharacterNames::FLAG_PARTIAL;
+}
+
+CharName
+NoteStyle::getFlagCharName(int flagCount)
+{
+ switch (flagCount) {
+ case 1:
+ return NoteCharacterNames::FLAG_1;
+ case 2:
+ return NoteCharacterNames::FLAG_2;
+ case 3:
+ return NoteCharacterNames::FLAG_3;
+ case 4:
+ return NoteCharacterNames::FLAG_4;
+ default:
+ return NoteCharacterNames::UNKNOWN;
+ }
+}
+
+CharName
+NoteStyle::getTimeSignatureDigitName(int digit)
+{
+ switch (digit) {
+ case 0:
+ return NoteCharacterNames::DIGIT_ZERO;
+ case 1:
+ return NoteCharacterNames::DIGIT_ONE;
+ case 2:
+ return NoteCharacterNames::DIGIT_TWO;
+ case 3:
+ return NoteCharacterNames::DIGIT_THREE;
+ case 4:
+ return NoteCharacterNames::DIGIT_FOUR;
+ case 5:
+ return NoteCharacterNames::DIGIT_FIVE;
+ case 6:
+ return NoteCharacterNames::DIGIT_SIX;
+ case 7:
+ return NoteCharacterNames::DIGIT_SEVEN;
+ case 8:
+ return NoteCharacterNames::DIGIT_EIGHT;
+ case 9:
+ return NoteCharacterNames::DIGIT_NINE;
+ default:
+ return NoteCharacterNames::UNKNOWN;
+ }
+}
+
+void
+NoteStyle::setBaseStyle(NoteStyleName name)
+{
+ try {
+ m_baseStyle = NoteStyleFactory::getStyle(name);
+ if (m_baseStyle == this)
+ m_baseStyle = 0;
+ } catch (NoteStyleFactory::StyleUnavailable u) {
+ if (name != NoteStyleFactory::DefaultStyle) {
+ std::cerr
+ << "NoteStyle::setBaseStyle: Base style "
+ << name << " not available, defaulting to "
+ << NoteStyleFactory::DefaultStyle << std::endl;
+ setBaseStyle(NoteStyleFactory::DefaultStyle);
+ } else {
+ std::cerr
+ << "NoteStyle::setBaseStyle: Base style "
+ << name << " not available" << std::endl;
+ m_baseStyle = 0;
+ }
+ }
+}
+
+void
+NoteStyle::checkDescription(Note::Type note)
+{
+ if (m_baseStyle && (m_notes.find(note) == m_notes.end())) {
+ m_baseStyle->checkDescription(note);
+ m_notes[note] = m_baseStyle->m_notes[note];
+ }
+}
+
+void
+NoteStyle::setShape(Note::Type note, NoteHeadShape shape)
+{
+ checkDescription(note);
+ m_notes[note].shape = shape;
+}
+
+void
+NoteStyle::setCharName(Note::Type note, CharName charName)
+{
+ checkDescription(note);
+ m_notes[note].charName = charName;
+}
+
+void
+NoteStyle::setFilled(Note::Type note, bool filled)
+{
+ checkDescription(note);
+ m_notes[note].filled = filled;
+}
+
+void
+NoteStyle::setStem(Note::Type note, bool stem)
+{
+ checkDescription(note);
+ m_notes[note].stem = stem;
+}
+
+void
+NoteStyle::setFlagCount(Note::Type note, int flags)
+{
+ checkDescription(note);
+ m_notes[note].flags = flags;
+}
+
+void
+NoteStyle::setSlashCount(Note::Type note, int slashes)
+{
+ checkDescription(note);
+ m_notes[note].slashes = slashes;
+}
+
+void
+NoteStyle::setStemFixPoints(Note::Type note, HFixPoint hfix, VFixPoint vfix)
+{
+ checkDescription(note);
+ m_notes[note].hfix = hfix;
+ m_notes[note].vfix = vfix;
+}
+
+}
diff --git a/src/gui/editors/notation/NoteStyle.h b/src/gui/editors/notation/NoteStyle.h
new file mode 100644
index 0000000..3959e01
--- /dev/null
+++ b/src/gui/editors/notation/NoteStyle.h
@@ -0,0 +1,142 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTESTYLE_H_
+#define _RG_NOTESTYLE_H_
+
+#include "base/NotationTypes.h"
+#include <map>
+#include "NoteCharacterNames.h"
+#include <string>
+#include <utility>
+#include "gui/editors/notation/NoteCharacterNames.h"
+
+
+class Mark;
+class Accidental;
+
+
+namespace Rosegarden
+{
+
+class Clef;
+
+typedef std::string NoteStyleName;
+
+
+class NoteStyle
+{
+public:
+ virtual ~NoteStyle();
+
+ typedef std::string NoteHeadShape;
+
+ static const NoteHeadShape AngledOval;
+ static const NoteHeadShape LevelOval;
+ static const NoteHeadShape Breve;
+ static const NoteHeadShape Cross;
+ static const NoteHeadShape TriangleUp;
+ static const NoteHeadShape TriangleDown;
+ static const NoteHeadShape Diamond;
+ static const NoteHeadShape Rectangle;
+ static const NoteHeadShape CustomCharName;
+ static const NoteHeadShape Number;
+
+ enum HFixPoint { Normal, Central, Reversed };
+ enum VFixPoint { Near, Middle, Far };
+
+ NoteStyleName getName() const { return m_name; }
+
+ NoteHeadShape getShape (Note::Type);
+ bool isFilled (Note::Type);
+ bool hasStem (Note::Type);
+ int getFlagCount (Note::Type);
+ int getSlashCount(Note::Type);
+
+ typedef std::pair<CharName, bool> CharNameRec; // bool is "inverted"
+ CharNameRec getNoteHeadCharName(Note::Type);
+
+ CharName getRestCharName(Note::Type, bool restOutsideStave);
+ CharName getPartialFlagCharName(bool final);
+ CharName getFlagCharName(int flagCount);
+ CharName getAccidentalCharName(const Accidental &);
+ CharName getMarkCharName(const Mark &);
+ CharName getClefCharName(const Clef &);
+ CharName getTimeSignatureDigitName(int digit);
+
+ void setBaseStyle (NoteStyleName name);
+ void setShape (Note::Type, NoteHeadShape);
+ void setCharName (Note::Type, CharName);
+ void setFilled (Note::Type, bool);
+ void setStem (Note::Type, bool);
+ void setFlagCount (Note::Type, int);
+ void setSlashCount(Note::Type, int);
+
+ void getStemFixPoints(Note::Type, HFixPoint &, VFixPoint &);
+ void setStemFixPoints(Note::Type, HFixPoint, VFixPoint);
+
+protected:
+ struct NoteDescription {
+ NoteHeadShape shape; // if CustomCharName, use charName
+ CharName charName; // only used if shape == CustomCharName
+ bool filled;
+ bool stem;
+ int flags;
+ int slashes;
+ HFixPoint hfix;
+ VFixPoint vfix;
+
+ NoteDescription() :
+ shape(AngledOval), charName(NoteCharacterNames::UNKNOWN),
+ filled(true), stem(true), flags(0), slashes(0),
+ hfix(Normal), vfix(Middle) { }
+
+ NoteDescription(NoteHeadShape _shape, CharName _charName,
+ bool _filled, bool _stem, int _flags, int _slashes,
+ HFixPoint _hfix, VFixPoint _vfix) :
+ shape(_shape), charName(_charName),
+ filled(_filled), stem(_stem), flags(_flags), slashes(_slashes),
+ hfix(_hfix), vfix(_vfix) { }
+ };
+
+ typedef std::map<Note::Type, NoteDescription> NoteDescriptionMap;
+
+ NoteDescriptionMap m_notes;
+ NoteStyle *m_baseStyle;
+ NoteStyleName m_name;
+
+ void checkDescription(Note::Type type);
+
+protected: // for use by NoteStyleFileReader
+ NoteStyle(NoteStyleName name) : m_baseStyle(0), m_name(name) { }
+ friend class NoteStyleFileReader;
+};
+
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteStyleFactory.cpp b/src/gui/editors/notation/NoteStyleFactory.cpp
new file mode 100644
index 0000000..d4a8be8
--- /dev/null
+++ b/src/gui/editors/notation/NoteStyleFactory.cpp
@@ -0,0 +1,124 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NoteStyleFactory.h"
+
+#include <kstddirs.h>
+#include "misc/Strings.h"
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "NotationProperties.h"
+#include "NoteStyle.h"
+#include "NoteStyleFileReader.h"
+#include <kglobal.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+
+namespace Rosegarden
+{
+
+const NoteStyleName NoteStyleFactory::DefaultStyle = "Classical";
+
+std::vector<NoteStyleName>
+NoteStyleFactory::getAvailableStyleNames()
+{
+ std::vector<NoteStyleName> names;
+
+ QString styleDir = KGlobal::dirs()->findResource("appdata", "styles/");
+ QDir dir(styleDir);
+ if (!dir.exists()) {
+ std::cerr << "NoteStyle::getAvailableStyleNames: directory \"" << styleDir
+ << "\" not found" << std::endl;
+ return names;
+ }
+
+ dir.setFilter(QDir::Files | QDir::Readable);
+ QStringList files = dir.entryList();
+ bool foundDefault = false;
+
+ for (QStringList::Iterator i = files.begin(); i != files.end(); ++i) {
+ if ((*i).length() > 4 && (*i).right(4) == ".xml") {
+ QFileInfo fileInfo(QString("%1/%2").arg(styleDir).arg(*i));
+ if (fileInfo.exists() && fileInfo.isReadable()) {
+ std::string styleName = qstrtostr((*i).left((*i).length() - 4));
+ if (styleName == DefaultStyle)
+ foundDefault = true;
+ names.push_back(styleName);
+ }
+ }
+ }
+
+ if (!foundDefault) {
+ std::cerr << "NoteStyleFactory::getAvailableStyleNames: WARNING: Default style name \"" << DefaultStyle << "\" not found" << std::endl;
+ }
+
+ return names;
+}
+
+NoteStyle *
+NoteStyleFactory::getStyle(NoteStyleName name)
+{
+ StyleMap::iterator i = m_styles.find(name);
+
+ if (i == m_styles.end()) {
+
+ try {
+ NoteStyle *newStyle = NoteStyleFileReader(name).getStyle();
+ m_styles[name] = newStyle;
+ return newStyle;
+
+ } catch (NoteStyleFileReader::StyleFileReadFailed f) {
+ std::cerr
+ << "NoteStyleFactory::getStyle: Style file read failed: "
+ << f.getMessage() << std::endl;
+ throw StyleUnavailable("Style file read failed: " +
+ f.getMessage());
+ }
+
+ } else {
+ return i->second;
+ }
+}
+
+NoteStyle *
+NoteStyleFactory::getStyleForEvent(Event *event)
+{
+ NoteStyleName styleName;
+ if (event->get
+ <String>(NotationProperties::NOTE_STYLE, styleName)) {
+ return getStyle(styleName);
+ }
+ else {
+ return getStyle(DefaultStyle);
+ }
+}
+
+NoteStyleFactory::StyleMap NoteStyleFactory::m_styles;
+
+
+}
diff --git a/src/gui/editors/notation/NoteStyleFactory.h b/src/gui/editors/notation/NoteStyleFactory.h
new file mode 100644
index 0000000..795537d
--- /dev/null
+++ b/src/gui/editors/notation/NoteStyleFactory.h
@@ -0,0 +1,61 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTESTYLEFACTORY_H_
+#define _RG_NOTESTYLEFACTORY_H_
+
+#include "base/Exception.h"
+#include <map>
+#include <vector>
+#include "NoteStyle.h"
+
+
+namespace Rosegarden
+{
+
+class NoteStyle;
+class Event;
+
+class NoteStyleFactory
+{
+public:
+ static std::vector<NoteStyleName> getAvailableStyleNames();
+ static const NoteStyleName DefaultStyle;
+
+ static NoteStyle *getStyle(NoteStyleName name);
+ static NoteStyle *getStyleForEvent(Event *event);
+
+ typedef Exception StyleUnavailable;
+
+private:
+ typedef std::map<std::string, NoteStyle *> StyleMap;
+ static StyleMap m_styles;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteStyleFileReader.cpp b/src/gui/editors/notation/NoteStyleFileReader.cpp
new file mode 100644
index 0000000..b3f3464
--- /dev/null
+++ b/src/gui/editors/notation/NoteStyleFileReader.cpp
@@ -0,0 +1,193 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "NoteStyleFileReader.h"
+
+#include <string>
+#include "NoteStyle.h"
+#include <qfileinfo.h>
+#include <qdir.h>
+
+#include <kglobal.h>
+#include <kstddirs.h>
+#include <klocale.h>
+
+#include "misc/Strings.h"
+#include "NotationStrings.h"
+#include "misc/Debug.h"
+
+namespace Rosegarden {
+
+
+NoteStyleFileReader::NoteStyleFileReader(std::string name) :
+ m_style(new NoteStyle(name)),
+ m_haveNote(false)
+{
+ QString styleDirectory =
+ KGlobal::dirs()->findResource("appdata", "styles/");
+
+ QString styleFileName =
+ QString("%1/%2.xml").arg(styleDirectory).arg(strtoqstr(name));
+
+ QFileInfo fileInfo(styleFileName);
+
+ if (!fileInfo.isReadable()) {
+ throw StyleFileReadFailed
+ (qstrtostr(i18n("Can't open style file %1").arg(styleFileName)));
+ }
+
+ QFile styleFile(styleFileName);
+
+ QXmlInputSource source(styleFile);
+ QXmlSimpleReader reader;
+ reader.setContentHandler(this);
+ reader.setErrorHandler(this);
+ bool ok = reader.parse(source);
+ styleFile.close();
+
+ if (!ok) {
+ throw StyleFileReadFailed(qstrtostr(m_errorString));
+ }
+}
+
+bool
+NoteStyleFileReader::startElement(const QString &, const QString &,
+ const QString &qName,
+ const QXmlAttributes &attributes)
+{
+ QString lcName = qName.lower();
+
+ if (lcName == "rosegarden-note-style") {
+
+ QString s = attributes.value("base-style");
+ if (s) m_style->setBaseStyle(qstrtostr(s));
+
+ } else if (lcName == "note") {
+
+ m_haveNote = true;
+
+ QString s = attributes.value("type");
+ if (!s) {
+ m_errorString = i18n("type is a required attribute of note");
+ return false;
+ }
+
+ try {
+ Note::Type type = NotationStrings::getNoteForName(s).getNoteType();
+ if (!setFromAttributes(type, attributes)) return false;
+
+ } catch (NotationStrings::MalformedNoteName n) {
+ m_errorString = i18n("Unrecognised note name %1").arg(s);
+ return false;
+ }
+
+ } else if (lcName == "global") {
+
+ if (m_haveNote) {
+ m_errorString = i18n("global element must precede note elements");
+ return false;
+ }
+
+ for (Note::Type type = Note::Shortest; type <= Note::Longest; ++type) {
+ if (!setFromAttributes(type, attributes)) return false;
+ }
+ }
+
+ return true;
+}
+
+
+bool
+NoteStyleFileReader::setFromAttributes(Note::Type type,
+ const QXmlAttributes &attributes)
+{
+ QString s;
+ bool haveShape = false;
+
+ s = attributes.value("shape");
+ if (s) {
+ m_style->setShape(type, qstrtostr(s.lower()));
+ haveShape = true;
+ }
+
+ s = attributes.value("charname");
+ if (s) {
+ if (haveShape) {
+ m_errorString = i18n("global and note elements may have shape "
+ "or charname attribute, but not both");
+ return false;
+ }
+ m_style->setShape(type, NoteStyle::CustomCharName);
+ m_style->setCharName(type, qstrtostr(s));
+ }
+
+ s = attributes.value("filled");
+ if (s) m_style->setFilled(type, s.lower() == "true");
+
+ s = attributes.value("stem");
+ if (s) m_style->setStem(type, s.lower() == "true");
+
+ s = attributes.value("flags");
+ if (s) m_style->setFlagCount(type, s.toInt());
+
+ s = attributes.value("slashes");
+ if (s) m_style->setSlashCount(type, s.toInt());
+
+ NoteStyle::HFixPoint hfix;
+ NoteStyle::VFixPoint vfix;
+ m_style->getStemFixPoints(type, hfix, vfix);
+ bool haveHFix = false;
+ bool haveVFix = false;
+
+ s = attributes.value("hfixpoint");
+ if (s) {
+ s = s.lower();
+ haveHFix = true;
+ if (s == "normal") hfix = NoteStyle::Normal;
+ else if (s == "central") hfix = NoteStyle::Central;
+ else if (s == "reversed") hfix = NoteStyle::Reversed;
+ else haveHFix = false;
+ }
+
+ s = attributes.value("vfixpoint");
+ if (s) {
+ s = s.lower();
+ haveVFix = true;
+ if (s == "near") vfix = NoteStyle::Near;
+ else if (s == "middle") vfix = NoteStyle::Middle;
+ else if (s == "far") vfix = NoteStyle::Far;
+ else haveVFix = false;
+ }
+
+ if (haveHFix || haveVFix) {
+ m_style->setStemFixPoints(type, hfix, vfix);
+ // otherwise they inherit from base style, so avoid setting here
+ }
+
+ return true;
+}
+
+
+}
+
diff --git a/src/gui/editors/notation/NoteStyleFileReader.h b/src/gui/editors/notation/NoteStyleFileReader.h
new file mode 100644
index 0000000..d3dfbbe
--- /dev/null
+++ b/src/gui/editors/notation/NoteStyleFileReader.h
@@ -0,0 +1,59 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_NOTESTYLEFILEREADER_H_
+#define _RG_NOTESTYLEFILEREADER_H_
+
+#include <qxml.h>
+
+#include "NoteStyle.h"
+
+namespace Rosegarden {
+
+class NoteStyleFileReader : public QXmlDefaultHandler
+{
+public:
+ NoteStyleFileReader(NoteStyleName name);
+
+ typedef Rosegarden::Exception StyleFileReadFailed;
+
+ NoteStyle *getStyle() { return m_style; }
+
+ // Xml handler methods:
+
+ virtual bool startElement
+ (const QString& namespaceURI, const QString& localName,
+ const QString& qName, const QXmlAttributes& atts);
+
+private:
+ bool setFromAttributes(Note::Type type, const QXmlAttributes &attributes);
+
+ QString m_errorString;
+ NoteStyle *m_style;
+ bool m_haveNote;
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/RestInserter.cpp b/src/gui/editors/notation/RestInserter.cpp
new file mode 100644
index 0000000..399cf2d
--- /dev/null
+++ b/src/gui/editors/notation/RestInserter.cpp
@@ -0,0 +1,150 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "RestInserter.h"
+
+#include <klocale.h>
+#include "base/Event.h"
+#include "base/NotationTypes.h"
+#include "base/BaseProperties.h"
+#include "base/Segment.h"
+#include "commands/notation/NoteInsertionCommand.h"
+#include "commands/notation/RestInsertionCommand.h"
+#include "commands/notation/TupletCommand.h"
+#include "gui/general/EditTool.h"
+#include "NotationStrings.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NoteInserter.h"
+#include "NotePixmapFactory.h"
+#include <kaction.h>
+#include <kcommand.h>
+#include <qiconset.h>
+#include <qregexp.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+using namespace BaseProperties;
+
+RestInserter::RestInserter(NotationView* view)
+ : NoteInserter("RestInserter", view)
+{
+ QIconSet icon;
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("dotted-rest-crotchet")));
+ new KToggleAction(i18n("Dotted rest"), icon, 0, this,
+ SLOT(slotToggleDot()), actionCollection(),
+ "toggle_dot");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("select")));
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("crotchet")));
+ new KAction(i18n("Switch to Inserting Notes"), icon, 0, this,
+ SLOT(slotNotesSelected()), actionCollection(),
+ "notes");
+
+ createMenu("restinserter.rc");
+}
+
+void
+RestInserter::showPreview()
+{
+ // no preview available for now
+}
+
+Event *
+RestInserter::doAddCommand(Segment &segment, timeT time, timeT endTime,
+ const Note &note, int, Accidental)
+{
+ if (time < segment.getStartTime() ||
+ endTime > segment.getEndMarkerTime() ||
+ time + note.getDuration() > segment.getEndMarkerTime()) {
+ return 0;
+ }
+
+ NoteInsertionCommand *insertionCommand =
+ new RestInsertionCommand(segment, time, endTime, note);
+
+ KCommand *activeCommand = insertionCommand;
+
+ if (m_nParentView->isInTripletMode()) {
+ Segment::iterator i(segment.findTime(time));
+ if (i != segment.end() &&
+ !(*i)->has(BEAMED_GROUP_TUPLET_BASE)) {
+
+ KMacroCommand *command = new KMacroCommand(insertionCommand->name());
+ command->addCommand(new TupletCommand
+ (segment, time, note.getDuration()));
+ command->addCommand(insertionCommand);
+ activeCommand = command;
+ }
+ }
+
+ m_nParentView->addCommandToHistory(activeCommand);
+
+ return insertionCommand->getLastInsertedEvent();
+}
+
+void RestInserter::slotToggleDot()
+{
+ m_noteDots = (m_noteDots) ? 0 : 1;
+ Note note(m_noteType, m_noteDots);
+ QString actionName(NotationStrings::getReferenceName(note, true));
+ actionName.replace(QRegExp("-"), "_");
+ KAction *action = m_parentView->actionCollection()->action(actionName);
+ if (!action) {
+ std::cerr << "WARNING: No such action as " << actionName << std::endl;
+ } else {
+ action->activate();
+ }
+}
+
+void RestInserter::slotNotesSelected()
+{
+ Note note(m_noteType, m_noteDots);
+ QString actionName(NotationStrings::getReferenceName(note));
+ actionName.replace(QRegExp(" "), "_");
+ m_parentView->actionCollection()->action(actionName)->activate();
+}
+
+const QString RestInserter::ToolName = "restinserter";
+
+}
+#include "RestInserter.moc"
diff --git a/src/gui/editors/notation/RestInserter.h b/src/gui/editors/notation/RestInserter.h
new file mode 100644
index 0000000..90239fb
--- /dev/null
+++ b/src/gui/editors/notation/RestInserter.h
@@ -0,0 +1,76 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_RESTINSERTER_H_
+#define _RG_RESTINSERTER_H_
+
+#include "NoteInserter.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class Note;
+class NotationView;
+class Event;
+
+
+/**
+ * This tool will insert rests on mouse click events
+ */
+class RestInserter : public NoteInserter
+{
+ Q_OBJECT
+
+ friend class NotationToolBox;
+
+public:
+
+ static const QString ToolName;
+
+protected:
+ RestInserter(NotationView*);
+
+ virtual Event *doAddCommand(Segment &,
+ timeT time,
+ timeT endTime,
+ const Note &,
+ int pitch, Accidental);
+ virtual void showPreview();
+
+protected slots:
+ void slotToggleDot();
+ void slotNotesSelected();
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/SystemFont.cpp b/src/gui/editors/notation/SystemFont.cpp
new file mode 100644
index 0000000..71f0ce7
--- /dev/null
+++ b/src/gui/editors/notation/SystemFont.cpp
@@ -0,0 +1,165 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SystemFont.h"
+#include "SystemFontQt.h"
+#include "SystemFontXft.h"
+
+#include "misc/Debug.h"
+
+#include <kstddirs.h>
+#include "NoteFontMap.h"
+#include <qfont.h>
+#include <qfontinfo.h>
+#include <qpixmap.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+SystemFont *
+SystemFont::loadSystemFont(const SystemFontSpec &spec)
+{
+ QString name = spec.first;
+ int size = spec.second;
+
+ NOTATION_DEBUG << "SystemFont::loadSystemFont: name is " << name << ", size " << size << endl;
+
+ if (name == "DEFAULT") {
+ QFont font;
+ font.setPixelSize(size);
+ return new SystemFontQt(font);
+ }
+
+#ifdef HAVE_XFT
+
+ FcPattern *pattern, *match;
+ FcResult result;
+ FcChar8 *matchFamily;
+ XftFont *xfont = 0;
+
+ Display *dpy = QPaintDevice::x11AppDisplay();
+ static bool haveFcDirectory = false;
+
+ if (!dpy) {
+ std::cerr << "SystemFont::loadSystemFont[Xft]: Xft support requested but no X11 display available!" << std::endl;
+ goto qfont;
+ }
+
+ if (!haveFcDirectory) {
+ QString fontDir = KGlobal::dirs()->findResource("appdata", "fonts/");
+ if (!FcConfigAppFontAddDir(FcConfigGetCurrent(),
+ (const FcChar8 *)fontDir.latin1())) {
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Xft]: Failed to add font directory " << fontDir << " to fontconfig, continuing without it" << endl;
+ }
+ haveFcDirectory = true;
+ }
+
+ pattern = FcPatternCreate();
+ FcPatternAddString(pattern, FC_FAMILY, (FcChar8 *)name.latin1());
+ FcPatternAddInteger(pattern, FC_PIXEL_SIZE, size);
+ FcConfigSubstitute(FcConfigGetCurrent(), pattern, FcMatchPattern);
+
+ result = FcResultMatch;
+ match = FcFontMatch(FcConfigGetCurrent(), pattern, &result);
+ FcPatternDestroy(pattern);
+
+ if (!match || result != FcResultMatch) {
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Xft]: No match for font "
+ << name << " (result is " << result
+ << "), falling back on QFont" << endl;
+ if (match)
+ FcPatternDestroy(match);
+ goto qfont;
+ }
+
+ FcPatternGetString(match, FC_FAMILY, 0, &matchFamily);
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Xft]: match family is "
+ << (char *)matchFamily << endl;
+
+ if (QString((char *)matchFamily).lower() != name.lower()) {
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Xft]: Wrong family returned, falling back on QFont" << endl;
+ FcPatternDestroy(match);
+ goto qfont;
+ }
+
+ xfont = XftFontOpenPattern(dpy, match);
+ if (!xfont) {
+ FcPatternDestroy(match);
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Xft]: Unable to load font "
+ << name << " via Xft, falling back on QFont" << endl;
+ goto qfont;
+ }
+
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Xft]: successfully loaded font "
+ << name << " through Xft" << endl;
+
+ return new SystemFontXft(dpy, xfont);
+
+
+qfont:
+
+#endif
+
+ QFont qfont(name, size, QFont::Normal);
+ qfont.setPixelSize(size);
+
+ QFontInfo info(qfont);
+
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Qt]: have family " << info.family() << " (exactMatch " << info.exactMatch() << ")" << endl;
+
+ // return info.exactMatch();
+
+ // The Qt documentation says:
+ //
+ // bool QFontInfo::exactMatch() const
+ // Returns TRUE if the matched window system font is exactly the
+ // same as the one specified by the font; otherwise returns FALSE.
+ //
+ // My arse. I specify "feta", I get "Verdana", and exactMatch
+ // returns true. Uh huh.
+ //
+ // UPDATE: in newer versions of Qt, I specify "fughetta", I get
+ // "Fughetta [macromedia]", and exactMatch returns false. Just as
+ // useless, but in a different way.
+
+ QString family = info.family().lower();
+
+ if (family == name.lower())
+ return new SystemFontQt(qfont);
+ else {
+ int bracket = family.find(" [");
+ if (bracket > 1)
+ family = family.left(bracket);
+ if (family == name.lower())
+ return new SystemFontQt(qfont);
+ }
+
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Qt]: Wrong family returned, failing" << endl;
+ return 0;
+}
+
+}
diff --git a/src/gui/editors/notation/SystemFont.h b/src/gui/editors/notation/SystemFont.h
new file mode 100644
index 0000000..0acc2dd
--- /dev/null
+++ b/src/gui/editors/notation/SystemFont.h
@@ -0,0 +1,63 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SYSTEMFONT_H_
+#define _RG_SYSTEMFONT_H_
+
+#include <qpixmap.h>
+#include "gui/editors/notation/NoteCharacterNames.h"
+
+
+class SystemFontSpec;
+
+
+namespace Rosegarden
+{
+
+typedef std::pair<QString, int> SystemFontSpec;
+
+
+class SystemFont
+{
+public:
+ enum Strategy {
+ PreferGlyphs, PreferCodes, OnlyGlyphs, OnlyCodes
+ };
+
+ virtual QPixmap renderChar(CharName charName,
+ int glyph, int code,
+ Strategy strategy,
+ bool &success) = 0;
+
+ static SystemFont *loadSystemFont(const SystemFontSpec &spec);
+};
+
+
+// Helper class for looking up information about a font
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/SystemFontQt.cpp b/src/gui/editors/notation/SystemFontQt.cpp
new file mode 100644
index 0000000..f9c99b1
--- /dev/null
+++ b/src/gui/editors/notation/SystemFontQt.cpp
@@ -0,0 +1,78 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "SystemFontQt.h"
+
+#include "misc/Debug.h"
+#include "gui/general/PixmapFunctions.h"
+
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+
+namespace Rosegarden {
+
+QPixmap
+SystemFontQt::renderChar(CharName charName, int glyph, int code,
+ Strategy strategy, bool &success)
+{
+ success = false;
+
+ if (strategy == OnlyGlyphs) {
+ NOTATION_DEBUG << "SystemFontQt::renderChar: OnlyGlyphs strategy not supported by Qt renderer, can't render character " << charName.getName() << " (glyph " << glyph << ")" << endl;
+ return QPixmap();
+ }
+
+ if (code < 0) {
+ NOTATION_DEBUG << "SystemFontQt::renderChar: Can't render using Qt with only glyph value (" << glyph << ") for character " << charName.getName() << ", need a code point" << endl;
+ return QPixmap();
+ }
+
+ QFontMetrics metrics(m_font);
+ QChar qc(code);
+
+ QPixmap map;
+ map = QPixmap(metrics.width(qc), metrics.height());
+
+ map.fill();
+ QPainter painter;
+ painter.begin(&map);
+ painter.setFont(m_font);
+ painter.setPen(Qt::black);
+
+ NOTATION_DEBUG << "NoteFont: Drawing character code "
+ << code << " for " << charName.getName()
+ << " using QFont" << endl;
+
+ painter.drawText(0, metrics.ascent(), qc);
+
+ painter.end();
+ map.setMask(PixmapFunctions::generateMask(map, Qt::white.rgb()));
+
+ success = true;
+ return map;
+}
+
+}
diff --git a/src/gui/editors/notation/SystemFontQt.h b/src/gui/editors/notation/SystemFontQt.h
new file mode 100644
index 0000000..ea8f5f2
--- /dev/null
+++ b/src/gui/editors/notation/SystemFontQt.h
@@ -0,0 +1,49 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SYSTEMFONTQT_H_
+#define _RG_SYSTEMFONTQT_H_
+
+#include "SystemFont.h"
+
+#include <qfont.h>
+
+namespace Rosegarden {
+
+class SystemFontQt : public SystemFont
+{
+public:
+ SystemFontQt(QFont &font) : m_font(font) { }
+ virtual ~SystemFontQt() { }
+
+ virtual QPixmap renderChar(CharName charName, int glyph, int code,
+ Strategy strategy, bool &success);
+
+private:
+ QFont m_font;
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/SystemFontXft.cpp b/src/gui/editors/notation/SystemFontXft.cpp
new file mode 100644
index 0000000..ce42f61
--- /dev/null
+++ b/src/gui/editors/notation/SystemFontXft.cpp
@@ -0,0 +1,193 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "SystemFontXft.h"
+
+#ifdef HAVE_XFT
+
+#include "misc/Debug.h"
+#include "gui/general/PixmapFunctions.h"
+
+namespace Rosegarden {
+
+/*!!! Just test code.
+
+int
+staticMoveTo(FT_Vector *to, void *)
+{
+ NOTATION_DEBUG << "moveTo: (" << to->x << "," << to->y << ")" << endl;
+ return 0;
+}
+
+int
+staticLineTo(FT_Vector *to, void *)
+{
+ NOTATION_DEBUG << "lineTo: (" << to->x << "," << to->y << ")" << endl;
+ return 0;
+}
+
+int
+staticConicTo(FT_Vector *control, FT_Vector *to, void *)
+{
+ NOTATION_DEBUG << "conicTo: (" << to->x << "," << to->y << ") control (" << control->x << "," << control->y << ")" << endl;
+ return 0;
+}
+
+int
+staticCubicTo(FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *)
+{
+ NOTATION_DEBUG << "cubicTo: (" << to->x << "," << to->y << ") control1 (" << control1->x << "," << control1->y << ") control2 (" << control2->x << "," << control2->y << ")" << endl;
+ return 0;
+}
+
+*/
+
+QPixmap
+SystemFontXft::renderChar(CharName charName, int glyph, int code,
+ Strategy strategy, bool &success)
+{
+ success = false;
+
+ if (glyph < 0 && code < 0) {
+ NOTATION_DEBUG << "SystemFontXft::renderChar: Have neither glyph nor code point for character " << charName.getName() << ", can't render" << endl;
+ return QPixmap();
+ }
+
+ if (code < 0 && strategy == OnlyCodes) {
+ NOTATION_DEBUG << "SystemFontXft::renderChar: strategy is OnlyCodes but no code point provided for character " << charName.getName() << " (glyph is " << glyph << ")" << endl;
+ return QPixmap();
+ }
+
+ if (glyph < 0 && strategy == OnlyGlyphs) {
+ NOTATION_DEBUG << "SystemFontXft::renderChar: strategy is OnlyGlyphs but no glyph index provided for character " << charName.getName() << " (code is " << code << ")" << endl;
+ return QPixmap();
+ }
+
+ XGlyphInfo extents;
+
+ bool useGlyph = true;
+ if (glyph < 0 || (strategy == PreferCodes && code >= 0)) useGlyph = false;
+ if (glyph >= 0 && useGlyph == false && !XftCharExists(m_dpy, m_font, code)) {
+ NOTATION_DEBUG << "SystemFontXft::renderChar: code " << code << " is preferred for character " << charName.getName() << ", but it doesn't exist in font! Falling back to glyph " << glyph << endl;
+ useGlyph = true;
+ }
+
+ if (useGlyph) {
+ FT_UInt ui(glyph);
+ XftGlyphExtents(m_dpy, m_font, &ui, 1, &extents);
+ if (extents.width == 0 || extents.height == 0) {
+ NOTATION_DEBUG
+ << "SystemFontXft::renderChar: zero extents for character "
+ << charName.getName() << " (glyph " << glyph << ")" << endl;
+ return QPixmap();
+ }
+ } else {
+ FcChar32 char32(code);
+ XftTextExtents32(m_dpy, m_font, &char32, 1, &extents);
+ if (extents.width == 0 || extents.height == 0) {
+ NOTATION_DEBUG
+ << "SystemFontXft::renderChar: zero extents for character "
+ << charName.getName() << " (code " << code << ")" << endl;
+ return QPixmap();
+ }
+ }
+
+ QPixmap map(extents.width, extents.height);
+ map.fill();
+
+ Drawable drawable = (Drawable)map.handle();
+ if (!drawable) {
+ std::cerr << "ERROR: SystemFontXft::renderChar: No drawable in QPixmap!" << std::endl;
+ return map;
+ }
+
+ XftDraw *draw = XftDrawCreate(m_dpy,
+ drawable,
+ (Visual *)map.x11Visual(),
+ map.x11Colormap());
+
+ QColor pen(Qt::black);
+ XftColor col;
+ col.color.red = pen.red () | pen.red() << 8;
+ col.color.green = pen.green () | pen.green() << 8;
+ col.color.blue = pen.blue () | pen.blue() << 8;
+ col.color.alpha = 0xffff;
+ col.pixel = pen.pixel();
+
+ if (useGlyph) {
+ NOTATION_DEBUG << "NoteFont: drawing raw character glyph "
+ << glyph << " for " << charName.getName()
+ << " using Xft" << endl;
+ FT_UInt ui(glyph);
+ XftDrawGlyphs(draw, &col, m_font, extents.x, extents.y, &ui, 1);
+ } else {
+ NOTATION_DEBUG << "NoteFont: drawing character code "
+ << code << " for " << charName.getName()
+ << " using Xft" << endl;
+ FcChar32 char32(code);
+ XftDrawString32(draw, &col, m_font, extents.x, extents.y, &char32, 1);
+ }
+
+ XftDrawDestroy(draw);
+
+ map.setMask(PixmapFunctions::generateMask(map, Qt::white.rgb()));
+ success = true;
+
+
+ //!!! experimental stuff
+/*!!!
+ FT_Face face = XftLockFace(m_font);
+ if (!face) {
+ NOTATION_DEBUG << "Couldn't lock face" << endl;
+ return map;
+ }
+ // not checking return value here
+ FT_Load_Glyph(face, glyph, 0);
+ if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) {
+ NOTATION_DEBUG << "Glyph " << glyph << " isn't an outline" << endl;
+ XftUnlockFace(m_font);
+ return map;
+ }
+ FT_Glyph ftglyph;
+ FT_Get_Glyph(face->glyph, &ftglyph);
+ FT_Outline &outline = ((FT_OutlineGlyph)ftglyph)->outline;
+ NOTATION_DEBUG << "Outline: " << outline.n_contours << " contours, "
+ << outline.n_points << " points" << endl;
+
+
+ FT_Outline_Funcs funcs = {
+ staticMoveTo, staticLineTo, staticConicTo, staticCubicTo, 0, 0
+ };
+ FT_Outline_Decompose(&outline, &funcs, 0);
+ FT_Done_Glyph(ftglyph);
+ XftUnlockFace(m_font);
+*/
+
+ return map;
+}
+
+}
+
+#endif /* HAVE_XFT */
+
diff --git a/src/gui/editors/notation/SystemFontXft.h b/src/gui/editors/notation/SystemFontXft.h
new file mode 100644
index 0000000..b1487c4
--- /dev/null
+++ b/src/gui/editors/notation/SystemFontXft.h
@@ -0,0 +1,58 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SYSTEMFONTXFT_H_
+#define _RG_SYSTEMFONTXFT_H_
+
+#ifdef HAVE_XFT
+
+#include "SystemFont.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#include FT_GLYPH_H
+#include <X11/Xft/Xft.h>
+
+namespace Rosegarden {
+
+class SystemFontXft : public SystemFont
+{
+public:
+ SystemFontXft(Display *dpy, XftFont *font) : m_dpy(dpy), m_font(font) { }
+ virtual ~SystemFontXft() { if (m_font) XftFontClose(m_dpy, m_font); }
+
+ virtual QPixmap renderChar(CharName charName, int glyph, int code,
+ Strategy strategy, bool &success);
+
+private:
+ Display *m_dpy;
+ XftFont *m_font;
+};
+
+}
+
+#endif
+
+#endif
diff --git a/src/gui/editors/notation/TextInserter.cpp b/src/gui/editors/notation/TextInserter.cpp
new file mode 100644
index 0000000..aa8e1ff
--- /dev/null
+++ b/src/gui/editors/notation/TextInserter.cpp
@@ -0,0 +1,169 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TextInserter.h"
+
+#include <klocale.h>
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "base/NotationTypes.h"
+#include "base/ViewElement.h"
+#include "commands/notation/EraseEventCommand.h"
+#include "commands/notation/TextInsertionCommand.h"
+#include "gui/dialogs/TextEventDialog.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/LinedStaff.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NotePixmapFactory.h"
+#include "NotationElement.h"
+#include <kaction.h>
+#include <qdialog.h>
+#include <qiconset.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+TextInserter::TextInserter(NotationView* view)
+ : NotationTool("TextInserter", view),
+ m_text("", Text::Dynamic)
+{
+ QIconSet icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("select")));
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("crotchet")));
+ new KAction(i18n("Switch to Inserting Notes"), icon, 0, this,
+ SLOT(slotNotesSelected()), actionCollection(),
+ "notes");
+
+ createMenu("textinserter.rc");
+}
+
+void TextInserter::slotNotesSelected()
+{
+ m_nParentView->slotLastNoteAction();
+}
+
+void TextInserter::slotEraseSelected()
+{
+ m_parentView->actionCollection()->action("erase")->activate();
+}
+
+void TextInserter::slotSelectSelected()
+{
+ m_parentView->actionCollection()->action("select")->activate();
+}
+
+void TextInserter::ready()
+{
+ m_nParentView->setCanvasCursor(Qt::crossCursor);
+ m_nParentView->setHeightTracking(false);
+}
+
+void TextInserter::handleLeftButtonPress(timeT,
+ int,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element)
+{
+ if (staffNo < 0)
+ return ;
+ LinedStaff *staff = m_nParentView->getLinedStaff(staffNo);
+
+ Text defaultText(m_text);
+ timeT insertionTime;
+ Event *eraseEvent = 0;
+
+ if (element && element->event()->isa(Text::EventType)) {
+
+ // edit an existing text, if that's what we clicked on
+
+ try {
+ defaultText = Text(*element->event());
+ } catch (Exception e) {}
+
+ insertionTime = element->event()->getAbsoluteTime(); // not getViewAbsoluteTime()
+
+ eraseEvent = element->event();
+
+ } else {
+
+ Event *clef = 0, *key = 0;
+
+ NotationElementList::iterator closestElement =
+ staff->getClosestElementToCanvasCoords(e->x(), (int)e->y(),
+ clef, key, false, -1);
+
+ if (closestElement == staff->getViewElementList()->end())
+ return ;
+
+ insertionTime = (*closestElement)->event()->getAbsoluteTime(); // not getViewAbsoluteTime()
+
+ }
+
+ TextEventDialog *dialog = new TextEventDialog
+ (m_nParentView, m_nParentView->getNotePixmapFactory(), defaultText);
+
+ if (dialog->exec() == QDialog::Accepted) {
+
+ m_text = dialog->getText();
+
+ TextInsertionCommand *command =
+ new TextInsertionCommand
+ (staff->getSegment(), insertionTime, m_text);
+
+ if (eraseEvent) {
+ KMacroCommand *macroCommand = new KMacroCommand(command->name());
+ macroCommand->addCommand(new EraseEventCommand(staff->getSegment(),
+ eraseEvent, false));
+ macroCommand->addCommand(command);
+ m_nParentView->addCommandToHistory(macroCommand);
+ } else {
+ m_nParentView->addCommandToHistory(command);
+ }
+
+ Event *event = command->getLastInsertedEvent();
+ if (event)
+ m_nParentView->setSingleSelectedEvent(staffNo, event);
+ }
+
+ delete dialog;
+}
+
+const QString TextInserter::ToolName = "textinserter";
+
+}
+#include "TextInserter.moc"
diff --git a/src/gui/editors/notation/TextInserter.h b/src/gui/editors/notation/TextInserter.h
new file mode 100644
index 0000000..3b4821b
--- /dev/null
+++ b/src/gui/editors/notation/TextInserter.h
@@ -0,0 +1,78 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TEXTINSERTER_H_
+#define _RG_TEXTINSERTER_H_
+
+#include "base/NotationTypes.h"
+#include "NotationTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class NotationView;
+
+
+/**
+ * This tool will request and insert text on mouse click events
+ */
+class TextInserter : public NotationTool
+{
+ Q_OBJECT
+
+ friend class NotationToolBox;
+
+public:
+ virtual void ready();
+
+ virtual void handleLeftButtonPress(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement* el);
+ static const QString ToolName;
+
+protected slots:
+ void slotNotesSelected();
+ void slotEraseSelected();
+ void slotSelectSelected();
+
+protected:
+ TextInserter(NotationView*);
+ Text m_text;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/TrackHeader.cpp b/src/gui/editors/notation/TrackHeader.cpp
new file mode 100644
index 0000000..32fab2f
--- /dev/null
+++ b/src/gui/editors/notation/TrackHeader.cpp
@@ -0,0 +1,450 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ This file is Copyright 2007-2008
+ Yves Guillemot <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+
+#include "TrackHeader.h"
+#include "HeadersGroup.h"
+#include "base/Composition.h"
+#include "base/NotationTypes.h"
+#include "base/StaffExportTypes.h"
+#include "base/Colour.h"
+#include "base/ColourMap.h"
+#include "base/Track.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/LinedStaff.h"
+#include "document/RosegardenGUIDoc.h"
+#include "misc/Strings.h"
+#include "NotePixmapFactory.h"
+#include "NotationView.h"
+#include "NotationStaff.h"
+
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <qsize.h>
+#include <qwidget.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+#include <qframe.h>
+#include <qstring.h>
+#include <qtooltip.h>
+
+
+namespace Rosegarden
+{
+
+
+// Status bits
+const int TrackHeader::SEGMENT_HERE = 1 << 0;
+const int TrackHeader::SUPERIMPOSED_SEGMENTS = 1 << 1;
+const int TrackHeader::INCONSISTENT_CLEFS = 1 << 2;
+const int TrackHeader::INCONSISTENT_KEYS = 1 << 3;
+const int TrackHeader::INCONSISTENT_LABELS = 1 << 4;
+const int TrackHeader::INCONSISTENT_TRANSPOSITIONS = 1 << 5;
+const int TrackHeader::BEFORE_FIRST_SEGMENT = 1 << 6;
+
+
+TrackHeader::TrackHeader(QWidget *parent, TrackId trackId, int height, int ypos) :
+ QLabel(parent),
+ m_track(trackId),
+ m_height(height),
+ m_ypos(ypos),
+ m_lastClef(Clef()),
+ m_lastKey(Rosegarden::Key()),
+ m_lastLabel(QString("")),
+ m_lastTranspose(0),
+ m_lastUpperText(QString("")),
+ m_neverUpdated(true),
+ m_isCurrent(true),
+ m_lastStatusPart(0),
+ m_lastWidth(0),
+ m_key(Rosegarden::Key()),
+ m_label(QString("")),
+ m_transpose(0),
+ m_status(0),
+ m_current(false)
+{
+
+ m_notationView = static_cast<HeadersGroup *>(parent)->getNotationView();
+
+ setFrameStyle(QFrame::Box | QFrame::Plain);
+ setCurrent(false);
+
+
+ //
+ // Tooltip text creation
+
+ Composition *comp =
+ static_cast<HeadersGroup *>(parent)->getComposition();
+ Track *track = comp->getTrackById(m_track);
+ int trackPos = comp->getTrackPositionById(m_track);
+
+ QString toolTipText = QString(i18n("Track %1 : \"%2\"")
+ .arg(trackPos + 1)
+ .arg(strtoqstr(track->getLabel())));
+
+ QString preset = track->getPresetLabel();
+ if (preset != QString(""))
+ toolTipText += QString(i18n("\nNotate for: %1").arg(preset));
+
+ QString notationSize = i18n("normal");
+ switch (track->getStaffSize()) {
+ case StaffTypes::Small:
+ notationSize = i18n("small");
+ break;
+ case StaffTypes::Tiny:
+ notationSize = i18n("tiny");
+ break;
+ }
+
+ QString bracketText = i18n("--");
+ switch (track->getStaffBracket()) {
+ case Brackets::SquareOn:
+ bracketText = "[-";
+ break;
+ case Brackets::SquareOff:
+ bracketText = "-]";
+ break;
+ case Brackets::SquareOnOff:
+ bracketText = "[-]";
+ break;
+ case Brackets::CurlyOn:
+ bracketText = "{-";
+ break;
+ case Brackets::CurlyOff:
+ bracketText = "-}";
+ break;
+ case Brackets::CurlySquareOn:
+ bracketText = "{[-";
+ break;
+ case Brackets::CurlySquareOff:
+ bracketText = "-]}";
+ break;
+ }
+
+ toolTipText += QString(i18n("\nSize: %1, Bracket: %2 "))
+ .arg(notationSize)
+ .arg(bracketText);
+
+ // Sort segments by position on the track
+ SortedSegments segments;
+ for (int i=0; i<m_notationView->getStaffCount(); i++) {
+
+ NotationStaff * notationStaff = m_notationView->getNotationStaff(i);
+ Segment &segment = notationStaff->getSegment();
+ TrackId trackId = segment.getTrack();
+
+ if (trackId == m_track) {
+ segments.insert(&segment);
+ }
+ }
+
+ for (SortedSegments::iterator i=segments.begin(); i!=segments.end(); ++i) {
+ timeT segStart = (*i)->getStartTime();
+ timeT segEnd = (*i)->getEndMarkerTime();
+ int barStart = comp->getBarNumber(segStart) + 1;
+ int barEnd = comp->getBarNumber(segEnd) + 1;
+
+ int transpose = (*i)->getTranspose();
+ if (transpose) {
+ QString transposeName;
+ transposeValueToName(transpose, transposeName);
+ toolTipText += QString(i18n("\nbars [%1-%2] in %3 (tr=%4) : \"%5\""))
+ .arg(barStart)
+ .arg(barEnd)
+ .arg(transposeName)
+ .arg(transpose)
+ .arg(strtoqstr((*i)->getLabel()));
+ } else {
+ toolTipText += QString(i18n("\nbars [%1-%2] (tr=%3) : \"%4\""))
+ .arg(barStart)
+ .arg(barEnd)
+ .arg(transpose)
+ .arg(strtoqstr((*i)->getLabel()));
+ }
+ }
+
+ QToolTip::add(this, toolTipText);
+
+ m_firstSeg = *segments.begin();
+ m_firstSegStartTime = m_firstSeg->getStartTime();
+
+ /// This may not work if two segments are superimposed
+ /// at the beginning of the track (inconsistencies are
+ /// not detected).
+ /// TODO : Look for the first segment(s) in
+ /// lookAtStaff() and not here.
+}
+
+void
+TrackHeader::setCurrent(bool current)
+{
+ /// TODO : use colours from GUIPalette
+
+ if (current != m_isCurrent) {
+ m_isCurrent = current;
+ if (current) {
+ setLineWidth(2);
+ setMargin(0);
+ setPaletteForegroundColor(QColor(0, 0, 255));
+ } else {
+ setLineWidth(1);
+ setMargin(1);
+ setPaletteForegroundColor(QColor(0, 0, 0));
+ }
+ }
+}
+
+void
+TrackHeader::transposeValueToName(int transpose, QString &transposeName)
+{
+
+ /// TODO : Should be rewrited using methods from Pitch class
+
+ int noteIndex = transpose % 12;
+ if (noteIndex < 0) noteIndex += 12;
+
+ switch(noteIndex) {
+ case 0 : transposeName = i18n("C"); break;
+ case 1 : transposeName = i18n("C#"); break;
+ case 2 : transposeName = i18n("D"); break;
+ case 3 : transposeName = i18n("Eb"); break;
+ case 4 : transposeName = i18n("E"); break;
+ case 5 : transposeName = i18n("F"); break;
+ case 6 : transposeName = i18n("F#"); break;
+ case 7 : transposeName = i18n("G"); break;
+ case 8 : transposeName = i18n("G#"); break;
+ case 9 : transposeName = i18n("A"); break;
+ case 10 : transposeName = i18n("Bb"); break;
+ case 11 : transposeName = i18n("B"); break;
+ }
+}
+
+int
+TrackHeader::lookAtStaff(double x, int maxWidth)
+{
+ // Read Clef and Key on canvas at (x, m_ypos + m_height / 2)
+ // then guess the header needed width and return it
+
+ // When walking through the segments :
+ // clef, key, label and transpose are current values
+ // clef0, key0, label0 and transpose0 are preceding values used to look
+ // for inconsistencies
+ // key1, label1 and transpose1 are "visible" (opposed at invisible as are
+ // key=<C major>, label="" or transpose=0)
+ // preceding or current values which may be
+ // displayed with a red colour if some
+ // inconsistency occurs.
+ Clef clef, clef0;
+ Rosegarden::Key key, key0, key1 = Rosegarden::Key("C major");
+ QString label = QString(""), label0, label1 = QString("");
+ int transpose = 0, transpose0, transpose1 = 0;
+
+ int staff;
+
+ Composition *comp =
+ static_cast<HeadersGroup *>(parent())->getComposition();
+ Track *track = comp->getTrackById(m_track);
+ int trackPos = comp->getTrackPositionById(m_track);
+
+ int status = 0;
+ bool current = false;
+ for (int i=0; i<m_notationView->getStaffCount(); i++) {
+ NotationStaff * notationStaff = m_notationView->getNotationStaff(i);
+ Segment &segment = notationStaff->getSegment();
+ TrackId trackId = segment.getTrack();
+ if (trackId == m_track) {
+
+ /// TODO : What if a segment has been moved ???
+ timeT xTime = notationStaff->getTimeAtCanvasCoords(x, m_ypos);
+ if (xTime < m_firstSegStartTime) {
+ status |= BEFORE_FIRST_SEGMENT;
+ /// TODO : What if superimposed segments ???
+ m_firstSeg->getFirstClefAndKey(clef, key);
+ label = strtoqstr(m_firstSeg->getLabel());
+ transpose = m_firstSeg->getTranspose();
+ current = current || m_notationView->isCurrentStaff(i);
+ break;
+ }
+ timeT segStart = segment.getStartTime();
+ timeT segEnd = segment.getEndMarkerTime();
+ current = current || m_notationView->isCurrentStaff(i);
+
+ if ((xTime >= segStart) && (xTime < segEnd)) {
+
+ notationStaff->getClefAndKeyAtCanvasCoords(x,
+ m_ypos + m_height / 2, clef, key);
+ label = strtoqstr(segment.getLabel());
+ transpose = segment.getTranspose();
+
+ if (status & SEGMENT_HERE) {
+ status |= SUPERIMPOSED_SEGMENTS;
+ if (clef != clef0)
+ status |= INCONSISTENT_CLEFS;
+ if (key != key0)
+ status |= INCONSISTENT_KEYS;
+ if (label != label0)
+ status |= INCONSISTENT_LABELS;
+ if (transpose != transpose0)
+ status |= INCONSISTENT_TRANSPOSITIONS;
+ } else {
+ status |= SEGMENT_HERE;
+ }
+
+ staff = i;
+
+ // If current value is visible, remember it
+ if (key.getAccidentalCount()) key1 = key;
+ if (label.stripWhiteSpace().length()) label1 = label;
+ if (transpose) transpose1 = transpose;
+
+ // Current values become last values
+ clef0 = clef;
+ key0 = key;
+ label0 = label;
+ transpose0 = transpose;
+ } // if(xTime...)
+ } // if(trackId...)
+ }
+
+ // Remember current data (but only visible data if inconsistency)
+ m_clef = clef;
+ m_key = (status & INCONSISTENT_KEYS) ? key1 : key;
+ m_label = (status & INCONSISTENT_LABELS) ? label1 : label;
+ m_transpose = (status & INCONSISTENT_TRANSPOSITIONS) ? transpose1 : transpose;
+ m_current = current;
+ m_status = status;
+
+ QString noteName;
+ transposeValueToName(m_transpose, noteName);
+
+ m_upperText = QString(i18n("%1: %2")
+ .arg(trackPos + 1)
+ .arg(strtoqstr(track->getLabel())));
+ if (m_transpose) m_transposeText = i18n(" in %1").arg(noteName);
+ else m_transposeText = QString("");
+
+ NotePixmapFactory * npf = m_notationView->getNotePixmapFactory();
+ int clefAndKeyWidth = npf->getClefAndKeyWidth(m_key, m_clef);
+
+ // How many text lines may be written above or under the clef
+ // in track header ?
+ m_numberOfTextLines = npf->getTrackHeaderNTL(m_height);
+
+ int trackLabelWidth =
+ npf->getTrackHeaderTextWidth(m_upperText + m_transposeText)
+ / m_numberOfTextLines;
+ int segmentNameWidth =
+ npf->getTrackHeaderTextWidth(m_label) / m_numberOfTextLines;
+
+ // Get the max. width from upper text and lower text
+ int width = (segmentNameWidth > trackLabelWidth)
+ ? segmentNameWidth : trackLabelWidth;
+
+ // Text width is limited by max header Width
+ if (width > maxWidth) width = maxWidth;
+
+ // But clef and key width may override max header width
+ if (width < clefAndKeyWidth) width = clefAndKeyWidth;
+
+ return width;
+}
+
+
+
+void
+TrackHeader::updateHeader(int width)
+{
+
+ // Update the header (using given width) if necessary
+
+ // Filter out bits whose display doesn't depend from
+ int statusPart = m_status & ~(SUPERIMPOSED_SEGMENTS);
+
+ // Header should be updated only if necessary
+ if ( m_neverUpdated
+ || (width != m_lastWidth)
+ || (statusPart != m_lastStatusPart)
+ || (m_key != m_lastKey)
+ || (m_clef != m_lastClef)
+ || (m_label != m_lastLabel)
+ || (m_upperText != m_lastUpperText)
+ || (m_transpose != m_lastTranspose)) {
+
+ m_neverUpdated = false;
+ m_lastStatusPart = statusPart;
+ m_lastKey = m_key;
+ m_lastClef = m_clef;
+ m_lastLabel = m_label;
+ m_lastTranspose = m_transpose;
+ m_lastUpperText = m_upperText;
+
+ bool drawClef = true;
+ QColor clefColour;
+ if (m_status & (SEGMENT_HERE | BEFORE_FIRST_SEGMENT)) {
+ if (m_status & (INCONSISTENT_CLEFS | INCONSISTENT_KEYS))
+ clefColour = Qt::red;
+ else
+ clefColour = Qt::black;
+ } else {
+ drawClef = false;
+ }
+
+ NotePixmapFactory * npf = m_notationView->getNotePixmapFactory();
+ QPixmap pmap = NotePixmapFactory::toQPixmap(
+ npf->makeTrackHeaderPixmap(width, m_height, this));
+
+ setPixmap(pmap);
+ setFixedWidth(width);
+
+ // Forced width may differ from localy computed width
+ m_lastWidth = width;
+ }
+
+ // Highlight header if track is the current one
+ setCurrent(m_current);
+}
+
+bool
+TrackHeader::SegmentCmp::operator()(const Segment * s1, const Segment * s2) const
+{
+ // Sort segments by start time, then by end time
+ if (s1->getStartTime() < s2->getStartTime()) return true;
+ if (s1->getStartTime() > s2->getStartTime()) return false;
+ if (s1->getEndMarkerTime() < s2->getEndMarkerTime()) return true;
+ return false;
+}
+
+}
+#include "TrackHeader.moc"
diff --git a/src/gui/editors/notation/TrackHeader.h b/src/gui/editors/notation/TrackHeader.h
new file mode 100644
index 0000000..0104430
--- /dev/null
+++ b/src/gui/editors/notation/TrackHeader.h
@@ -0,0 +1,219 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ This file is Copyright 2007-2008
+ Yves Guillemot <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#ifndef _RG_TRACKHEADER_H_
+#define _RG_TRACKHEADER_H_
+
+#include "base/NotationTypes.h"
+#include "base/Track.h"
+
+#include <qsize.h>
+#include <qwidget.h>
+#include <qlabel.h>
+
+#include <set>
+
+class QLabel;
+
+
+namespace Rosegarden
+{
+
+class NotePixmapFactory;
+class NotationView;
+class ColourMap;
+class Segment;
+
+class TrackHeader : public QLabel
+{
+ Q_OBJECT
+public:
+ /**
+ * Create a new track header for track of id trackId.
+ * *parent is the parent widget, height the height of staff and
+ * ypos is the staff y position on canvas.
+ */
+ TrackHeader(QWidget *parent, TrackId trackId, int height, int ypos);
+
+ /**
+ * Draw a blue line around header when current is true
+ * (intended to highlight the "current" track).
+ */
+ void setCurrent(bool current);
+
+ /**
+ * Examine staff at x position and gather data needed to draw
+ * the track header.
+ * Return the minimum width required to display the track header.
+ * maxWidth is the maximum width allowed to show text. Return width
+ * may be greater than maxWidth if needed to show clef and key signature.
+ * (Header have always to show complete clef and key signature).
+ */
+ int lookAtStaff(double x, int maxWidth);
+
+ /**
+ * (Re)draw the header on the notation view using the data gathered
+ * by lookAtStaff() last call and the specified width.
+ */
+ void updateHeader(int width);
+
+ /**
+ * Return the Id of the associated track.
+ */
+ TrackId getId()
+ { return m_track;
+ }
+
+ /**
+ * Return how many text lines may be written in the header (above
+ * the clef and under the clef).
+ * This data is coming from the last call of lookAtStaff().
+ */
+ int getNumberOfTextLines() { return m_numberOfTextLines; }
+
+ /**
+ * Return the Clef to draw in the header
+ */
+ Clef & getClef() { return m_clef; }
+
+ /**
+ * Get which key signature should be drawn in the header
+ * from the last call of lookAtStaff().
+ */
+ Rosegarden::Key & getKey() { return m_key; }
+
+ /**
+ * Return true if a Clef (and a key signature) have to be drawn in the header
+ */
+ bool isAClefToDraw()
+ {
+ return (m_status & SEGMENT_HERE) || (m_status & BEFORE_FIRST_SEGMENT);
+ }
+
+ /**
+ * Return the text to write in the header top
+ */
+ QString getUpperText() { return m_upperText; }
+
+ /**
+ * Return the transposition text
+ * (to be written at the end of the upper text)
+ */
+ QString getTransposeText() { return m_transposeText; }
+
+ /**
+ * Return the text to write in the header bottom
+ */
+ QString getLowerText() { return m_label; }
+
+ /**
+ * Return true if two segments or more are superimposed and are
+ * not using the same clef
+ */
+ bool isClefInconsistent() { return m_status & INCONSISTENT_CLEFS; }
+
+ /**
+ * Return true if two segments or more are superimposed and are
+ * not using the same key signature
+ */
+ bool isKeyInconsistent() { return m_status & INCONSISTENT_KEYS; }
+
+ /**
+ * Return true if two segments or more are superimposed and are
+ * not using the same label
+ */
+ bool isLabelInconsistent() { return m_status & INCONSISTENT_LABELS; }
+
+ /**
+ * Return true if two segments or more are superimposed and are
+ * not using the same transposition
+ */
+ bool isTransposeInconsistent()
+ {
+ return m_status & INCONSISTENT_TRANSPOSITIONS;
+ }
+
+
+private :
+ /**
+ * Convert the transpose value to the instrument tune and
+ * return it in a printable string.
+ */
+ void transposeValueToName(int transpose, QString &transposeName);
+
+
+ // Status bits
+ static const int SEGMENT_HERE;
+ static const int SUPERIMPOSED_SEGMENTS;
+ static const int INCONSISTENT_CLEFS;
+ static const int INCONSISTENT_KEYS;
+ static const int INCONSISTENT_LABELS;
+ static const int INCONSISTENT_TRANSPOSITIONS;
+ static const int BEFORE_FIRST_SEGMENT;
+
+ TrackId m_track;
+ int m_height;
+ int m_ypos;
+ NotationView * m_notationView;
+
+ Clef m_lastClef;
+ Rosegarden::Key m_lastKey;
+ QString m_lastLabel;
+ int m_lastTranspose;
+ QString m_lastUpperText;
+ bool m_neverUpdated;
+ bool m_isCurrent;
+ int m_lastStatusPart;
+ int m_lastWidth;
+
+ Clef m_clef;
+ Rosegarden::Key m_key;
+ QString m_label;
+ int m_transpose;
+ int m_status;
+ bool m_current;
+
+ QString m_upperText;
+ QString m_transposeText;
+ int m_numberOfTextLines;
+
+ // Used to sort the segments listed in the header toolTipText
+ struct SegmentCmp {
+ bool operator()(const Segment *s1, const Segment *s2) const;
+ };
+ typedef std::multiset<Segment *, SegmentCmp> SortedSegments;
+
+ // First segment on the track.
+ Segment * m_firstSeg;
+ timeT m_firstSegStartTime;
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/parameters/AudioInstrumentParameterPanel.cpp b/src/gui/editors/parameters/AudioInstrumentParameterPanel.cpp
new file mode 100644
index 0000000..44a202b
--- /dev/null
+++ b/src/gui/editors/parameters/AudioInstrumentParameterPanel.cpp
@@ -0,0 +1,437 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "AudioInstrumentParameterPanel.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/AudioPluginInstance.h"
+#include "base/Instrument.h"
+#include "base/MidiProgram.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/studio/AudioPluginManager.h"
+#include "gui/studio/AudioPlugin.h"
+#include "gui/studio/StudioControl.h"
+#include "gui/widgets/AudioFaderBox.h"
+#include "gui/widgets/AudioVUMeter.h"
+#include "gui/widgets/Fader.h"
+#include "gui/widgets/Rotary.h"
+#include "gui/widgets/AudioRouteMenu.h"
+#include "InstrumentParameterPanel.h"
+#include "sound/MappedCommon.h"
+#include "sound/MappedStudio.h"
+#include <qcolor.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qpalette.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+#include <qsignalmapper.h>
+
+
+namespace Rosegarden
+{
+
+void
+AudioInstrumentParameterPanel::slotSelectAudioLevel(float dB)
+{
+ if (m_selectedInstrument == 0)
+ return ;
+
+ if (m_selectedInstrument->getType() == Instrument::Audio ||
+ m_selectedInstrument->getType() == Instrument::SoftSynth) {
+ m_selectedInstrument->setLevel(dB);
+
+ StudioControl::setStudioObjectProperty
+ (MappedObjectId(m_selectedInstrument->getMappedId()),
+ MappedAudioFader::FaderLevel,
+ MappedObjectValue(dB));
+ }
+
+ emit updateAllBoxes();
+ emit instrumentParametersChanged(m_selectedInstrument->getId());
+}
+
+void
+AudioInstrumentParameterPanel::slotSelectAudioRecordLevel(float dB)
+{
+ if (m_selectedInstrument == 0)
+ return ;
+
+ // std::cerr << "AudioInstrumentParameterPanel::slotSelectAudioRecordLevel("
+ // << dB << ")" << std::endl;
+
+ if (m_selectedInstrument->getType() == Instrument::Audio) {
+ m_selectedInstrument->setRecordLevel(dB);
+
+ StudioControl::setStudioObjectProperty
+ (MappedObjectId(m_selectedInstrument->getMappedId()),
+ MappedAudioFader::FaderRecordLevel,
+ MappedObjectValue(dB));
+
+ emit updateAllBoxes();
+ emit instrumentParametersChanged(m_selectedInstrument->getId());
+ }
+}
+
+void
+AudioInstrumentParameterPanel::slotPluginSelected(InstrumentId instrumentId,
+ int index, int plugin)
+{
+ if (!m_selectedInstrument ||
+ instrumentId != m_selectedInstrument->getId())
+ return ;
+
+ RG_DEBUG << "AudioInstrumentParameterPanel::slotPluginSelected - "
+ << "instrument = " << instrumentId
+ << ", index = " << index
+ << ", plugin = " << plugin << endl;
+
+ QColor pluginBackgroundColour = Qt::black;
+ bool bypassed = false;
+
+ QPushButton *button = 0;
+ QString noneText;
+
+ // updates synth gui button &c:
+ m_audioFader->slotSetInstrument(&m_doc->getStudio(), m_selectedInstrument);
+
+ if (index == (int)Instrument::SYNTH_PLUGIN_POSITION) {
+ button = m_audioFader->m_synthButton;
+ noneText = i18n("<no synth>");
+ } else {
+ button = m_audioFader->m_plugins[index];
+ noneText = i18n("<no plugin>");
+ }
+
+ if (!button)
+ return ;
+
+ if (plugin == -1) {
+
+ button->setText(noneText);
+ QToolTip::add
+ (button, noneText);
+
+ } else {
+
+ AudioPlugin *pluginClass = m_doc->getPluginManager()->getPlugin(plugin);
+
+ if (pluginClass) {
+ button->setText(pluginClass->getLabel());
+
+ QToolTip::add
+ (button, pluginClass->getLabel());
+
+ pluginBackgroundColour = pluginClass->getColour();
+ }
+ }
+
+ AudioPluginInstance *inst =
+ m_selectedInstrument->getPlugin(index);
+
+ if (inst)
+ bypassed = inst->isBypassed();
+
+ setButtonColour(index, bypassed, pluginBackgroundColour);
+
+ if (index == (int)Instrument::SYNTH_PLUGIN_POSITION) {
+ emit changeInstrumentLabel(instrumentId, button->text());
+ }
+}
+
+void
+AudioInstrumentParameterPanel::slotPluginBypassed(InstrumentId instrumentId,
+ int pluginIndex, bool bp)
+{
+ if (!m_selectedInstrument ||
+ instrumentId != m_selectedInstrument->getId())
+ return ;
+
+ AudioPluginInstance *inst =
+ m_selectedInstrument->getPlugin(pluginIndex);
+
+ QColor backgroundColour = Qt::black; // default background colour
+
+ if (inst && inst->isAssigned()) {
+ AudioPlugin *pluginClass
+ = m_doc->getPluginManager()->getPlugin(
+ m_doc->getPluginManager()->
+ getPositionByIdentifier(inst->getIdentifier().c_str()));
+
+ /// Set the colour on the button
+ //
+ if (pluginClass)
+ backgroundColour = pluginClass->getColour();
+ }
+
+ setButtonColour(pluginIndex, bp, backgroundColour);
+}
+
+void
+AudioInstrumentParameterPanel::setButtonColour(
+ int pluginIndex, bool bypassState, const QColor &colour)
+{
+ RG_DEBUG << "AudioInstrumentParameterPanel::setButtonColour "
+ << "pluginIndex = " << pluginIndex
+ << ", bypassState = " << bypassState
+ << ", rgb = " << colour.name() << endl;
+
+ QPushButton *button = 0;
+
+ if (pluginIndex == Instrument::SYNTH_PLUGIN_POSITION) {
+ button = m_audioFader->m_synthButton;
+ } else {
+ button = m_audioFader->m_plugins[pluginIndex];
+ }
+
+ if (!button)
+ return ;
+
+ // Set the bypass colour on the plugin button
+ if (bypassState) {
+ button->
+ setPaletteForegroundColor(kapp->palette().
+ color(QPalette::Active, QColorGroup::Button));
+
+ button->
+ setPaletteBackgroundColor(kapp->palette().
+ color(QPalette::Active, QColorGroup::ButtonText));
+ } else if (colour == Qt::black) {
+ button->
+ setPaletteForegroundColor(kapp->palette().
+ color(QPalette::Active, QColorGroup::ButtonText));
+
+ button->
+ setPaletteBackgroundColor(kapp->palette().
+ color(QPalette::Active, QColorGroup::Button));
+ } else {
+ button->
+ setPaletteForegroundColor(Qt::white);
+
+ button->
+ setPaletteBackgroundColor(colour);
+ }
+}
+
+AudioInstrumentParameterPanel::AudioInstrumentParameterPanel(RosegardenGUIDoc* doc, QWidget* parent)
+ : InstrumentParameterPanel(doc, parent),
+ m_audioFader(new AudioFaderBox(this))
+{
+ QGridLayout *gridLayout = new QGridLayout(this, 3, 2, 5, 5);
+
+ // Instrument label : first row, all cols
+ gridLayout->addMultiCellWidget(m_instrumentLabel, 0, 0, 0, 1, AlignCenter);
+
+ // fader and connect it
+ gridLayout->addMultiCellWidget(m_audioFader, 1, 1, 0, 1);
+
+ gridLayout->setRowStretch(2, 1);
+
+ connect(m_audioFader, SIGNAL(audioChannelsChanged(int)),
+ this, SLOT(slotAudioChannels(int)));
+
+ connect(m_audioFader->m_signalMapper, SIGNAL(mapped(int)),
+ this, SLOT(slotSelectPlugin(int)));
+
+ connect(m_audioFader->m_fader, SIGNAL(faderChanged(float)),
+ this, SLOT(slotSelectAudioLevel(float)));
+
+ connect(m_audioFader->m_recordFader, SIGNAL(faderChanged(float)),
+ this, SLOT(slotSelectAudioRecordLevel(float)));
+
+ connect(m_audioFader->m_pan, SIGNAL(valueChanged(float)),
+ this, SLOT(slotSetPan(float)));
+
+ connect(m_audioFader->m_audioOutput, SIGNAL(changed()),
+ this, SLOT(slotAudioRoutingChanged()));
+
+ connect(m_audioFader->m_audioInput, SIGNAL(changed()),
+ this, SLOT(slotAudioRoutingChanged()));
+
+ connect(m_audioFader->m_synthButton, SIGNAL(clicked()),
+ this, SLOT(slotSynthButtonClicked()));
+
+ connect(m_audioFader->m_synthGUIButton, SIGNAL(clicked()),
+ this, SLOT(slotSynthGUIButtonClicked()));
+}
+
+void
+AudioInstrumentParameterPanel::slotSynthButtonClicked()
+{
+ slotSelectPlugin(Instrument::SYNTH_PLUGIN_POSITION);
+}
+
+void
+AudioInstrumentParameterPanel::slotSynthGUIButtonClicked()
+{
+ emit showPluginGUI(m_selectedInstrument->getId(),
+ Instrument::SYNTH_PLUGIN_POSITION);
+}
+
+void
+AudioInstrumentParameterPanel::slotSetPan(float pan)
+{
+ RG_DEBUG << "AudioInstrumentParameterPanel::slotSetPan - "
+ << "pan = " << pan << endl;
+
+ StudioControl::setStudioObjectProperty
+ (MappedObjectId(m_selectedInstrument->getMappedId()),
+ MappedAudioFader::Pan,
+ MappedObjectValue(pan));
+
+ m_selectedInstrument->setPan(MidiByte(pan + 100.0));
+ emit instrumentParametersChanged(m_selectedInstrument->getId());
+}
+
+void
+AudioInstrumentParameterPanel::setAudioMeter(float dBleft, float dBright,
+ float recDBleft, float recDBright)
+{
+ // RG_DEBUG << "AudioInstrumentParameterPanel::setAudioMeter: (" << dBleft
+ // << "," << dBright << ")" << endl;
+
+ if (m_selectedInstrument) {
+ // Always set stereo, because we have to reflect what's happening
+ // with the pan setting even on mono tracks
+ m_audioFader->m_vuMeter->setLevel(dBleft, dBright);
+ m_audioFader->m_vuMeter->setRecordLevel(recDBleft, recDBright);
+ }
+}
+
+void
+AudioInstrumentParameterPanel::setupForInstrument(Instrument* instrument)
+{
+ blockSignals(true);
+
+ m_selectedInstrument = instrument;
+
+ m_instrumentLabel->setText(strtoqstr(instrument->getName()));
+
+ m_audioFader->m_recordFader->setFader(instrument->getRecordLevel());
+ m_audioFader->m_fader->setFader(instrument->getLevel());
+
+ m_audioFader->slotSetInstrument(&m_doc->getStudio(), instrument);
+
+ int start = 0;
+
+ if (instrument->getType() == Instrument::SoftSynth)
+ start = -1;
+
+ for (int i = start; i < int(m_audioFader->m_plugins.size()); i++) {
+ int index;
+ QPushButton *button;
+ QString noneText;
+
+ if (i == -1) {
+ index = Instrument::SYNTH_PLUGIN_POSITION;
+ button = m_audioFader->m_synthButton;
+ noneText = i18n("<no synth>");
+ } else {
+ index = i;
+ button = m_audioFader->m_plugins[i];
+ noneText = i18n("<no plugin>");
+ }
+
+ button->show();
+
+ AudioPluginInstance *inst = instrument->getPlugin(index);
+
+ if (inst && inst->isAssigned()) {
+ AudioPlugin *pluginClass
+ = m_doc->getPluginManager()->getPlugin(
+ m_doc->getPluginManager()->
+ getPositionByIdentifier(inst->getIdentifier().c_str()));
+
+ if (pluginClass) {
+ button->setText(pluginClass->getLabel());
+ QToolTip::add
+ (button, pluginClass->getLabel());
+ setButtonColour(index, inst->isBypassed(),
+ pluginClass->getColour());
+ }
+ } else {
+ button->setText(noneText);
+ QToolTip::add
+ (button, noneText);
+ setButtonColour(index, inst ? inst->isBypassed() : false, Qt::black);
+ }
+ }
+
+ // Set the number of channels on the fader widget
+ //
+ m_audioFader->setAudioChannels(instrument->getAudioChannels());
+
+ // Pan - adjusted backwards
+ //
+ m_audioFader->m_pan->setPosition(instrument->getPan() - 100);
+
+ // Tell fader box whether to include e.g. audio input selection
+ //
+ m_audioFader->setIsSynth(instrument->getType() == Instrument::SoftSynth);
+
+ blockSignals(false);
+}
+
+void
+AudioInstrumentParameterPanel::slotAudioChannels(int channels)
+{
+ RG_DEBUG << "AudioInstrumentParameterPanel::slotAudioChannels - "
+ << "channels = " << channels << endl;
+
+ m_selectedInstrument->setAudioChannels(channels);
+
+ StudioControl::setStudioObjectProperty
+ (MappedObjectId(m_selectedInstrument->getMappedId()),
+ MappedAudioFader::Channels,
+ MappedObjectValue(channels));
+
+ emit instrumentParametersChanged(m_selectedInstrument->getId());
+
+}
+
+void
+AudioInstrumentParameterPanel::slotAudioRoutingChanged()
+{
+ if (m_selectedInstrument)
+ emit instrumentParametersChanged(m_selectedInstrument->getId());
+}
+
+void
+AudioInstrumentParameterPanel::slotSelectPlugin(int index)
+{
+ if (m_selectedInstrument) {
+ emit selectPlugin(0, m_selectedInstrument->getId(), index);
+ }
+}
+
+}
+#include "AudioInstrumentParameterPanel.moc"
diff --git a/src/gui/editors/parameters/AudioInstrumentParameterPanel.h b/src/gui/editors/parameters/AudioInstrumentParameterPanel.h
new file mode 100644
index 0000000..932e6bc
--- /dev/null
+++ b/src/gui/editors/parameters/AudioInstrumentParameterPanel.h
@@ -0,0 +1,107 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_AUDIOINSTRUMENTPARAMETERPANEL_H_
+#define _RG_AUDIOINSTRUMENTPARAMETERPANEL_H_
+
+#include "base/MidiProgram.h"
+#include "InstrumentParameterPanel.h"
+#include <qpixmap.h>
+#include <qstring.h>
+
+
+class QWidget;
+class QColor;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class Instrument;
+class AudioFaderBox;
+
+
+class AudioInstrumentParameterPanel : public InstrumentParameterPanel
+{
+ Q_OBJECT
+public:
+ AudioInstrumentParameterPanel(RosegardenGUIDoc* doc, QWidget* parent);
+
+ virtual void setupForInstrument(Instrument*);
+
+ // Set the audio meter to a given level for a maximum of
+ // two channels.
+ //
+ void setAudioMeter(float dBleft, float dBright,
+ float recDBleft, float recDBright);
+
+ // Set the button colour
+ //
+ void setButtonColour(int pluginIndex, bool bypassState,
+ const QColor &color);
+
+public slots:
+ // From AudioFaderBox
+ //
+ void slotSelectAudioLevel(float dB);
+ void slotSelectAudioRecordLevel(float dB);
+ void slotAudioChannels(int channels);
+ void slotAudioRoutingChanged();
+ void slotSelectPlugin(int index);
+
+ // From the parameter box clicks
+ void slotSetPan(float pan);
+
+ // From Plugin dialog
+ //
+ void slotPluginSelected(InstrumentId id, int index, int plugin);
+ void slotPluginBypassed(InstrumentId id, int pluginIndex, bool bp);
+
+ void slotSynthButtonClicked();
+ void slotSynthGUIButtonClicked();
+
+signals:
+ void selectPlugin(QWidget *, InstrumentId, int index);
+ void instrumentParametersChanged(InstrumentId);
+ void showPluginGUI(InstrumentId, int index);
+ void changeInstrumentLabel(InstrumentId id, QString label);
+
+protected:
+ //--------------- Data members ---------------------------------
+
+ AudioFaderBox *m_audioFader;
+
+private:
+
+ QPixmap m_monoPixmap;
+ QPixmap m_stereoPixmap;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/parameters/InstrumentParameterBox.cpp b/src/gui/editors/parameters/InstrumentParameterBox.cpp
new file mode 100644
index 0000000..8114e0d
--- /dev/null
+++ b/src/gui/editors/parameters/InstrumentParameterBox.cpp
@@ -0,0 +1,265 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "InstrumentParameterBox.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "AudioInstrumentParameterPanel.h"
+#include "base/Instrument.h"
+#include "base/MidiProgram.h"
+#include "document/RosegardenGUIDoc.h"
+#include "MIDIInstrumentParameterPanel.h"
+#include "RosegardenParameterArea.h"
+#include "RosegardenParameterBox.h"
+#include <ktabwidget.h>
+#include <qfont.h>
+#include <qframe.h>
+#include <qscrollview.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qwidgetstack.h>
+
+
+namespace Rosegarden
+{
+
+InstrumentParameterBox::InstrumentParameterBox(RosegardenGUIDoc *doc,
+ QWidget *parent)
+ : RosegardenParameterBox(i18n("Instrument"),
+ i18n("Instrument Parameters"),
+ parent),
+ m_widgetStack(new QWidgetStack(this)),
+ m_noInstrumentParameters(new QVBox(this)),
+ m_midiInstrumentParameters(new MIDIInstrumentParameterPanel(doc, this)),
+ m_audioInstrumentParameters(new AudioInstrumentParameterPanel(doc, this)),
+ m_selectedInstrument(-1),
+ m_doc(doc),
+ m_lastShowAdditionalControlsArg(false)
+{
+ m_widgetStack->setFont(m_font);
+ m_noInstrumentParameters->setFont(m_font);
+ m_midiInstrumentParameters->setFont(m_font);
+ m_audioInstrumentParameters->setFont(m_font);
+
+ bool contains = false;
+
+ std::vector<InstrumentParameterBox*>::iterator it =
+ instrumentParamBoxes.begin();
+
+ for (; it != instrumentParamBoxes.end(); it++)
+ if ((*it) == this)
+ contains = true;
+
+ if (!contains)
+ instrumentParamBoxes.push_back(this);
+
+ m_widgetStack->addWidget(m_midiInstrumentParameters);
+ m_widgetStack->addWidget(m_audioInstrumentParameters);
+ m_widgetStack->addWidget(m_noInstrumentParameters);
+
+ m_midiInstrumentParameters->adjustSize();
+ m_audioInstrumentParameters->adjustSize();
+ m_noInstrumentParameters->adjustSize();
+
+ connect(m_audioInstrumentParameters, SIGNAL(updateAllBoxes()),
+ this, SLOT(slotUpdateAllBoxes()));
+
+ connect(m_audioInstrumentParameters,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ this,
+ SIGNAL(instrumentParametersChanged(InstrumentId)));
+
+ connect(m_audioInstrumentParameters,
+ SIGNAL(selectPlugin(QWidget *, InstrumentId, int)),
+ this,
+ SIGNAL(selectPlugin(QWidget *, InstrumentId, int)));
+
+ connect(m_audioInstrumentParameters,
+ SIGNAL(showPluginGUI(InstrumentId, int)),
+ this,
+ SIGNAL(showPluginGUI(InstrumentId, int)));
+
+ connect(m_midiInstrumentParameters, SIGNAL(updateAllBoxes()),
+ this, SLOT(slotUpdateAllBoxes()));
+
+ connect(m_midiInstrumentParameters,
+ SIGNAL(changeInstrumentLabel(InstrumentId, QString)),
+ this, SIGNAL(changeInstrumentLabel(InstrumentId, QString)));
+
+ connect(m_audioInstrumentParameters,
+ SIGNAL(changeInstrumentLabel(InstrumentId, QString)),
+ this, SIGNAL(changeInstrumentLabel(InstrumentId, QString)));
+
+ connect(m_midiInstrumentParameters,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ this,
+ SIGNAL(instrumentParametersChanged(InstrumentId)));
+
+ // Layout the groups left to right.
+
+ QBoxLayout* layout = new QVBoxLayout(this);
+ layout->addWidget(m_widgetStack);
+
+}
+
+InstrumentParameterBox::~InstrumentParameterBox()
+{
+ // deregister this parameter box
+ std::vector<InstrumentParameterBox*>::iterator it =
+ instrumentParamBoxes.begin();
+
+ for (; it != instrumentParamBoxes.end(); it++) {
+ if ((*it) == this) {
+ instrumentParamBoxes.erase(it);
+ break;
+ }
+ }
+}
+
+Instrument *
+InstrumentParameterBox::getSelectedInstrument()
+{
+ if (m_selectedInstrument < 0) return 0;
+ if (!m_doc) return 0;
+ return m_doc->getStudio().getInstrumentById(m_selectedInstrument);
+}
+
+QString
+InstrumentParameterBox::getPreviousBox(RosegardenParameterArea::Arrangement arrangement) const
+{
+ return i18n("Track");
+}
+
+void
+InstrumentParameterBox::setAudioMeter(float ch1, float ch2, float ch1r, float ch2r)
+{
+ m_audioInstrumentParameters->setAudioMeter(ch1, ch2, ch1r, ch2r);
+}
+
+void
+InstrumentParameterBox::setDocument(RosegardenGUIDoc* doc)
+{
+ m_doc = doc;
+ m_midiInstrumentParameters->setDocument(m_doc);
+ m_audioInstrumentParameters->setDocument(m_doc);
+}
+
+void
+InstrumentParameterBox::slotPluginSelected(InstrumentId id, int index, int plugin)
+{
+ m_audioInstrumentParameters->slotPluginSelected(id, index, plugin);
+}
+
+void
+InstrumentParameterBox::slotPluginBypassed(InstrumentId id, int index, bool bypassState)
+{
+ m_audioInstrumentParameters->slotPluginBypassed(id, index, bypassState);
+}
+
+void
+InstrumentParameterBox::useInstrument(Instrument *instrument)
+{
+ RG_DEBUG << "useInstrument() - populate Instrument\n";
+
+ if (instrument == 0) {
+ m_widgetStack->raiseWidget(m_noInstrumentParameters);
+ emit instrumentPercussionSetChanged(instrument);
+ return ;
+ }
+
+ // ok
+ if (instrument) {
+ m_selectedInstrument = instrument->getId();
+ } else {
+ m_selectedInstrument = -1;
+ }
+
+ // Hide or Show according to Instrument type
+ //
+ if (instrument->getType() == Instrument::Audio ||
+ instrument->getType() == Instrument::SoftSynth) {
+
+ m_audioInstrumentParameters->setupForInstrument(getSelectedInstrument());
+ m_widgetStack->raiseWidget(m_audioInstrumentParameters);
+
+ } else { // Midi
+
+ m_midiInstrumentParameters->setupForInstrument(getSelectedInstrument());
+ m_midiInstrumentParameters->showAdditionalControls(m_lastShowAdditionalControlsArg);
+ m_widgetStack->raiseWidget(m_midiInstrumentParameters);
+ emit instrumentPercussionSetChanged(instrument);
+
+ }
+
+}
+
+void
+InstrumentParameterBox::slotUpdateAllBoxes()
+{
+ emit instrumentPercussionSetChanged(getSelectedInstrument());
+
+ std::vector<InstrumentParameterBox*>::iterator it =
+ instrumentParamBoxes.begin();
+
+ // To update all open IPBs
+ //
+ for (; it != instrumentParamBoxes.end(); it++) {
+ if ((*it) != this && getSelectedInstrument() &&
+ (*it)->getSelectedInstrument() == getSelectedInstrument())
+ (*it)->useInstrument(getSelectedInstrument());
+ }
+}
+
+void
+InstrumentParameterBox::slotInstrumentParametersChanged(InstrumentId id)
+{
+ std::vector<InstrumentParameterBox*>::iterator it =
+ instrumentParamBoxes.begin();
+
+ blockSignals(true);
+
+ for (; it != instrumentParamBoxes.end(); it++) {
+ if ((*it)->getSelectedInstrument()) {
+ if ((*it)->getSelectedInstrument()->getId() == id) {
+ (*it)->useInstrument((*it)->getSelectedInstrument()); // refresh
+ }
+ }
+ }
+
+ blockSignals(false);
+}
+
+void
+InstrumentParameterBox::showAdditionalControls(bool showThem)
+{
+ m_midiInstrumentParameters->showAdditionalControls(showThem);
+ m_lastShowAdditionalControlsArg = showThem;
+}
+
+}
+#include "InstrumentParameterBox.moc"
diff --git a/src/gui/editors/parameters/InstrumentParameterBox.h b/src/gui/editors/parameters/InstrumentParameterBox.h
new file mode 100644
index 0000000..f406567
--- /dev/null
+++ b/src/gui/editors/parameters/InstrumentParameterBox.h
@@ -0,0 +1,126 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_INSTRUMENTPARAMETERBOX_H_
+#define _RG_INSTRUMENTPARAMETERBOX_H_
+
+#include "base/MidiProgram.h"
+#include "RosegardenParameterArea.h"
+#include "RosegardenParameterBox.h"
+#include <qstring.h>
+#include <vector>
+
+
+class QWidgetStack;
+class QWidget;
+class QFrame;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class MIDIInstrumentParameterPanel;
+class Instrument;
+class AudioInstrumentParameterPanel;
+
+
+/**
+ * Display and allow modification of Instrument parameters
+ */
+class InstrumentParameterBox : public RosegardenParameterBox
+{
+Q_OBJECT
+
+public:
+ InstrumentParameterBox(RosegardenGUIDoc *doc,
+ QWidget *parent = 0);
+ ~InstrumentParameterBox();
+
+ void useInstrument(Instrument *instrument);
+
+ Instrument* getSelectedInstrument();
+
+ void setAudioMeter(float dBleft, float dBright,
+ float recDBleft, float recDBright);
+
+ void setDocument(RosegardenGUIDoc* doc);
+
+ virtual void showAdditionalControls(bool showThem);
+
+ virtual QString getPreviousBox(RosegardenParameterArea::Arrangement) const;
+
+public slots:
+
+ // To update all InstrumentParameterBoxen for an Instrument. Called
+ // from one of the parameter panels when something changes.
+ //
+ void slotUpdateAllBoxes();
+
+ // Update InstrumentParameterBoxes that are showing a given instrument.
+ // Called from the Outside.
+ //
+ void slotInstrumentParametersChanged(InstrumentId id);
+
+ // From Plugin dialog
+ //
+ void slotPluginSelected(InstrumentId id, int index, int plugin);
+ void slotPluginBypassed(InstrumentId id, int pluginIndex, bool bp);
+
+signals:
+
+ void changeInstrumentLabel(InstrumentId id, QString label);
+
+ void selectPlugin(QWidget*, InstrumentId id, int index);
+ void showPluginGUI(InstrumentId id, int index);
+
+ void instrumentParametersChanged(InstrumentId);
+ void instrumentPercussionSetChanged(Instrument *);
+
+protected:
+
+ //--------------- Data members ---------------------------------
+ QWidgetStack *m_widgetStack;
+ QFrame *m_noInstrumentParameters;
+ MIDIInstrumentParameterPanel *m_midiInstrumentParameters;
+ AudioInstrumentParameterPanel *m_audioInstrumentParameters;
+
+ // -1 if no instrument, InstrumentId otherwise
+ int m_selectedInstrument;
+
+ // So we can setModified()
+ //
+ RosegardenGUIDoc *m_doc;
+ bool m_lastShowAdditionalControlsArg;
+};
+
+// Global references
+//
+static std::vector<InstrumentParameterBox*> instrumentParamBoxes;
+
+
+}
+
+#endif
diff --git a/src/gui/editors/parameters/InstrumentParameterPanel.cpp b/src/gui/editors/parameters/InstrumentParameterPanel.cpp
new file mode 100644
index 0000000..9437daf
--- /dev/null
+++ b/src/gui/editors/parameters/InstrumentParameterPanel.cpp
@@ -0,0 +1,61 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "InstrumentParameterPanel.h"
+
+#include "base/Instrument.h"
+#include "document/RosegardenGUIDoc.h"
+#include <ksqueezedtextlabel.h>
+#include <qfontmetrics.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+InstrumentParameterPanel::InstrumentParameterPanel(RosegardenGUIDoc *doc,
+ QWidget* parent)
+ : QFrame(parent),
+ m_instrumentLabel(new KSqueezedTextLabel(this)),
+ m_selectedInstrument(0),
+ m_doc(doc)
+{
+ QFontMetrics metrics(m_instrumentLabel->fontMetrics());
+ int width25 = metrics.width("1234567890123456789012345");
+
+ m_instrumentLabel->setFixedWidth(width25);
+ m_instrumentLabel->setAlignment(Qt::AlignCenter);
+}
+
+void
+InstrumentParameterPanel::setDocument(RosegardenGUIDoc* doc)
+{
+ m_doc = doc;
+}
+
+}
+#include "InstrumentParameterPanel.moc"
diff --git a/src/gui/editors/parameters/InstrumentParameterPanel.h b/src/gui/editors/parameters/InstrumentParameterPanel.h
new file mode 100644
index 0000000..9a794d0
--- /dev/null
+++ b/src/gui/editors/parameters/InstrumentParameterPanel.h
@@ -0,0 +1,78 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_INSTRUMENTPARAMETERPANEL_H_
+#define _RG_INSTRUMENTPARAMETERPANEL_H_
+
+#include <qframe.h>
+#include <vector>
+#include <utility>
+
+class QWidget;
+class QLabel;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class Instrument;
+class Rotary;
+
+typedef std::pair<Rotary *, QLabel *> RotaryPair;
+typedef std::vector<std::pair<int, RotaryPair> > RotaryMap;
+
+
+////////////////////////////////////////////////////////////////////////
+
+class InstrumentParameterPanel : public QFrame
+{
+ Q_OBJECT
+public:
+ InstrumentParameterPanel(RosegardenGUIDoc *doc, QWidget* parent);
+
+ virtual ~InstrumentParameterPanel() {};
+
+ virtual void setupForInstrument(Instrument*) = 0;
+
+ void setDocument(RosegardenGUIDoc* doc);
+
+ void showAdditionalControls(bool showThem);
+
+signals:
+ void updateAllBoxes();
+
+protected:
+ //--------------- Data members ---------------------------------
+ QLabel *m_instrumentLabel;
+ Instrument *m_selectedInstrument;
+ RosegardenGUIDoc *m_doc;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/parameters/MIDIInstrumentParameterPanel.cpp b/src/gui/editors/parameters/MIDIInstrumentParameterPanel.cpp
new file mode 100644
index 0000000..fcd4247
--- /dev/null
+++ b/src/gui/editors/parameters/MIDIInstrumentParameterPanel.cpp
@@ -0,0 +1,1175 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MIDIInstrumentParameterPanel.h"
+#include <qlayout.h>
+
+#include "sound/Midi.h"
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Colour.h"
+#include "base/Composition.h"
+#include "base/ControlParameter.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/MidiProgram.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/studio/StudioControl.h"
+#include "gui/widgets/Rotary.h"
+#include "InstrumentParameterPanel.h"
+#include "sound/MappedEvent.h"
+#include "sound/MappedInstrument.h"
+#include <kcombobox.h>
+#include <ksqueezedtextlabel.h>
+#include <qcheckbox.h>
+#include <qcolor.h>
+#include <qfontmetrics.h>
+#include <qframe.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qregexp.h>
+#include <qsignalmapper.h>
+#include <qstring.h>
+#include <qwidget.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+MIDIInstrumentParameterPanel::MIDIInstrumentParameterPanel(RosegardenGUIDoc *doc, QWidget* parent):
+ InstrumentParameterPanel(doc, parent),
+ m_rotaryFrame(0),
+ m_rotaryMapper(new QSignalMapper(this))
+{
+ m_mainGrid = new QGridLayout(this, 10, 3, 2, 1);
+
+ m_connectionLabel = new KSqueezedTextLabel(this);
+ m_bankValue = new KComboBox(this);
+ m_channelValue = new KComboBox(this);
+ m_programValue = new KComboBox(this);
+ m_variationValue = new KComboBox(this);
+ m_bankCheckBox = new QCheckBox(this);
+ m_programCheckBox = new QCheckBox(this);
+ m_variationCheckBox = new QCheckBox(this);
+ m_percussionCheckBox = new QCheckBox(this);
+
+ m_bankValue->setSizeLimit(20);
+ m_programValue->setSizeLimit(20);
+ m_variationValue->setSizeLimit(20);
+
+ m_bankLabel = new QLabel(i18n("Bank"), this);
+ m_variationLabel = new QLabel(i18n("Variation"), this);
+ m_programLabel = new QLabel(i18n("Program"), this);
+ QLabel *channelLabel = new QLabel(i18n("Channel out"), this);
+ QLabel *percussionLabel = new QLabel(i18n("Percussion"), this);
+
+ // Ensure a reasonable amount of space in the program dropdowns even
+ // if no instrument initially selected
+ QFontMetrics metrics(m_programValue->font());
+ int width22 = metrics.width("1234567890123456789012");
+ int width25 = metrics.width("1234567890123456789012345");
+
+ m_bankValue->setMinimumWidth(width22);
+ m_programValue->setMinimumWidth(width22);
+ m_variationValue->setMinimumWidth(width22);
+
+ m_connectionLabel->setFixedWidth(width25);
+ m_connectionLabel->setAlignment(Qt::AlignCenter);
+
+ // Configure the empty final row to accomodate any extra vertical space.
+
+ m_mainGrid->setRowStretch(m_mainGrid->numRows() - 1, 1);
+
+
+ m_mainGrid->setColStretch(2, 1);
+
+ m_mainGrid->addMultiCellWidget(m_instrumentLabel, 0, 0, 0, 2, AlignCenter);
+ m_mainGrid->addMultiCellWidget(m_connectionLabel, 1, 1, 0, 2, AlignCenter);
+
+ m_mainGrid->addMultiCellWidget(channelLabel, 2, 2, 0, 1, AlignLeft);
+ m_mainGrid->addWidget(m_channelValue, 2, 2, AlignRight);
+
+ m_mainGrid->addMultiCellWidget(percussionLabel, 3, 3, 0, 1, AlignLeft);
+ m_mainGrid->addWidget(m_percussionCheckBox, 3, 2, AlignRight);
+
+ m_mainGrid->addWidget(m_bankLabel, 4, 0, AlignLeft);
+ m_mainGrid->addWidget(m_bankCheckBox, 4, 1, AlignRight);
+ m_mainGrid->addWidget(m_bankValue, 4, 2, AlignRight);
+
+ m_mainGrid->addWidget(m_programLabel, 5, 0, AlignLeft);
+ m_mainGrid->addWidget(m_programCheckBox, 5, 1, AlignRight);
+ m_mainGrid->addWidget(m_programValue, 5, 2, AlignRight);
+
+ m_mainGrid->addWidget(m_variationLabel, 6, 0);
+ m_mainGrid->addWidget(m_variationCheckBox, 6, 1);
+ m_mainGrid->addWidget(m_variationValue, 6, 2, AlignRight);
+
+ // Populate channel lists
+ //
+ for (int i = 0; i < 16; i++) {
+ m_channelValue->insertItem(QString("%1").arg(i + 1));
+ }
+
+ m_channelValue->setSizeLimit(16);
+
+ // Disable these by default - they are activate by their
+ // checkboxes
+ //
+ m_programValue->setDisabled(true);
+ m_bankValue->setDisabled(true);
+ m_variationValue->setDisabled(true);
+
+ // Only active if we have an Instrument selected
+ //
+ m_percussionCheckBox->setDisabled(true);
+ m_programCheckBox->setDisabled(true);
+ m_bankCheckBox->setDisabled(true);
+ m_variationCheckBox->setDisabled(true);
+
+ // Connect up the toggle boxes
+ //
+ connect(m_percussionCheckBox, SIGNAL(toggled(bool)),
+ this, SLOT(slotTogglePercussion(bool)));
+
+ connect(m_programCheckBox, SIGNAL(toggled(bool)),
+ this, SLOT(slotToggleProgramChange(bool)));
+
+ connect(m_bankCheckBox, SIGNAL(toggled(bool)),
+ this, SLOT(slotToggleBank(bool)));
+
+ connect(m_variationCheckBox, SIGNAL(toggled(bool)),
+ this, SLOT(slotToggleVariation(bool)));
+
+
+ // Connect activations
+ //
+ connect(m_bankValue, SIGNAL(activated(int)),
+ this, SLOT(slotSelectBank(int)));
+
+ connect(m_variationValue, SIGNAL(activated(int)),
+ this, SLOT(slotSelectVariation(int)));
+
+ connect(m_programValue, SIGNAL(activated(int)),
+ this, SLOT(slotSelectProgram(int)));
+
+ connect(m_channelValue, SIGNAL(activated(int)),
+ this, SLOT(slotSelectChannel(int)));
+
+ // don't select any of the options in any dropdown
+ m_programValue->setCurrentItem( -1);
+ m_bankValue->setCurrentItem( -1);
+ m_channelValue->setCurrentItem( -1);
+ m_variationValue->setCurrentItem( -1);
+
+ connect(m_rotaryMapper, SIGNAL(mapped(int)),
+ this, SLOT(slotControllerChanged(int)));
+}
+
+void
+MIDIInstrumentParameterPanel::setupForInstrument(Instrument *instrument)
+{
+ RG_DEBUG << "MIDIInstrumentParameterPanel::setupForInstrument" << endl;
+ MidiDevice *md = dynamic_cast<MidiDevice*>
+ (instrument->getDevice());
+ if (!md) {
+ RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::setupForInstrument:"
+ << " No MidiDevice for Instrument "
+ << instrument->getId() << endl;
+ return ;
+ }
+
+ m_selectedInstrument = instrument;
+
+ // Set instrument name
+ //
+ m_instrumentLabel->setText(strtoqstr(instrument->getPresentationName()));
+
+ // Set Studio Device name
+ //
+ QString connection(strtoqstr(md->getConnection()));
+ if (connection == "") {
+ m_connectionLabel->setText(i18n("[ %1 ]").arg(i18n("No connection")));
+ } else {
+
+ // remove trailing "(duplex)", "(read only)", "(write only)" etc
+ connection.replace(QRegExp("\\s*\\([^)0-9]+\\)\\s*$"), "");
+
+ QString text = i18n("[ %1 ]").arg(connection);
+ /*QString origText(text);
+
+ QFontMetrics metrics(m_connectionLabel->fontMetrics());
+ int maxwidth = metrics.width
+ ("Program: [X] Acoustic Grand Piano 123");// kind of arbitrary!
+
+ int hlen = text.length() / 2;
+ while (metrics.width(text) > maxwidth && text.length() > 10) {
+ --hlen;
+ text = origText.left(hlen) + "..." + origText.right(hlen);
+ }
+
+ if (text.length() > origText.length() - 7) text = origText;*/
+ m_connectionLabel->setText(text);
+ }
+
+ // Enable all check boxes
+ //
+ m_percussionCheckBox->setDisabled(false);
+ m_programCheckBox->setDisabled(false);
+ m_bankCheckBox->setDisabled(false);
+ m_variationCheckBox->setDisabled(false);
+
+ // Activate all checkboxes
+ //
+ m_percussionCheckBox->setChecked(instrument->isPercussion());
+ m_programCheckBox->setChecked(instrument->sendsProgramChange());
+ m_bankCheckBox->setChecked(instrument->sendsBankSelect());
+ m_variationCheckBox->setChecked(instrument->sendsBankSelect());
+
+ // Basic parameters
+ //
+ m_channelValue->setCurrentItem((int)instrument->getMidiChannel());
+
+ // Check for program change
+ //
+ populateBankList();
+ populateProgramList();
+ populateVariationList();
+
+ // Setup the ControlParameters
+ //
+ setupControllers(md);
+
+ // Set all the positions by controller number
+ //
+ for (RotaryMap::iterator it = m_rotaries.begin() ;
+ it != m_rotaries.end(); ++it) {
+ MidiByte value = 0;
+
+ // Special cases
+ //
+ if (it->first == MIDI_CONTROLLER_PAN)
+ value = int(instrument->getPan());
+ else if (it->first == MIDI_CONTROLLER_VOLUME)
+ value = int(instrument->getVolume());
+ else {
+ try {
+ value = instrument->getControllerValue(
+ MidiByte(it->first));
+ } catch (...) {
+ continue;
+ }
+ }
+
+ setRotaryToValue(it->first, int(value));
+ }
+}
+
+void
+MIDIInstrumentParameterPanel::setupControllers(MidiDevice *md)
+{
+ if (!m_rotaryFrame) {
+ m_rotaryFrame = new QFrame(this);
+ m_mainGrid->addMultiCellWidget(m_rotaryFrame, 8, 8, 0, 2, Qt::AlignHCenter);
+ m_rotaryGrid = new QGridLayout(m_rotaryFrame, 10, 3, 8, 1);
+ m_rotaryGrid->addItem(new QSpacerItem(10, 4), 0, 1);
+ }
+
+ // To cut down on flicker, we avoid destroying and recreating
+ // widgets as far as possible here. If a label already exists,
+ // we just set its text; if a rotary exists, we only replace it
+ // if we actually need a different one.
+
+ Composition &comp = m_doc->getComposition();
+ ControlList list = md->getControlParameters();
+
+ // sort by IPB position
+ //
+ std::sort(list.begin(), list.end(),
+ ControlParameter::ControlPositionCmp());
+
+ int count = 0;
+ RotaryMap::iterator rmi = m_rotaries.begin();
+
+ for (ControlList::iterator it = list.begin();
+ it != list.end(); ++it) {
+ if (it->getIPBPosition() == -1)
+ continue;
+
+ // Get the knob colour - only if the colour is non-default (>0)
+ //
+ QColor knobColour = Qt::black; // special case for Rotary
+ if (it->getColourIndex() > 0) {
+ Colour c =
+ comp.getGeneralColourMap().getColourByIndex
+ (it->getColourIndex());
+ knobColour = QColor(c.getRed(), c.getGreen(), c.getBlue());
+ }
+
+ Rotary *rotary = 0;
+
+ if (rmi != m_rotaries.end()) {
+
+ // Update the controller number that is associated with the
+ // existing rotary widget.
+
+ rmi->first = it->getControllerValue();
+
+ // Update the properties of the existing rotary widget.
+
+ rotary = rmi->second.first;
+ int redraw = 0; // 1 -> position, 2 -> all
+
+ if (rotary->getMinValue() != it->getMin()) {
+ rotary->setMinValue(it->getMin());
+ redraw = 1;
+ }
+ if (rotary->getMaxValue() != it->getMax()) {
+ rotary->setMaxValue(it->getMax());
+ redraw = 1;
+ }
+ if (rotary->getKnobColour() != knobColour) {
+ rotary->setKnobColour(knobColour);
+ redraw = 2;
+ }
+ if (redraw == 1 || rotary->getPosition() != it->getDefault()) {
+ rotary->setPosition(it->getDefault());
+ if (redraw == 1)
+ redraw = 0;
+ }
+ if (redraw == 2) {
+ rotary->repaint();
+ }
+
+ // Update the controller name that is associated with
+ // with the existing rotary widget.
+
+ QLabel *label = rmi->second.second;
+ label->setText(strtoqstr(it->getName()));
+
+ ++rmi;
+
+ } else {
+
+ QHBox *hbox = new QHBox(m_rotaryFrame);
+ hbox->setSpacing(8);
+
+ float smallStep = 1.0;
+
+ float bigStep = 5.0;
+ if (it->getMax() - it->getMin() < 10)
+ bigStep = 1.0;
+ else if (it->getMax() - it->getMin() < 20)
+ bigStep = 2.0;
+
+ rotary = new Rotary
+ (hbox,
+ it->getMin(),
+ it->getMax(),
+ smallStep,
+ bigStep,
+ it->getDefault(),
+ 20,
+ Rotary::NoTicks,
+ false,
+ it->getDefault() == 64); //!!! hacky
+
+ rotary->setKnobColour(knobColour);
+
+ // Add a label
+ QLabel *label = new KSqueezedTextLabel(strtoqstr(it->getName()), hbox);
+
+ RG_DEBUG << "Adding new widget at " << (count / 2) << "," << (count % 2) << endl;
+
+ // Add the compound widget
+ //
+ m_rotaryGrid->addWidget(hbox, count / 2, (count % 2) * 2, AlignLeft);
+ hbox->show();
+
+ // Add to list
+ //
+ m_rotaries.push_back(std::pair<int, RotaryPair>
+ (it->getControllerValue(),
+ RotaryPair(rotary, label)));
+
+ // Connect
+ //
+ connect(rotary, SIGNAL(valueChanged(float)),
+ m_rotaryMapper, SLOT(map()));
+
+ rmi = m_rotaries.end();
+ }
+
+ // Add signal mapping
+ //
+ m_rotaryMapper->setMapping(rotary,
+ int(it->getControllerValue()));
+
+ count++;
+ }
+
+ if (rmi != m_rotaries.end()) {
+ for (RotaryMap::iterator rmj = rmi; rmj != m_rotaries.end(); ++rmj) {
+ delete rmj->second.first;
+ delete rmj->second.second;
+ }
+ m_rotaries = std::vector<std::pair<int, RotaryPair> >
+ (m_rotaries.begin(), rmi);
+ }
+
+ m_rotaryFrame->show();
+}
+
+void
+MIDIInstrumentParameterPanel::setRotaryToValue(int controller, int value)
+{
+ /*
+ RG_DEBUG << "MIDIInstrumentParameterPanel::setRotaryToValue - "
+ << "controller = " << controller
+ << ", value = " << value << std::endl;
+ */
+
+ for (RotaryMap::iterator it = m_rotaries.begin() ; it != m_rotaries.end(); ++it) {
+ if (it->first == controller) {
+ it->second.first->setPosition(float(value));
+ return ;
+ }
+ }
+}
+
+void
+MIDIInstrumentParameterPanel::slotSelectChannel(int index)
+{
+ if (m_selectedInstrument == 0)
+ return ;
+
+ m_selectedInstrument->setMidiChannel(index);
+
+ // don't use the emit - use this method instead
+ StudioControl::sendMappedInstrument(
+ MappedInstrument(m_selectedInstrument));
+ emit updateAllBoxes();
+}
+
+void
+MIDIInstrumentParameterPanel::populateBankList()
+{
+ if (m_selectedInstrument == 0)
+ return ;
+
+ m_bankValue->clear();
+ m_banks.clear();
+
+ MidiDevice *md = dynamic_cast<MidiDevice*>
+ (m_selectedInstrument->getDevice());
+ if (!md) {
+ RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::populateBankList:"
+ << " No MidiDevice for Instrument "
+ << m_selectedInstrument->getId() << endl;
+ return ;
+ }
+
+ int currentBank = -1;
+ BankList banks;
+
+ /*
+ RG_DEBUG << "MIDIInstrumentParameterPanel::populateBankList: "
+ << "variation type is " << md->getVariationType() << endl;
+ */
+
+ if (md->getVariationType() == MidiDevice::NoVariations) {
+
+ banks = md->getBanks(m_selectedInstrument->isPercussion());
+
+ if (!banks.empty()) {
+ if (m_bankLabel->isHidden()) {
+ m_bankLabel->show();
+ m_bankCheckBox->show();
+ m_bankValue->show();
+ }
+ } else {
+ m_bankLabel->hide();
+ m_bankCheckBox->hide();
+ m_bankValue->hide();
+ }
+
+ for (unsigned int i = 0; i < banks.size(); ++i) {
+ if (m_selectedInstrument->getProgram().getBank() == banks[i]) {
+ currentBank = i;
+ }
+ }
+
+ } else {
+
+ MidiByteList bytes;
+ bool useMSB = (md->getVariationType() == MidiDevice::VariationFromLSB);
+
+ if (useMSB) {
+ bytes = md->getDistinctMSBs(m_selectedInstrument->isPercussion());
+ } else {
+ bytes = md->getDistinctLSBs(m_selectedInstrument->isPercussion());
+ }
+
+ if (bytes.size() < 2) {
+ if (!m_bankLabel->isHidden()) {
+ m_bankLabel->hide();
+ m_bankCheckBox->hide();
+ m_bankValue->hide();
+ }
+ } else {
+ if (m_bankLabel->isHidden()) {
+ m_bankLabel->show();
+ m_bankCheckBox->show();
+ m_bankValue->show();
+ }
+ }
+
+ if (useMSB) {
+ for (unsigned int i = 0; i < bytes.size(); ++i) {
+ BankList bl = md->getBanksByMSB
+ (m_selectedInstrument->isPercussion(), bytes[i]);
+ RG_DEBUG << "MIDIInstrumentParameterPanel::populateBankList: have " << bl.size() << " variations for msb " << bytes[i] << endl;
+
+ if (bl.size() == 0)
+ continue;
+ if (m_selectedInstrument->getMSB() == bytes[i]) {
+ currentBank = banks.size();
+ }
+ banks.push_back(bl[0]);
+ }
+ } else {
+ for (unsigned int i = 0; i < bytes.size(); ++i) {
+ BankList bl = md->getBanksByLSB
+ (m_selectedInstrument->isPercussion(), bytes[i]);
+ RG_DEBUG << "MIDIInstrumentParameterPanel::populateBankList: have " << bl.size() << " variations for lsb " << bytes[i] << endl;
+ if (bl.size() == 0)
+ continue;
+ if (m_selectedInstrument->getLSB() == bytes[i]) {
+ currentBank = banks.size();
+ }
+ banks.push_back(bl[0]);
+ }
+ }
+ }
+
+ for (BankList::const_iterator i = banks.begin();
+ i != banks.end(); ++i) {
+ m_banks.push_back(*i);
+ m_bankValue->insertItem(strtoqstr(i->getName()));
+ }
+
+ m_bankValue->setEnabled(m_selectedInstrument->sendsBankSelect());
+
+ if (currentBank < 0 && !banks.empty()) {
+ m_bankValue->setCurrentItem(0);
+ slotSelectBank(0);
+ } else {
+ m_bankValue->setCurrentItem(currentBank);
+ }
+}
+
+void
+MIDIInstrumentParameterPanel::populateProgramList()
+{
+ if (m_selectedInstrument == 0)
+ return ;
+
+ m_programValue->clear();
+ m_programs.clear();
+
+ MidiDevice *md = dynamic_cast<MidiDevice*>
+ (m_selectedInstrument->getDevice());
+ if (!md) {
+ RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::populateProgramList: No MidiDevice for Instrument "
+ << m_selectedInstrument->getId() << endl;
+ return ;
+ }
+
+ /*
+ RG_DEBUG << "MIDIInstrumentParameterPanel::populateProgramList:"
+ << " variation type is " << md->getVariationType() << endl;
+ */
+
+ MidiBank bank( m_selectedInstrument->isPercussion(),
+ m_selectedInstrument->getMSB(),
+ m_selectedInstrument->getLSB());
+
+ if (m_selectedInstrument->sendsBankSelect()) {
+ bank = m_selectedInstrument->getProgram().getBank();
+ }
+
+ int currentProgram = -1;
+
+ ProgramList programs = md->getPrograms(bank);
+
+ if (!programs.empty()) {
+ if (m_programLabel->isHidden()) {
+ m_programLabel->show();
+ m_programCheckBox->show();
+ m_programValue->show();
+ }
+ } else {
+ m_programLabel->hide();
+ m_programCheckBox->hide();
+ m_programValue->hide();
+ }
+
+ for (unsigned int i = 0; i < programs.size(); ++i) {
+ std::string programName = programs[i].getName();
+ if (programName != "") {
+ m_programValue->insertItem(QString("%1. %2")
+ .arg(programs[i].getProgram() + 1)
+ .arg(strtoqstr(programName)));
+ if (m_selectedInstrument->getProgram() == programs[i]) {
+ currentProgram = m_programs.size();
+ }
+ m_programs.push_back(programs[i]);
+ }
+ }
+
+ m_programValue->setEnabled(m_selectedInstrument->sendsProgramChange());
+
+ if (currentProgram < 0 && !m_programs.empty()) {
+ m_programValue->setCurrentItem(0);
+ slotSelectProgram(0);
+ } else {
+ m_programValue->setCurrentItem(currentProgram);
+
+ // Ensure that stored program change value is same as the one
+ // we're now showing (BUG 937371)
+ //
+ if (!m_programs.empty()) {
+ m_selectedInstrument->setProgramChange
+ ((m_programs[m_programValue->currentItem()]).getProgram());
+ }
+ }
+}
+
+void
+MIDIInstrumentParameterPanel::populateVariationList()
+{
+ if (m_selectedInstrument == 0)
+ return ;
+
+ m_variationValue->clear();
+ m_variations.clear();
+
+ MidiDevice *md = dynamic_cast<MidiDevice*>
+ (m_selectedInstrument->getDevice());
+ if (!md) {
+ RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::populateVariationList: No MidiDevice for Instrument "
+ << m_selectedInstrument->getId() << endl;
+ return ;
+ }
+
+ /*
+ RG_DEBUG << "MIDIInstrumentParameterPanel::populateVariationList:"
+ << " variation type is " << md->getVariationType() << endl;
+ */
+
+ if (md->getVariationType() == MidiDevice::NoVariations) {
+ if (!m_variationLabel->isHidden()) {
+ m_variationLabel->hide();
+ m_variationCheckBox->hide();
+ m_variationValue->hide();
+ }
+ return ;
+ }
+
+ bool useMSB = (md->getVariationType() == MidiDevice::VariationFromMSB);
+ MidiByteList variations;
+
+ if (useMSB) {
+ MidiByte lsb = m_selectedInstrument->getLSB();
+ variations = md->getDistinctMSBs(m_selectedInstrument->isPercussion(),
+ lsb);
+ RG_DEBUG << "MIDIInstrumentParameterPanel::populateVariationList: have " << variations.size() << " variations for lsb " << lsb << endl;
+
+ } else {
+ MidiByte msb = m_selectedInstrument->getMSB();
+ variations = md->getDistinctLSBs(m_selectedInstrument->isPercussion(),
+ msb);
+ RG_DEBUG << "MIDIInstrumentParameterPanel::populateVariationList: have " << variations.size() << " variations for msb " << msb << endl;
+ }
+
+ m_variationValue->setCurrentItem( -1);
+
+ MidiProgram defaultProgram;
+
+ if (useMSB) {
+ defaultProgram = MidiProgram
+ (MidiBank(m_selectedInstrument->isPercussion(),
+ 0,
+ m_selectedInstrument->getLSB()),
+ m_selectedInstrument->getProgramChange());
+ } else {
+ defaultProgram = MidiProgram
+ (MidiBank(m_selectedInstrument->isPercussion(),
+ m_selectedInstrument->getMSB(),
+ 0),
+ m_selectedInstrument->getProgramChange());
+ }
+ std::string defaultProgramName = md->getProgramName(defaultProgram);
+
+ int currentVariation = -1;
+
+ for (unsigned int i = 0; i < variations.size(); ++i) {
+
+ MidiProgram program;
+
+ if (useMSB) {
+ program = MidiProgram
+ (MidiBank(m_selectedInstrument->isPercussion(),
+ variations[i],
+ m_selectedInstrument->getLSB()),
+ m_selectedInstrument->getProgramChange());
+ } else {
+ program = MidiProgram
+ (MidiBank(m_selectedInstrument->isPercussion(),
+ m_selectedInstrument->getMSB(),
+ variations[i]),
+ m_selectedInstrument->getProgramChange());
+ }
+
+ std::string programName = md->getProgramName(program);
+
+ if (programName != "") { // yes, that is how you know whether it exists
+ /*
+ m_variationValue->insertItem(programName == defaultProgramName ?
+ i18n("(default)") :
+ strtoqstr(programName));
+ */
+ m_variationValue->insertItem(QString("%1. %2")
+ .arg(variations[i] + 1)
+ .arg(strtoqstr(programName)));
+ if (m_selectedInstrument->getProgram() == program) {
+ currentVariation = m_variations.size();
+ }
+ m_variations.push_back(variations[i]);
+ }
+ }
+
+ if (currentVariation < 0 && !m_variations.empty()) {
+ m_variationValue->setCurrentItem(0);
+ slotSelectVariation(0);
+ } else {
+ m_variationValue->setCurrentItem(currentVariation);
+ }
+
+ if (m_variations.size() < 2) {
+ if (!m_variationLabel->isHidden()) {
+ m_variationLabel->hide();
+ m_variationCheckBox->hide();
+ m_variationValue->hide();
+ }
+
+ } else {
+ //!!! seem to have problems here -- the grid layout doesn't
+ //like us adding stuff in the middle so if we go from 1
+ //visible row (say program) to 2 (program + variation) the
+ //second one overlaps the control knobs
+
+ if (m_variationLabel->isHidden()) {
+ m_variationLabel->show();
+ m_variationCheckBox->show();
+ m_variationValue->show();
+ }
+
+ if (m_programValue->width() > m_variationValue->width()) {
+ m_variationValue->setMinimumWidth(m_programValue->width());
+ } else {
+ m_programValue->setMinimumWidth(m_variationValue->width());
+ }
+ }
+
+ m_variationValue->setEnabled(m_selectedInstrument->sendsBankSelect());
+}
+
+void
+MIDIInstrumentParameterPanel::slotTogglePercussion(bool value)
+{
+ if (m_selectedInstrument == 0) {
+ m_percussionCheckBox->setChecked(false);
+ emit updateAllBoxes();
+ return ;
+ }
+
+ m_selectedInstrument->setPercussion(value);
+
+ populateBankList();
+ populateProgramList();
+ populateVariationList();
+
+ sendBankAndProgram();
+
+ emit changeInstrumentLabel(m_selectedInstrument->getId(),
+ strtoqstr(m_selectedInstrument->
+ getProgramName()));
+ emit updateAllBoxes();
+
+ emit instrumentParametersChanged(m_selectedInstrument->getId());
+}
+
+void
+MIDIInstrumentParameterPanel::slotToggleBank(bool value)
+{
+ if (m_selectedInstrument == 0) {
+ m_bankCheckBox->setChecked(false);
+ emit updateAllBoxes();
+ return ;
+ }
+
+ m_variationCheckBox->setChecked(value);
+ m_selectedInstrument->setSendBankSelect(value);
+
+ m_bankValue->setDisabled(!value);
+ populateBankList();
+ populateProgramList();
+ populateVariationList();
+
+ sendBankAndProgram();
+
+ emit changeInstrumentLabel(m_selectedInstrument->getId(),
+ strtoqstr(m_selectedInstrument->
+ getProgramName()));
+ emit updateAllBoxes();
+
+ emit instrumentParametersChanged(m_selectedInstrument->getId());
+}
+
+void
+MIDIInstrumentParameterPanel::slotToggleProgramChange(bool value)
+{
+ if (m_selectedInstrument == 0) {
+ m_programCheckBox->setChecked(false);
+ emit updateAllBoxes();
+ return ;
+ }
+
+ m_selectedInstrument->setSendProgramChange(value);
+
+ m_programValue->setDisabled(!value);
+ populateProgramList();
+ populateVariationList();
+
+ if (value)
+ sendBankAndProgram();
+
+ emit changeInstrumentLabel(m_selectedInstrument->getId(),
+ strtoqstr(m_selectedInstrument->
+ getProgramName()));
+ emit updateAllBoxes();
+
+ emit instrumentParametersChanged(m_selectedInstrument->getId());
+}
+
+void
+MIDIInstrumentParameterPanel::slotToggleVariation(bool value)
+{
+ if (m_selectedInstrument == 0) {
+ m_variationCheckBox->setChecked(false);
+ emit updateAllBoxes();
+ return ;
+ }
+
+ m_bankCheckBox->setChecked(value);
+ m_selectedInstrument->setSendBankSelect(value);
+
+ m_variationValue->setDisabled(!value);
+ populateVariationList();
+
+ sendBankAndProgram();
+
+ emit changeInstrumentLabel(m_selectedInstrument->getId(),
+ strtoqstr(m_selectedInstrument->
+ getProgramName()));
+ emit updateAllBoxes();
+
+ emit instrumentParametersChanged(m_selectedInstrument->getId());
+}
+
+void
+MIDIInstrumentParameterPanel::slotSelectBank(int index)
+{
+ if (m_selectedInstrument == 0)
+ return ;
+
+ MidiDevice *md = dynamic_cast<MidiDevice*>
+ (m_selectedInstrument->getDevice());
+ if (!md) {
+ RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::slotSelectBank: No MidiDevice for Instrument "
+ << m_selectedInstrument->getId() << endl;
+ return ;
+ }
+
+ const MidiBank *bank = &m_banks[index];
+
+ bool change = false;
+
+ if (md->getVariationType() != MidiDevice::VariationFromLSB) {
+ if (m_selectedInstrument->getLSB() != bank->getLSB()) {
+ m_selectedInstrument->setLSB(bank->getLSB());
+ change = true;
+ }
+ }
+ if (md->getVariationType() != MidiDevice::VariationFromMSB) {
+ if (m_selectedInstrument->getMSB() != bank->getMSB()) {
+ m_selectedInstrument->setMSB(bank->getMSB());
+ change = true;
+ }
+ }
+
+ populateProgramList();
+
+ if (change) {
+ sendBankAndProgram();
+ emit updateAllBoxes();
+ }
+
+ emit instrumentParametersChanged(m_selectedInstrument->getId());
+}
+
+void
+MIDIInstrumentParameterPanel::slotSelectProgram(int index)
+{
+ const MidiProgram *prg = &m_programs[index];
+ if (prg == 0) {
+ RG_DEBUG << "program change not found in bank" << endl;
+ return ;
+ }
+
+ bool change = false;
+ if (m_selectedInstrument->getProgramChange() != prg->getProgram()) {
+ m_selectedInstrument->setProgramChange(prg->getProgram());
+ change = true;
+ }
+
+ populateVariationList();
+
+ if (change) {
+ sendBankAndProgram();
+ emit changeInstrumentLabel(m_selectedInstrument->getId(),
+ strtoqstr(m_selectedInstrument->
+ getProgramName()));
+ emit updateAllBoxes();
+ }
+
+ emit instrumentParametersChanged(m_selectedInstrument->getId());
+}
+
+void
+MIDIInstrumentParameterPanel::slotSelectVariation(int index)
+{
+ MidiDevice *md = dynamic_cast<MidiDevice*>
+ (m_selectedInstrument->getDevice());
+ if (!md) {
+ RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::slotSelectVariation: No MidiDevice for Instrument "
+ << m_selectedInstrument->getId() << endl;
+ return ;
+ }
+
+ if (index < 0 || index > int(m_variations.size())) {
+ RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::slotSelectVariation: index " << index << " out of range" << endl;
+ return ;
+ }
+
+ MidiByte v = m_variations[index];
+
+ bool change = false;
+
+ if (md->getVariationType() == MidiDevice::VariationFromLSB) {
+ if (m_selectedInstrument->getLSB() != v) {
+ m_selectedInstrument->setLSB(v);
+ change = true;
+ }
+ } else if (md->getVariationType() == MidiDevice::VariationFromMSB) {
+ if (m_selectedInstrument->getMSB() != v) {
+ m_selectedInstrument->setMSB(v);
+ change = true;
+ }
+ }
+
+ if (change) {
+ sendBankAndProgram();
+ }
+
+ emit instrumentParametersChanged(m_selectedInstrument->getId());
+}
+
+void
+MIDIInstrumentParameterPanel::sendBankAndProgram()
+{
+ if (m_selectedInstrument == 0)
+ return ;
+
+ MidiDevice *md = dynamic_cast<MidiDevice*>
+ (m_selectedInstrument->getDevice());
+ if (!md) {
+ RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::sendBankAndProgram: No MidiDevice for Instrument "
+ << m_selectedInstrument->getId() << endl;
+ return ;
+ }
+
+ if (m_selectedInstrument->sendsBankSelect()) {
+
+ // Send the bank select message before any PC message
+ //
+ MappedEvent mEMSB(m_selectedInstrument->getId(),
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_BANK_MSB,
+ m_selectedInstrument->getMSB());
+
+ RG_DEBUG << "MIDIInstrumentParameterPanel::sendBankAndProgram - "
+ << "sending MSB = "
+ << int(m_selectedInstrument->getMSB())
+ << endl;
+
+ StudioControl::sendMappedEvent(mEMSB);
+
+ MappedEvent mELSB(m_selectedInstrument->getId(),
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_BANK_LSB,
+ m_selectedInstrument->getLSB());
+
+ RG_DEBUG << "MIDIInstrumentParameterPanel::sendBankAndProgram - "
+ << "sending LSB = "
+ << int(m_selectedInstrument->getLSB())
+ << endl;
+
+ StudioControl::sendMappedEvent(mELSB);
+ }
+
+ MappedEvent mE(m_selectedInstrument->getId(),
+ MappedEvent::MidiProgramChange,
+ m_selectedInstrument->getProgramChange(),
+ (MidiByte)0);
+
+ RG_DEBUG << "MIDIInstrumentParameterPanel::sendBankAndProgram - "
+ << "sending program change = "
+ << int(m_selectedInstrument->getProgramChange())
+ << endl;
+
+
+ // Send the controller change
+ //
+ StudioControl::sendMappedEvent(mE);
+}
+
+void
+MIDIInstrumentParameterPanel::slotControllerChanged(int controllerNumber)
+{
+
+ RG_DEBUG << "MIDIInstrumentParameterPanel::slotControllerChanged - "
+ << "controller = " << controllerNumber << "\n";
+
+
+ if (m_selectedInstrument == 0)
+ return ;
+
+ MidiDevice *md = dynamic_cast<MidiDevice*>
+ (m_selectedInstrument->getDevice());
+ if (!md)
+ return ;
+
+ /*
+ ControlParameter *controller =
+ md->getControlParameter(MidiByte(controllerNumber));
+ */
+
+ int value = getValueFromRotary(controllerNumber);
+
+ if (value == -1) {
+ RG_DEBUG << "MIDIInstrumentParameterPanel::slotControllerChanged - "
+ << "couldn't get value of rotary for controller "
+ << controllerNumber << endl;
+ return ;
+ }
+
+
+ // two special cases
+ if (controllerNumber == int(MIDI_CONTROLLER_PAN)) {
+ float adjValue = value;
+ if (m_selectedInstrument->getType() == Instrument::Audio ||
+ m_selectedInstrument->getType() == Instrument::SoftSynth)
+ value += 100;
+
+ m_selectedInstrument->setPan(MidiByte(adjValue));
+ } else if (controllerNumber == int(MIDI_CONTROLLER_VOLUME)) {
+ m_selectedInstrument->setVolume(MidiByte(value));
+ } else // just set the controller (this will create it on the instrument if
+ // it doesn't exist)
+ {
+ m_selectedInstrument->setControllerValue(MidiByte(controllerNumber),
+ MidiByte(value));
+
+ RG_DEBUG << "SET CONTROLLER VALUE (" << controllerNumber << ") = " << value << endl;
+ }
+ /*
+ else
+ {
+ RG_DEBUG << "MIDIInstrumentParameterPanel::slotControllerChanged - "
+ << "no controller retrieved\n";
+ return;
+ }
+ */
+
+ MappedEvent mE(m_selectedInstrument->getId(),
+ MappedEvent::MidiController,
+ (MidiByte)controllerNumber,
+ (MidiByte)value);
+ StudioControl::sendMappedEvent(mE);
+
+ emit updateAllBoxes();
+ emit instrumentParametersChanged(m_selectedInstrument->getId());
+
+}
+
+int
+MIDIInstrumentParameterPanel::getValueFromRotary(int rotary)
+{
+ for (RotaryMap::iterator it = m_rotaries.begin(); it != m_rotaries.end(); ++it) {
+ if (it->first == rotary)
+ return int(it->second.first->getPosition());
+ }
+
+ return -1;
+}
+
+void
+MIDIInstrumentParameterPanel::showAdditionalControls(bool showThem)
+{
+ m_instrumentLabel->setShown(showThem);
+ int index = 0;
+ for (RotaryMap::iterator it = m_rotaries.begin(); it != m_rotaries.end(); ++it) {
+ it->second.first->parentWidget()->setShown(showThem || (index < 8));
+ //it->second.first->setShown(showThem || (index < 8));
+ //it->second.second->setShown(showThem || (index < 8));
+ index++;
+ }
+}
+
+}
+#include "MIDIInstrumentParameterPanel.moc"
diff --git a/src/gui/editors/parameters/MIDIInstrumentParameterPanel.h b/src/gui/editors/parameters/MIDIInstrumentParameterPanel.h
new file mode 100644
index 0000000..7f1a1c5
--- /dev/null
+++ b/src/gui/editors/parameters/MIDIInstrumentParameterPanel.h
@@ -0,0 +1,137 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MIDIINSTRUMENTPARAMETERPANEL_H_
+#define _RG_MIDIINSTRUMENTPARAMETERPANEL_H_
+
+#include "base/MidiProgram.h"
+#include "base/MidiDevice.h"
+#include "InstrumentParameterPanel.h"
+#include <qstring.h>
+
+
+class QWidget;
+class QSignalMapper;
+class QLabel;
+class QGridLayout;
+class QFrame;
+class QCheckBox;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class MidiDevice;
+class Instrument;
+
+
+class MIDIInstrumentParameterPanel : public InstrumentParameterPanel
+{
+ Q_OBJECT
+public:
+
+ MIDIInstrumentParameterPanel(RosegardenGUIDoc *doc, QWidget* parent);
+
+ void setupControllers(MidiDevice *); // setup ControlParameters on box
+
+ virtual void setupForInstrument(Instrument*);
+
+ void showAdditionalControls(bool showThem);
+
+signals:
+ void changeInstrumentLabel(InstrumentId id, QString label);
+ void instrumentParametersChanged(InstrumentId);
+
+public slots:
+ void slotSelectProgram(int index);
+ void slotSelectBank(int index);
+ void slotSelectVariation(int index);
+ void slotSelectChannel(int index);
+ //void slotSelectInputChannel(int index);
+
+ void slotControllerChanged(int index);
+
+ void slotTogglePercussion(bool value);
+ void slotToggleProgramChange(bool value);
+ void slotToggleBank(bool value);
+ void slotToggleVariation(bool value);
+
+protected:
+
+ // fill (or hide) bank combo based on whether the instrument is percussion
+ void populateBankList();
+
+ // fill program combo based on current bank
+ void populateProgramList();
+
+ // fill (or hide) variation combo based on current bank and program
+ void populateVariationList();
+
+ // send the bank and program events relevant to this instrument
+ void sendBankAndProgram();
+
+ // get value of a specific rotary (keyed by controller value)
+ int getValueFromRotary(int rotary);
+
+ // set rotary to value
+ void setRotaryToValue(int controller, int value);
+
+ //--------------- Data members ---------------------------------
+
+ QLabel *m_connectionLabel;
+
+ KComboBox *m_bankValue;
+ KComboBox *m_variationValue;
+ KComboBox *m_channelValue;
+ KComboBox *m_programValue;
+ //KComboBox *m_channelInValue;
+
+ QCheckBox *m_percussionCheckBox;
+ QCheckBox *m_bankCheckBox;
+ QCheckBox *m_variationCheckBox;
+ QCheckBox *m_programCheckBox;
+
+ QLabel *m_bankLabel;
+ QLabel *m_variationLabel;
+ QLabel *m_programLabel;
+
+ QGridLayout *m_mainGrid;
+ QFrame *m_rotaryFrame;
+ QGridLayout *m_rotaryGrid;
+ RotaryMap m_rotaries;
+ QSignalMapper *m_rotaryMapper;
+
+ BankList m_banks;
+ ProgramList m_programs;
+ MidiByteList m_variations;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/parameters/RosegardenParameterArea.cpp b/src/gui/editors/parameters/RosegardenParameterArea.cpp
new file mode 100644
index 0000000..968c737
--- /dev/null
+++ b/src/gui/editors/parameters/RosegardenParameterArea.cpp
@@ -0,0 +1,227 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ This file Copyright 2006 Martin Shepherd <[email protected]>.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "RosegardenParameterArea.h"
+
+#include "RosegardenParameterBox.h"
+#include <ktabwidget.h>
+#include <qfont.h>
+#include <qframe.h>
+#include <qpoint.h>
+#include <qscrollview.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qlayout.h>
+#include <qvgroupbox.h>
+#include <qwidget.h>
+#include <qwidgetstack.h>
+#include <iostream>
+#include <set>
+
+
+namespace Rosegarden
+{
+
+RosegardenParameterArea::RosegardenParameterArea(QWidget *parent,
+ const char *name, WFlags f)
+ : QWidgetStack(parent, name, f),
+ m_style(RosegardenParameterArea::CLASSIC_STYLE),
+ m_scrollView(new QScrollView(this, 0, Qt::WStaticContents)),
+ m_classic(new QVBox(m_scrollView->viewport())),
+ m_tabBox(new KTabWidget(this)),
+ m_active(0),
+ m_spacing(0)
+{
+ m_scrollView->addChild(m_classic);
+ m_scrollView->setHScrollBarMode(QScrollView::AlwaysOff);
+ m_scrollView->setVScrollBarMode(QScrollView::Auto);
+ m_scrollView->setResizePolicy(QScrollView::AutoOneFit);
+
+ // Install the classic-style VBox widget in the widget-stack.
+
+ addWidget(m_scrollView, CLASSIC_STYLE);
+
+ // Install the widget that implements the tab-style to the widget-stack.
+
+ addWidget(m_tabBox, TAB_BOX_STYLE);
+
+}
+
+void RosegardenParameterArea::addRosegardenParameterBox(
+ RosegardenParameterBox *b)
+{
+ // Check that the box hasn't been added before.
+
+ for (unsigned int i = 0; i < m_parameterBoxes.size(); i++) {
+ if (m_parameterBoxes[i] == b)
+ return ;
+ }
+
+ // Append the parameter box to the list to be displayed.
+
+ m_parameterBoxes.push_back(b);
+
+ m_scrollView->setMinimumWidth(std::max(m_scrollView->minimumWidth(),
+ b->sizeHint().width()) + 8);
+
+ // Create a titled group box for the parameter box, parented by the
+ // classic layout widget, so that it can be used to provide a title
+ // and outline, in classic mode. Add this container to an array that
+ // parallels the above array of parameter boxes.
+
+ QVGroupBox *box = new QVGroupBox(b->getLongLabel(), m_classic);
+ box->layout()->setMargin( 4 ); // about half the default value
+ QFont f;
+ f.setBold( true );
+ box->setFont( f );
+ m_groupBoxes.push_back(box);
+
+ if (m_spacing)
+ delete m_spacing;
+ m_spacing = new QFrame(m_classic);
+ m_classic->setStretchFactor(m_spacing, 100);
+
+ // Add the parameter box to the current container of the displayed
+ // widgets, unless the current container has been set up yet.
+
+ if (m_active)
+ moveWidget(0, m_active, b);
+
+ // Queue a redisplay of the parameter area, to incorporate the new box.
+
+ update();
+}
+
+void RosegardenParameterArea::setArrangement(Arrangement style)
+{
+ // Lookup the container of the specified style.
+
+ QWidget *container;
+ switch (style) {
+ case CLASSIC_STYLE:
+ container = m_classic;
+ break;
+ case TAB_BOX_STYLE:
+ container = m_tabBox;
+ break;
+ default:
+ std::cerr << "setArrangement() was passed an unknown arrangement style."
+ << std::endl;
+ return ;
+ }
+
+ // Does the current container of the parameter-box widgets differ
+ // from the one that is associated with the currently configured
+ // style?
+
+ if (container != m_active) {
+
+ // Move the parameter boxes from the old container to the new one.
+
+ std::vector<RosegardenParameterBox *> sorted;
+ std::set<RosegardenParameterBox *> unsorted;
+
+ for (unsigned int i = 0; i < m_parameterBoxes.size(); i++) {
+ unsorted.insert(m_parameterBoxes[i]);
+ }
+
+ QString previous = "";
+
+ while (!unsorted.empty()) {
+ std::set<RosegardenParameterBox *>::iterator i = unsorted.begin();
+ bool have = false;
+ while (i != unsorted.end()) {
+ if ((*i)->getPreviousBox(style) == previous) {
+ sorted.push_back(*i);
+ previous = (*i)->getShortLabel();
+ unsorted.erase(i);
+ have = true;
+ break;
+ }
+ ++i;
+ }
+ if (!have) {
+ while (!unsorted.empty()) {
+ sorted.push_back(*unsorted.begin());
+ unsorted.erase(unsorted.begin());
+ }
+ break;
+ }
+ }
+
+ for (std::vector<RosegardenParameterBox *>::iterator i = sorted.begin();
+ i != sorted.end(); ++i) {
+ moveWidget(m_active, container, *i);
+ (*i)->showAdditionalControls(style == TAB_BOX_STYLE);
+ }
+
+ // Switch the widget stack to displaying the new container.
+
+ raiseWidget(style);
+ }
+
+ // Record the identity of the active container, and the associated
+ // arrangement style.
+
+ m_active = container;
+ m_style = style;
+}
+
+void RosegardenParameterArea::moveWidget(QWidget *old_container,
+ QWidget *new_container,
+ RosegardenParameterBox *box)
+{
+ // Remove any state that is associated with the parameter boxes,
+ // from the active container.
+
+ if (old_container == m_classic) {
+ ;
+ } else if (old_container == m_tabBox) {
+ m_tabBox->removePage(box);
+ }
+
+ // Reparent the parameter box, and perform any container-specific
+ // configuration.
+
+ if (new_container == m_classic) {
+ int index = 0;
+ while (index < m_parameterBoxes.size()) {
+ if (box == m_parameterBoxes[index])
+ break;
+ ++index;
+ }
+ if (index < m_parameterBoxes.size()) {
+ box->reparent(m_groupBoxes[index], 0, QPoint(0, 0), FALSE);
+ }
+ } else if (new_container == m_tabBox) {
+ box->reparent(new_container, 0, QPoint(0, 0), FALSE);
+ m_tabBox->insertTab(box, box->getShortLabel());
+ }
+}
+
+}
+#include "RosegardenParameterArea.moc"
diff --git a/src/gui/editors/parameters/RosegardenParameterArea.h b/src/gui/editors/parameters/RosegardenParameterArea.h
new file mode 100644
index 0000000..1236a43
--- /dev/null
+++ b/src/gui/editors/parameters/RosegardenParameterArea.h
@@ -0,0 +1,108 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ This file Copyright 2006 Martin Shepherd <[email protected]>.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_ROSEGARDENPARAMETERAREA_H_
+#define _RG_ROSEGARDENPARAMETERAREA_H_
+
+#include <qwidgetstack.h>
+#include <vector>
+
+
+class QWidget;
+class QVGroupBox;
+class QVBox;
+class QScrollView;
+class KTabWidget;
+
+
+namespace Rosegarden
+{
+
+class RosegardenParameterBox;
+
+
+/**
+ * A widget that arranges a set of Rosegarden parameter-box widgets
+ * within a frame, in a dynamically configurable manner.
+ */
+class RosegardenParameterArea : public QWidgetStack
+{
+ Q_OBJECT
+public:
+
+ // Create the parameter display area.
+
+ RosegardenParameterArea(QWidget *parent=0, const char *name=0, WFlags f=0);
+
+ // Add a rosegarden parameter box to the list that are to be displayed.
+
+ void addRosegardenParameterBox(RosegardenParameterBox *b);
+
+
+ // List the supported methods of arranging the various parameter-box
+ // widgets within the parameter area.
+
+ enum Arrangement {
+ CLASSIC_STYLE, // A simple vertical tiling of parameter-box widgets.
+ TAB_BOX_STYLE // A horizontal list of tabs, displaying one box at a time.
+ };
+
+ // Redisplay the widgets with a different layout style.
+
+ void setArrangement(Arrangement style);
+
+protected:
+private:
+ Arrangement m_style; // The current layout style.
+
+ // The list of parameter box widgets that are being displayed by this
+ // widget.
+
+ std::vector<RosegardenParameterBox *> m_parameterBoxes;
+
+ // Create a parallel array of group boxes, to be used when the
+ // corresponding parameter box widget needs to be enclosed by a
+ // titled outline.
+
+ std::vector<QVGroupBox *> m_groupBoxes;
+
+ // Move a RosegardenParameterBox widget from one container to another.
+
+ void moveWidget(QWidget *old_container, QWidget *new_container,
+ RosegardenParameterBox *box);
+
+ QScrollView *m_scrollView; // Holds the m_classic container
+ QVBox *m_classic; // The container widget for m_style==CLASSIC_STYLE.
+ KTabWidget *m_tabBox; // The container widget for m_style==TAB_BOX_STYLE.
+ QWidget *m_active; // The current container widget.
+ QWidget *m_spacing;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/parameters/RosegardenParameterBox.cpp b/src/gui/editors/parameters/RosegardenParameterBox.cpp
new file mode 100644
index 0000000..7d9100c
--- /dev/null
+++ b/src/gui/editors/parameters/RosegardenParameterBox.cpp
@@ -0,0 +1,89 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "RosegardenParameterBox.h"
+
+#include "RosegardenParameterArea.h"
+#include <ktabwidget.h>
+#include <qfont.h>
+#include <qframe.h>
+#include <qscrollview.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qwidgetstack.h>
+
+
+namespace Rosegarden
+{
+
+RosegardenParameterBox::RosegardenParameterBox(const QString &shortLabel,
+ const QString &longLabel,
+ QWidget *parent,
+ const char *name) :
+ QFrame(parent, name),
+ m_shortLabel(shortLabel),
+ m_longLabel(longLabel),
+ m_mode(LANDSCAPE_MODE)
+{
+ init();
+}
+
+void RosegardenParameterBox::init()
+{
+ QFont plainFont;
+ plainFont.setPointSize(plainFont.pointSize() * 95 / 100);
+ if (plainFont.pixelSize() > 14)
+ plainFont.setPixelSize(14);
+ plainFont.setBold(false);
+ m_font = plainFont;
+
+ QFont boldFont;
+ boldFont.setPointSize(int(boldFont.pointSize() * 9.5 / 10.0 + 0.5));
+ if (boldFont.pixelSize() > 14)
+ boldFont.setPixelSize(14);
+ boldFont.setBold(true);
+
+ setFont(boldFont);
+}
+
+QString RosegardenParameterBox::getShortLabel() const
+{
+ return m_shortLabel;
+}
+
+QString RosegardenParameterBox::getLongLabel() const
+{
+ return m_longLabel;
+}
+
+QString RosegardenParameterBox::getPreviousBox(RosegardenParameterArea::Arrangement) const
+{
+ // No ordering known -- depends on subclasses
+ return "";
+}
+
+}
+#include "RosegardenParameterBox.moc"
diff --git a/src/gui/editors/parameters/RosegardenParameterBox.h b/src/gui/editors/parameters/RosegardenParameterBox.h
new file mode 100644
index 0000000..6f17358
--- /dev/null
+++ b/src/gui/editors/parameters/RosegardenParameterBox.h
@@ -0,0 +1,92 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_ROSEGARDENPARAMETERBOX_H_
+#define _RG_ROSEGARDENPARAMETERBOX_H_
+
+#include "RosegardenParameterArea.h"
+#include <qfont.h>
+#include <qframe.h>
+#include <qstring.h>
+#include <klocale.h>
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * A flat QFrame, in which a group of parameters can be laid out.
+ * Virtual method functions are defined for for requesting a layout
+ * style, and returning the single-word to use for labelling the
+ * box.
+ */
+
+class RosegardenParameterBox : public QFrame
+{
+ Q_OBJECT
+public:
+ RosegardenParameterBox(const QString &shortLabel, // e.g. i18n("Track")
+ const QString &longLabel, // e.g. i18n("Track Parameters")
+ QWidget *parent = 0,
+ const char *name = 0);
+
+ // Ask for a one-word string that can be used to label the widget.
+ QString getShortLabel() const;
+
+ // Ask for the full label (e.g. short-label "Parameters")
+ QString getLongLabel() const;
+
+ // Get the short label of the prior parameter box (to establish an ordering)
+ virtual QString getPreviousBox(RosegardenParameterArea::Arrangement) const;
+
+ virtual void showAdditionalControls(bool) = 0;
+
+protected:
+ void init();
+
+ // List the layout styles that may be requested via a call to setStyle().
+
+ enum LayoutMode {
+ LANDSCAPE_MODE, // Optimize the layout for a tall and narrow parent.
+ PORTRAIT_MODE // Optimize the layout for a short and wide parent.
+ };
+
+ void setLayoutMode(LayoutMode mode);
+
+ QFont m_font;
+ QString m_shortLabel; // The string that containers can use for labelling and identification
+ QString m_longLabel; // The full title
+ LayoutMode m_mode; // The current layout mode.
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/parameters/SegmentParameterBox.cpp b/src/gui/editors/parameters/SegmentParameterBox.cpp
new file mode 100644
index 0000000..c17cbe2
--- /dev/null
+++ b/src/gui/editors/parameters/SegmentParameterBox.cpp
@@ -0,0 +1,1214 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentParameterBox.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/Colour.h"
+#include "base/ColourMap.h"
+#include "base/Composition.h"
+#include "base/MidiProgram.h"
+#include "base/NotationTypes.h"
+#include "base/BasicQuantizer.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "commands/segment/SegmentChangeQuantizationCommand.h"
+#include "commands/segment/SegmentColourCommand.h"
+#include "commands/segment/SegmentColourMapCommand.h"
+#include "commands/segment/SegmentCommandRepeat.h"
+#include "commands/segment/SegmentLabelCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/dialogs/PitchPickerDialog.h"
+#include "gui/editors/notation/NotationStrings.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/widgets/ColourTable.h"
+#include "gui/widgets/TristateCheckBox.h"
+#include "RosegardenParameterArea.h"
+#include "RosegardenParameterBox.h"
+#include <kcolordialog.h>
+#include <kcombobox.h>
+#include <kcommand.h>
+#include <kconfig.h>
+#include <klineeditdlg.h>
+#include <ktabwidget.h>
+#include <qbutton.h>
+#include <qcheckbox.h>
+#include <qcolor.h>
+#include <qdialog.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+#include <qscrollview.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qwidgetstack.h>
+
+
+namespace Rosegarden
+{
+
+SegmentParameterBox::SegmentParameterBox(RosegardenGUIDoc* doc,
+ QWidget *parent)
+ : RosegardenParameterBox(i18n("Segment"),
+ i18n("Segment Parameters"),
+ parent),
+ m_highestPlayable(127),
+ m_lowestPlayable(0),
+ m_standardQuantizations(BasicQuantizer::getStandardQuantizations()),
+ m_doc(doc),
+ m_transposeRange(48)
+{
+ initBox();
+
+ m_doc->getComposition().addObserver(this);
+
+ connect(getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(update()));
+}
+
+SegmentParameterBox::~SegmentParameterBox()
+{
+ if (!isCompositionDeleted()) {
+ m_doc->getComposition().removeObserver(this);
+ }
+}
+
+void
+SegmentParameterBox::initBox()
+{
+ QFont font(m_font);
+
+ QFontMetrics fontMetrics(font);
+ // magic numbers: 13 is the height of the menu pixmaps, 10 is just 10
+ //int comboHeight = std::max(fontMetrics.height(), 13) + 10;
+ int width = fontMetrics.width("12345678901234567890");
+
+ // QFrame *frame = new QFrame(this);
+ QGridLayout *gridLayout = new QGridLayout(this, 8, 6, 4, 2);
+
+ QLabel *label = new QLabel(i18n("Label"), this);
+ QLabel *repeatLabel = new QLabel(i18n("Repeat"), this);
+ QLabel *quantizeLabel = new QLabel(i18n("Quantize"), this);
+ QLabel *transposeLabel = new QLabel(i18n("Transpose"), this);
+ QLabel *delayLabel = new QLabel(i18n("Delay"), this);
+ QLabel *colourLabel = new QLabel(i18n("Color"), this);
+// m_autoFadeLabel = new QLabel(i18n("Audio auto-fade"), this);
+// m_fadeInLabel = new QLabel(i18n("Fade in"), this);
+// m_fadeOutLabel = new QLabel(i18n("Fade out"), this);
+// m_rangeLabel = new QLabel(i18n("Range"), this);
+
+ // Label ..
+ m_label = new QLabel(this);
+ m_label->setFont(font);
+ m_label->setFixedWidth(width);
+ //m_label->setFixedHeight(comboHeight);
+ m_label->setFrameStyle(QFrame::Panel | QFrame::Sunken);
+
+ // .. and edit button
+ m_labelButton = new QPushButton(i18n("Edit"), this);
+ m_labelButton->setFont(font);
+ // m_labelButton->setFixedWidth(50);
+
+ connect(m_labelButton, SIGNAL(released()),
+ SLOT(slotEditSegmentLabel()));
+
+ m_repeatValue = new TristateCheckBox(this);
+ m_repeatValue->setFont(font);
+ //m_repeatValue->setFixedHeight(comboHeight);
+
+ // handle state changes
+ connect(m_repeatValue, SIGNAL(pressed()), SLOT(slotRepeatPressed()));
+
+ // non-reversing motif style read-only combo
+ m_quantizeValue = new KComboBox(this);
+ m_quantizeValue->setFont(font);
+ //m_quantizeValue->setFixedHeight(comboHeight);
+
+ // handle quantize changes from drop down
+ connect(m_quantizeValue, SIGNAL(activated(int)),
+ SLOT(slotQuantizeSelected(int)));
+
+ // reversing motif style read-write combo
+ m_transposeValue = new KComboBox(this);
+ m_transposeValue->setFont(font);
+ //m_transposeValue->setFixedHeight(comboHeight);
+
+ // handle transpose combo changes
+ connect(m_transposeValue, SIGNAL(activated(int)),
+ SLOT(slotTransposeSelected(int)));
+
+ // and text changes
+ connect(m_transposeValue, SIGNAL(textChanged(const QString&)),
+ SLOT(slotTransposeTextChanged(const QString&)));
+
+ // reversing motif style read-write combo
+ m_delayValue = new KComboBox(this);
+ m_delayValue->setFont(font);
+ //m_delayValue->setFixedHeight(comboHeight);
+
+ // handle delay combo changes
+ connect(m_delayValue, SIGNAL(activated(int)),
+ SLOT(slotDelaySelected(int)));
+
+ // Detect when the document colours are updated
+ connect(m_doc, SIGNAL(docColoursChanged()),
+ this, SLOT(slotDocColoursChanged()));
+
+ // handle text changes for delay
+ connect(m_delayValue, SIGNAL(textChanged(const QString&)),
+ SLOT(slotDelayTextChanged(const QString &)));
+
+ // set up combo box for colours
+ m_colourValue = new KComboBox(false, this);
+ m_colourValue->setFont(font);
+ //m_colourValue->setFixedHeight(comboHeight);
+ // m_colourValue->setMaximumWidth(width);
+ m_colourValue->setSizeLimit(20);
+
+ // handle colour combo changes
+ connect(m_colourValue, SIGNAL(activated(int)),
+ SLOT(slotColourSelected(int)));
+
+ // pre-set width of buttons so they don't grow later
+// width = fontMetrics.width(i18n("used internally for spacing", "High: ----"));
+
+ // highest playable note
+ //
+// m_highButton = new QPushButton(i18n("High: ---"), this);
+// QToolTip::add
+// (m_highButton, i18n("Choose the highest suggested playable note, using a staff"));
+// m_highButton->setFont(font);
+// m_highButton->setMinimumWidth(width);
+
+// connect(m_highButton, SIGNAL(released()),
+// SLOT(slotHighestPressed()));
+
+ // lowest playable note
+ //
+// m_lowButton = new QPushButton(i18n("Low: ----"), this);
+// QToolTip::add
+// (m_lowButton, i18n("Choose the lowest suggested playable note, using a staff"));
+// m_lowButton->setFont(font);
+// m_lowButton->setMinimumWidth(width);
+
+// connect(m_lowButton, SIGNAL(released()),
+// SLOT(slotLowestPressed()));
+
+ // Audio autofade enabled
+ //
+// m_autoFadeBox = new QCheckBox(this);
+// connect(m_autoFadeBox, SIGNAL(stateChanged(int)),
+// this, SLOT(slotAudioFadeChanged(int)));
+
+ // Fade in and out times
+ //
+// m_fadeInSpin = new QSpinBox(this);
+// m_fadeInSpin->setMinValue(0);
+// m_fadeInSpin->setMaxValue(5000);
+// m_fadeInSpin->setSuffix(i18n(" ms"));
+// connect(m_fadeInSpin, SIGNAL(valueChanged(int)),
+// this, SLOT(slotFadeInChanged(int)));
+
+// m_fadeOutSpin = new QSpinBox(this);
+// m_fadeOutSpin->setMinValue(0);
+// m_fadeOutSpin->setMaxValue(5000);
+// m_fadeOutSpin->setSuffix(i18n(" ms"));
+// connect(m_fadeOutSpin, SIGNAL(valueChanged(int)),
+// this, SLOT(slotFadeOutChanged(int)));
+
+ label->setFont(font);
+ repeatLabel->setFont(font);
+ quantizeLabel->setFont(font);
+ transposeLabel->setFont(font);
+ delayLabel->setFont(font);
+ colourLabel->setFont(font);
+// m_autoFadeLabel->setFont(font);
+// m_fadeInLabel->setFont(font);
+// m_fadeOutLabel->setFont(font);
+// m_rangeLabel->setFont(font);
+
+ int row = 0;
+
+// gridLayout->addRowSpacing(0, 12); // why??
+
+ gridLayout->addWidget(label, row, 0); //, AlignRight);
+ gridLayout->addMultiCellWidget(m_label, row, row, 1, 4); //, AlignLeft);
+ gridLayout->addWidget(m_labelButton, row, 5); //, AlignLeft);
+ ++row;
+
+ gridLayout->addWidget(repeatLabel, row, 0); //, AlignRight);
+ gridLayout->addWidget(m_repeatValue, row, 1); //, AlignLeft);
+
+ gridLayout->addMultiCellWidget(transposeLabel, row, row, 2, 3, AlignRight);
+ gridLayout->addMultiCellWidget(m_transposeValue, row, row, 4, 5);
+ ++row;
+
+ gridLayout->addWidget(quantizeLabel, row, 0); //, AlignRight);
+ gridLayout->addMultiCellWidget(m_quantizeValue, row, row, 1, 2); //, AlignLeft);
+
+ gridLayout->addWidget(delayLabel, row, 3, AlignRight);
+ gridLayout->addMultiCellWidget(m_delayValue, row, row, 4, 5);
+ ++row;
+
+ gridLayout->addWidget(colourLabel, row, 0); //, AlignRight);
+ gridLayout->addMultiCellWidget(m_colourValue, row, row, 1, 5);
+ ++row;
+
+// gridLayout->addWidget(m_rangeLabel, row, 0); //, AlignRight);
+// gridLayout->addMultiCellWidget(m_lowButton, row, row, 1, 2);
+// gridLayout->addMultiCellWidget(m_highButton, row, row, 3, 4);
+// ++row;
+
+// m_autoFadeLabel->hide();
+// m_autoFadeBox->hide();
+ /*
+ gridLayout->addWidget(m_fadeInLabel, 5, 0, AlignRight);
+ gridLayout->addWidget(m_fadeInSpin, 5, 1);
+
+ gridLayout->addWidget(m_fadeOutLabel, 5, 2, AlignRight);
+ gridLayout->addWidget(m_fadeOutSpin, 5, 3);
+ */
+ // Configure the empty final row to accomodate any extra vertical space.
+
+ gridLayout->setRowStretch(gridLayout->numRows() - 1, 1);
+
+ // Configure the empty final column to accomodate any extra horizontal
+ // space.
+
+// gridLayout->setColStretch(gridLayout->numCols() - 1, 1);
+
+ // populate the quantize combo
+ //
+ QPixmap noMap = NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("menu-no-note"));
+
+ for (unsigned int i = 0; i < m_standardQuantizations.size(); ++i) {
+
+ timeT time = m_standardQuantizations[i];
+ timeT error = 0;
+ QString label = NotationStrings::makeNoteMenuLabel(time, true, error);
+ QPixmap pmap = NotePixmapFactory::toQPixmap(NotePixmapFactory::makeNoteMenuPixmap(time, error));
+ m_quantizeValue->insertItem(error ? noMap : pmap, label);
+ }
+ m_quantizeValue->insertItem(noMap, i18n("Off"));
+
+ // default to last item
+ m_quantizeValue->setCurrentItem(m_quantizeValue->count() - 1);
+
+ // populate the transpose combo
+ //
+ for (int i = -m_transposeRange; i < m_transposeRange + 1; i++) {
+ m_transposeValue->insertItem(noMap, QString("%1").arg(i));
+ if (i == 0)
+ m_transposeValue->setCurrentItem(m_transposeValue->count() - 1);
+ }
+
+ m_delays.clear();
+
+ for (int i = 0; i < 6; i++) {
+ timeT time = 0;
+ if (i > 0 && i < 6) {
+ time = Note(Note::Hemidemisemiquaver).getDuration() << (i - 1);
+ } else if (i > 5) {
+ time = Note(Note::Crotchet).getDuration() * (i - 4);
+ }
+
+ m_delays.push_back(time);
+
+ // check if it's a valid note duration (it will be for the
+ // time defn above, but if we were basing it on the sequencer
+ // resolution it might not be) & include a note pixmap if so
+ //
+ timeT error = 0;
+ QString label = NotationStrings::makeNoteMenuLabel(time, true, error);
+ QPixmap pmap = NotePixmapFactory::toQPixmap(NotePixmapFactory::makeNoteMenuPixmap(time, error));
+ m_delayValue->insertItem((error ? noMap : pmap), label);
+ }
+
+ for (int i = 0; i < 10; i++) {
+ int rtd = (i < 5 ? ((i + 1) * 10) : ((i - 3) * 50));
+ m_realTimeDelays.push_back(rtd);
+ m_delayValue->insertItem(i18n("%1 ms").arg(rtd));
+ }
+
+ // set delay blank initially
+ m_delayValue->setCurrentItem( -1);
+
+ // populate m_colourValue
+ slotDocColoursChanged();
+
+ //!!! disabled until after 1.3
+// m_highButton->hide();
+// m_lowButton->hide();
+// m_rangeLabel->hide();
+ //////////////////////////////
+
+}
+
+void
+SegmentParameterBox::setDocument(RosegardenGUIDoc* doc)
+{
+ if (m_doc != 0)
+ disconnect(m_doc, SIGNAL(docColoursChanged()),
+ this, SLOT(slotDocColoursChanged()));
+
+ m_doc = doc;
+
+ // Detect when the document colours are updated
+ connect (m_doc, SIGNAL(docColoursChanged()),
+ this, SLOT(slotDocColoursChanged()));
+
+ slotDocColoursChanged(); // repopulate combo
+}
+
+void
+SegmentParameterBox::useSegment(Segment *segment)
+{
+ m_segments.clear();
+ m_segments.push_back(segment);
+ populateBoxFromSegments();
+}
+
+void
+SegmentParameterBox::useSegments(const SegmentSelection &segments)
+{
+ m_segments.clear();
+
+ m_segments.resize(segments.size());
+ std::copy(segments.begin(), segments.end(), m_segments.begin());
+
+ populateBoxFromSegments();
+}
+
+void
+SegmentParameterBox::slotDocColoursChanged()
+{
+ RG_DEBUG << "SegmentParameterBox::slotDocColoursChanged()" << endl;
+
+ m_colourValue->clear();
+ m_colourList.clear();
+ // Populate it from composition.m_segmentColourMap
+ ColourMap temp = m_doc->getComposition().getSegmentColourMap();
+
+ unsigned int i = 0;
+
+ for (RCMap::const_iterator it = temp.begin(); it != temp.end(); ++it) {
+ QString qtrunc(strtoqstr(it->second.second));
+ QPixmap colour(15, 15);
+ colour.fill(GUIPalette::convertColour(it->second.first));
+ if (qtrunc == "") {
+ m_colourValue->insertItem(colour, i18n("Default"), i);
+ } else {
+ // truncate name to 15 characters to avoid the combo forcing the
+ // whole kit and kaboodle too wide
+ if (qtrunc.length() > 15)
+ qtrunc = qtrunc.left(12) + "...";
+ m_colourValue->insertItem(colour, qtrunc, i);
+ }
+ m_colourList[it->first] = i; // maps colour number to menu index
+ ++i;
+ }
+
+ m_addColourPos = i;
+ m_colourValue->insertItem(i18n("Add New Color"), m_addColourPos);
+
+ m_colourValue->setCurrentItem(0);
+}
+
+void SegmentParameterBox::update()
+{
+ RG_DEBUG << "SegmentParameterBox::update()" << endl;
+
+ populateBoxFromSegments();
+}
+
+void
+SegmentParameterBox::segmentRemoved(const Composition *composition,
+ Segment *segment)
+{
+ if (composition == &m_doc->getComposition()) {
+
+ for (std::vector<Segment*>::iterator it =
+ m_segments.begin(); it != m_segments.end(); ++it) {
+
+ if (*it == segment) {
+ m_segments.erase(it);
+ return ;
+ }
+ }
+ }
+}
+
+void
+SegmentParameterBox::populateBoxFromSegments()
+{
+ std::vector<Segment*>::iterator it;
+ Tristate repeated = NotApplicable;
+ Tristate quantized = NotApplicable;
+ Tristate transposed = NotApplicable;
+ Tristate delayed = NotApplicable;
+ Tristate diffcolours = NotApplicable;
+ Tristate highlow = NotApplicable;
+ unsigned int myCol = 0;
+ unsigned int myHigh = 127;
+ unsigned int myLow = 0;
+
+ timeT qntzLevel = 0;
+ // At the moment we have no negative delay, so we use negative
+ // values to represent real-time delay in ms
+ timeT delayLevel = 0;
+ int transposeLevel = 0;
+
+ if (m_segments.size() == 0)
+ m_label->setText("");
+ else
+ m_label->setText(strtoqstr(m_segments[0]->getLabel()));
+
+ for (it = m_segments.begin(); it != m_segments.end(); it++) {
+ // ok, first thing is we know we have at least one segment
+ if (repeated == NotApplicable)
+ repeated = None;
+ if (quantized == NotApplicable)
+ quantized = None;
+ if (transposed == NotApplicable)
+ transposed = None;
+ if (delayed == NotApplicable)
+ delayed = None;
+ if (diffcolours == NotApplicable)
+ diffcolours = None;
+ if (highlow == NotApplicable)
+ highlow = None;
+
+ // Set label to "*" when multiple labels don't match
+ //
+ if (strtoqstr((*it)->getLabel()) != m_label->text())
+ m_label->setText("*");
+
+ // Are all, some or none of the Segments repeating?
+ if ((*it)->isRepeating()) {
+ if (it == m_segments.begin())
+ repeated = All;
+ else {
+ if (repeated == None)
+ repeated = Some;
+ }
+ } else {
+ if (repeated == All)
+ repeated = Some;
+ }
+
+ // Quantization
+ //
+ if ((*it)->hasQuantization()) {
+ if (it == m_segments.begin()) {
+ quantized = All;
+ qntzLevel = (*it)->getQuantizer()->getUnit();
+ } else {
+ // If quantize levels don't match
+ if (quantized == None ||
+ (quantized == All &&
+ qntzLevel !=
+ (*it)->getQuantizer()->getUnit()))
+ quantized = Some;
+ }
+ } else {
+ if (quantized == All)
+ quantized = Some;
+ }
+
+ // Transpose
+ //
+ if ((*it)->getTranspose() != 0) {
+ if (it == m_segments.begin()) {
+ transposed = All;
+ transposeLevel = (*it)->getTranspose();
+ } else {
+ if (transposed == None ||
+ (transposed == All &&
+ transposeLevel != (*it)->getTranspose()))
+ transposed = Some;
+ }
+
+ } else {
+ if (transposed == All)
+ transposed = Some;
+ }
+
+ // Delay
+ //
+ timeT myDelay = (*it)->getDelay();
+ if (myDelay == 0) {
+ myDelay = -((*it)->getRealTimeDelay().sec * 1000 +
+ (*it)->getRealTimeDelay().msec());
+ }
+
+ if (myDelay != 0) {
+ if (it == m_segments.begin()) {
+ delayed = All;
+ delayLevel = myDelay;
+ } else {
+ if (delayed == None ||
+ (delayed == All &&
+ delayLevel != myDelay))
+ delayed = Some;
+ }
+ } else {
+ if (delayed == All)
+ delayed = Some;
+ }
+
+ // Colour
+
+ if (it == m_segments.begin()) {
+ myCol = (*it)->getColourIndex();
+ } else {
+ if (myCol != (*it)->getColourIndex())
+ ;
+ diffcolours = All;
+ }
+
+ // Highest/Lowest playable
+ //
+ if (it == m_segments.begin()) {
+ myHigh = (*it)->getHighestPlayable();
+ myLow = (*it)->getLowestPlayable();
+ } else {
+ if (myHigh != (*it)->getHighestPlayable() ||
+ myLow != (*it)->getLowestPlayable()) {
+ highlow = All;
+ }
+ }
+
+ }
+
+ switch (repeated) {
+ case All:
+ m_repeatValue->setChecked(true);
+ break;
+
+ case Some:
+ m_repeatValue->setNoChange();
+ break;
+
+ case None:
+ case NotApplicable:
+ default:
+ m_repeatValue->setChecked(false);
+ break;
+ }
+
+ m_repeatValue->setEnabled(repeated != NotApplicable);
+
+ switch (quantized) {
+ case All: {
+ for (unsigned int i = 0;
+ i < m_standardQuantizations.size(); ++i) {
+ if (m_standardQuantizations[i] == qntzLevel) {
+ m_quantizeValue->setCurrentItem(i);
+ break;
+ }
+ }
+ }
+ break;
+
+ case Some:
+ // Set the edit text to an unfeasible blank value meaning "Some"
+ //
+ m_quantizeValue->setCurrentItem( -1);
+ break;
+
+ // Assuming "Off" is always the last field
+ case None:
+ default:
+ m_quantizeValue->setCurrentItem(m_quantizeValue->count() - 1);
+ break;
+ }
+
+ m_quantizeValue->setEnabled(quantized != NotApplicable);
+
+ switch (transposed) {
+ // setCurrentItem works with QStrings
+ // 2nd arg of "true" means "add if necessary"
+ case All:
+ m_transposeValue->
+ setCurrentItem(QString("%1").arg(transposeLevel), true);
+ break;
+
+ case Some:
+ m_transposeValue->setCurrentItem(QString(""), true);
+ break;
+
+ case None:
+ default:
+ m_transposeValue->setCurrentItem("0");
+ break;
+ }
+
+ m_transposeValue->setEnabled(transposed != NotApplicable);
+
+ m_delayValue->blockSignals(true);
+
+ switch (delayed) {
+ case All:
+ if (delayLevel >= 0) {
+ timeT error = 0;
+ QString label = NotationStrings::makeNoteMenuLabel(delayLevel,
+ true,
+ error);
+ m_delayValue->setCurrentItem(label, true);
+
+ } else if (delayLevel < 0) {
+
+ m_delayValue->setCurrentItem(i18n("%1 ms").arg( -delayLevel),
+ true);
+ }
+
+ break;
+
+ case Some:
+ m_delayValue->setCurrentItem("", true);
+ break;
+
+ case None:
+ default:
+ m_delayValue->setCurrentItem(0);
+ break;
+ }
+
+ m_delayValue->setEnabled(delayed != NotApplicable);
+
+ m_delayValue->blockSignals(false);
+
+ switch (diffcolours) {
+ case None:
+ if (m_colourList.find(myCol) != m_colourList.end())
+ m_colourValue->setCurrentItem(m_colourList[myCol]);
+ else
+ m_colourValue->setCurrentItem(0);
+ break;
+
+
+ case All:
+ case NotApplicable:
+ default:
+ m_colourValue->setCurrentItem(0);
+ break;
+
+ }
+
+ m_colourValue->setEnabled(diffcolours != NotApplicable);
+
+ //!!! this is all borked up and useless; sort out after 1.3
+/*
+ switch (highlow) {
+ case All:
+ updateHighLow();
+ break;
+
+ case Some:
+ case None:
+ default:
+ m_highButton->setText(i18n("High: ---"));
+ m_lowButton->setText(i18n("Low: ----"));
+ highlow = NotApplicable;
+ break;
+ }
+
+ m_highButton->setEnabled(highlow != NotApplicable);
+ m_lowButton->setEnabled(highlow != NotApplicable);
+*/
+
+ // Enable or disable the fade in/out params
+/*
+ if (m_segments.size() == 1 &&
+ (*(m_segments.begin()))->getType() == Segment::Audio) {
+ m_autoFadeBox->blockSignals(true);
+ m_fadeInSpin->blockSignals(true);
+ m_fadeOutSpin->blockSignals(true);
+
+ ... !!! No, not setting up autofade widgets. The implementation's too
+ incomplete to finish for this release.
+
+ (Or for the next one after the one the previous comment referred to.)
+
+ (Or for the one after the one after that. Will we ever get those
+ working, or should Rich's final legacy simply be quietly disappeared?)
+
+ m_fadeInLabel->show();
+ m_fadeInSpin->show();
+ m_fadeOutLabel->show();
+ m_fadeOutSpin->show();
+
+ instead:
+
+ m_fadeInLabel->hide();
+ m_fadeInSpin->hide();
+ m_fadeOutLabel->hide();
+ m_fadeOutSpin->hide();
+
+ m_autoFadeLabel->setEnabled(true);
+ m_autoFadeBox->setEnabled(true);
+ m_fadeInLabel->setEnabled(true);
+ m_fadeInSpin->setEnabled(true);
+ m_fadeOutLabel->setEnabled(true);
+ m_fadeOutSpin->setEnabled(true);
+
+ Segment *seg = *(m_segments.begin());
+
+ int fadeInTime = seg->getFadeInTime().sec * 1000 +
+ seg->getFadeInTime().msec();
+ m_fadeInSpin->setValue(fadeInTime);
+
+ int fadeOutTime = seg->getFadeOutTime().sec * 1000 +
+ seg->getFadeOutTime().msec();
+ m_fadeOutSpin->setValue(fadeOutTime);
+
+ m_autoFadeBox->setChecked(seg->isAutoFading());
+
+ m_autoFadeBox->blockSignals(false);
+ m_fadeInSpin->blockSignals(false);
+ m_fadeOutSpin->blockSignals(false);
+ } else {
+ m_autoFadeLabel->setEnabled(false);
+ m_autoFadeBox->setEnabled(false);
+ m_fadeInLabel->setEnabled(false);
+ m_fadeInSpin->setEnabled(false);
+ m_fadeOutLabel->setEnabled(false);
+ m_fadeOutSpin->setEnabled(false);
+
+ m_autoFadeLabel->hide();
+ m_autoFadeBox->hide();
+ m_fadeInLabel->hide();
+ m_fadeInSpin->hide();
+ m_fadeOutLabel->hide();
+ m_fadeOutSpin->hide();
+
+ m_autoFadeBox->setChecked(false);
+ m_fadeInSpin->setValue(0);
+ m_fadeOutSpin->setValue(0);
+ }
+*/
+
+}
+
+void SegmentParameterBox::slotRepeatPressed()
+{
+ if (m_segments.size() == 0)
+ return ;
+
+ bool state = false;
+
+ switch (m_repeatValue->state()) {
+ case QButton::Off:
+ state = true;
+ break;
+
+ case QButton::NoChange:
+ case QButton::On:
+ default:
+ state = false;
+ break;
+ }
+
+ // update the check box and all current Segments
+ m_repeatValue->setChecked(state);
+
+ addCommandToHistory(new SegmentCommandRepeat(m_segments, state));
+
+ // std::vector<Segment*>::iterator it;
+
+ // for (it = m_segments.begin(); it != m_segments.end(); it++)
+ // (*it)->setRepeating(state);
+}
+
+void
+SegmentParameterBox::slotQuantizeSelected(int qLevel)
+{
+ bool off = (qLevel == m_quantizeValue->count() - 1);
+
+ SegmentChangeQuantizationCommand *command =
+ new SegmentChangeQuantizationCommand
+ (off ? 0 : m_standardQuantizations[qLevel]);
+
+ std::vector<Segment*>::iterator it;
+ for (it = m_segments.begin(); it != m_segments.end(); it++) {
+ command->addSegment(*it);
+ }
+
+ addCommandToHistory(command);
+}
+
+void
+SegmentParameterBox::slotTransposeTextChanged(const QString &text)
+{
+ if (text.isEmpty() || m_segments.size() == 0)
+ return ;
+
+ int transposeValue = text.toInt();
+
+ // addCommandToHistory(new SegmentCommandChangeTransposeValue(m_segments,
+ // transposeValue));
+
+ std::vector<Segment*>::iterator it;
+ for (it = m_segments.begin(); it != m_segments.end(); it++) {
+ (*it)->setTranspose(transposeValue);
+ }
+
+ emit documentModified();
+}
+
+void
+SegmentParameterBox::slotTransposeSelected(int value)
+{
+ slotTransposeTextChanged(m_transposeValue->text(value));
+}
+
+void
+SegmentParameterBox::slotDelayTimeChanged(timeT delayValue)
+{
+ // by convention and as a nasty hack, we use negative timeT here
+ // to represent positive RealTime in ms
+
+ if (delayValue > 0) {
+
+ std::vector<Segment*>::iterator it;
+ for (it = m_segments.begin(); it != m_segments.end(); it++) {
+ (*it)->setDelay(delayValue);
+ (*it)->setRealTimeDelay(RealTime::zeroTime);
+ }
+
+ } else if (delayValue < 0) {
+
+ std::vector<Segment*>::iterator it;
+ for (it = m_segments.begin(); it != m_segments.end(); it++) {
+ (*it)->setDelay(0);
+ int sec = ( -delayValue) / 1000;
+ int nsec = (( -delayValue) - 1000 * sec) * 1000000;
+ (*it)->setRealTimeDelay(RealTime(sec, nsec));
+ }
+ } else {
+
+ std::vector<Segment*>::iterator it;
+ for (it = m_segments.begin(); it != m_segments.end(); it++) {
+ (*it)->setDelay(0);
+ (*it)->setRealTimeDelay(RealTime::zeroTime);
+ }
+ }
+
+ emit documentModified();
+}
+
+void
+SegmentParameterBox::slotDelayTextChanged(const QString &text)
+{
+ if (text.isEmpty() || m_segments.size() == 0)
+ return ;
+
+ slotDelayTimeChanged( -(text.toInt()));
+}
+
+void
+SegmentParameterBox::slotDelaySelected(int value)
+{
+ if (value < int(m_delays.size())) {
+ slotDelayTimeChanged(m_delays[value]);
+ } else {
+ slotDelayTimeChanged( -(m_realTimeDelays[value - m_delays.size()]));
+ }
+}
+
+void
+SegmentParameterBox::slotColourSelected(int value)
+{
+ if (value != m_addColourPos) {
+ unsigned int temp = 0;
+
+ ColourTable::ColourList::const_iterator pos;
+ for (pos = m_colourList.begin(); pos != m_colourList.end(); ++pos) {
+ if (pos->second == value) {
+ temp = pos->first;
+ break;
+ }
+ }
+
+ SegmentSelection segments;
+ std::vector<Segment*>::iterator it;
+
+ for (it = m_segments.begin(); it != m_segments.end(); ++it) {
+ segments.insert(*it);
+ }
+
+ SegmentColourCommand *command = new SegmentColourCommand(segments, temp);
+
+ addCommandToHistory(command);
+ } else {
+ ColourMap newMap = m_doc->getComposition().getSegmentColourMap();
+ QColor newColour;
+ bool ok = false;
+ QString newName = KLineEditDlg::getText(i18n("New Color Name"), i18n("Enter new name"),
+ i18n("New"), &ok);
+ if ((ok == true) && (!newName.isEmpty())) {
+ KColorDialog box(this, "", true);
+
+ int result = box.getColor(newColour);
+
+ if (result == KColorDialog::Accepted) {
+ Colour newRColour = GUIPalette::convertColour(newColour);
+ newMap.addItem(newRColour, qstrtostr(newName));
+ SegmentColourMapCommand *command = new SegmentColourMapCommand(m_doc, newMap);
+ addCommandToHistory(command);
+ slotDocColoursChanged();
+ }
+ }
+ // Else we don't do anything as they either didn't give a name·
+ // or didn't give a colour
+ }
+
+
+}
+
+void
+SegmentParameterBox::updateHighLow()
+{
+ // Key of C major and NoAccidental means any "black key" notes will be
+ // written as sharps.
+ Accidental accidental = Accidentals::NoAccidental;
+ Rosegarden::Key key = Rosegarden::Key("C major");
+
+ Pitch highest(m_highestPlayable, accidental);
+ Pitch lowest(m_lowestPlayable, accidental);
+
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ int base = config->readNumEntry("midipitchoctave", -2);
+ //!!! FIXME this code is broken, and needs to be fixed after the fashion of
+ //the TPB, but I'm not bothering with that at this time, because they are
+ //going to be hidden for 1.3 anyway
+// m_highButton->setText(QString("&High: %1%2").arg(highest.getNoteName(key)).arg(highest.getOctave(base)));
+// m_lowButton->setText(QString("&Low: %1%2").arg(lowest.getNoteName(key)).arg(lowest.getOctave(base)));
+}
+
+void
+SegmentParameterBox::slotHighestPressed()
+{
+ RG_DEBUG << "SegmentParameterBox::slotHighestPressed()" << endl;
+
+ PitchPickerDialog dialog(0, m_highestPlayable, i18n("Highest playable note"));
+ std::vector<Segment*>::iterator it;
+
+ if (dialog.exec() == QDialog::Accepted) {
+ m_highestPlayable = dialog.getPitch();
+ updateHighLow();
+
+ for (it = m_segments.begin(); it != m_segments.end(); it++) {
+ (*it)->setHighestPlayable(m_highestPlayable);
+ }
+
+ emit documentModified();
+ }
+}
+
+void
+SegmentParameterBox::slotLowestPressed()
+{
+ RG_DEBUG << "SegmentParameterBox::slotLowestPressed()" << endl;
+
+ PitchPickerDialog dialog(0, m_lowestPlayable, i18n("Lowest playable note"));
+ std::vector<Segment*>::iterator it;
+
+ if (dialog.exec() == QDialog::Accepted) {
+ m_lowestPlayable = dialog.getPitch();
+ updateHighLow();
+
+ for (it = m_segments.begin(); it != m_segments.end(); it++) {
+ (*it)->setLowestPlayable(m_lowestPlayable);
+ }
+
+ emit documentModified();
+ }
+}
+
+MultiViewCommandHistory*
+SegmentParameterBox::getCommandHistory()
+{
+ return m_doc->getCommandHistory();
+}
+
+void
+SegmentParameterBox::addCommandToHistory(KCommand *command)
+{
+ m_doc->getCommandHistory()->addCommand(command);
+}
+
+void
+SegmentParameterBox::slotEditSegmentLabel()
+{
+ QString editLabel;
+
+ if (m_segments.size() == 0)
+ return ;
+ else if (m_segments.size() == 1)
+ editLabel = i18n("Modify Segment label");
+ else
+ editLabel = i18n("Modify Segments label");
+
+ bool ok = false;
+
+ // Remove the asterisk if we're using it
+ //
+ QString label = m_label->text();
+ if (label == "*")
+ label = "";
+
+ QString newLabel = KLineEditDlg::getText(editLabel,
+ i18n("Enter new label"),
+ m_label->text(),
+ &ok,
+ this);
+
+ if (ok) {
+ SegmentSelection segments;
+ std::vector<Segment*>::iterator it;
+ for (it = m_segments.begin(); it != m_segments.end(); ++it)
+ segments.insert(*it);
+
+ SegmentLabelCommand *command = new
+ SegmentLabelCommand(segments, newLabel);
+
+ addCommandToHistory(command);
+
+ // fix #1776915, maybe?
+ update();
+ }
+}
+
+void
+SegmentParameterBox::slotAudioFadeChanged(int value)
+{
+ RG_DEBUG << "SegmentParameterBox::slotAudioFadeChanged - value = "
+ << value << endl;
+/*
+ if (m_segments.size() == 0)
+ return ;
+
+ bool state = false;
+ if (value == QButton::On)
+ state = true;
+
+ std::vector<Segment*>::iterator it;
+ for (it = m_segments.begin(); it != m_segments.end(); it++) {
+ (*it)->setAutoFade(state);
+ }
+*/
+}
+
+void
+SegmentParameterBox::slotFadeInChanged(int value)
+{
+ RG_DEBUG << "SegmentParameterBox::slotFadeInChanged - value = "
+ << value << endl;
+/*
+ if (m_segments.size() == 0)
+ return ;
+
+ if (value == 0 && m_fadeOutSpin->value() == 0)
+ slotAudioFadeChanged(QButton::Off);
+ else
+ slotAudioFadeChanged(QButton::On);
+
+ // Convert from ms
+ //
+ RealTime fadeInTime(value / 1000, (value % 1000) * 1000000);
+
+ std::vector<Segment*>::iterator it;
+ for (it = m_segments.begin(); it != m_segments.end(); it++) {
+ (*it)->setFadeInTime(fadeInTime);
+ }
+
+ emit documentModified();
+*/
+}
+
+void
+SegmentParameterBox::slotFadeOutChanged(int value)
+{
+ RG_DEBUG << "SegmentParameterBox::slotFadeOutChanged - value = "
+ << value << endl;
+/*
+ if (m_segments.size() == 0)
+ return ;
+
+ if (value == 0 && m_fadeInSpin->value() == 0)
+ slotAudioFadeChanged(QButton::Off);
+ else
+ slotAudioFadeChanged(QButton::On);
+
+ // Convert from ms
+ //
+ RealTime fadeOutTime(value / 1000000, (value % 1000) * 10000000);
+
+ std::vector<Segment*>::iterator it;
+ for (it = m_segments.begin(); it != m_segments.end(); it++) {
+ (*it)->setFadeOutTime(fadeOutTime);
+ }
+
+ emit documentModified();
+*/
+}
+
+void
+SegmentParameterBox::showAdditionalControls(bool showThem)
+{
+ //!!! disabled until after 1.3
+ /* m_highButton->setShown(showThem);
+ m_lowButton->setShown(showThem);
+ m_rangeLabel->setShown(showThem); */
+}
+
+QString
+SegmentParameterBox::getPreviousBox(RosegardenParameterArea::Arrangement arrangement) const
+{
+ if (arrangement == RosegardenParameterArea::CLASSIC_STYLE) {
+ return "";
+ } else {
+ return i18n("Instrument");
+ }
+}
+
+}
+#include "SegmentParameterBox.moc"
diff --git a/src/gui/editors/parameters/SegmentParameterBox.h b/src/gui/editors/parameters/SegmentParameterBox.h
new file mode 100644
index 0000000..a8b0353
--- /dev/null
+++ b/src/gui/editors/parameters/SegmentParameterBox.h
@@ -0,0 +1,174 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTPARAMETERBOX_H_
+#define _RG_SEGMENTPARAMETERBOX_H_
+
+#include "base/Composition.h"
+#include "base/MidiProgram.h"
+#include "gui/widgets/ColourTable.h"
+#include "RosegardenParameterArea.h"
+#include "RosegardenParameterBox.h"
+#include <qstring.h>
+#include <vector>
+#include "base/Event.h"
+
+
+class QWidget;
+class QSpinBox;
+class QPushButton;
+class QLabel;
+class QCheckBox;
+class KCommand;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class TristateCheckBox;
+class SegmentSelection;
+class Segment;
+class RosegardenGUIDoc;
+class MultiViewCommandHistory;
+class Composition;
+
+
+class SegmentParameterBox : public RosegardenParameterBox,
+ public CompositionObserver
+{
+Q_OBJECT
+
+public:
+
+ typedef enum
+ {
+ None,
+ Some,
+ All,
+ NotApplicable // no applicable segments selected
+ } Tristate;
+
+ SegmentParameterBox(RosegardenGUIDoc *doc,
+ QWidget *parent=0);
+ ~SegmentParameterBox();
+
+ // Use Segments to update GUI parameters
+ //
+ void useSegment(Segment *segment);
+ void useSegments(const SegmentSelection &segments);
+
+ // Command history stuff
+ MultiViewCommandHistory* getCommandHistory();
+ void addCommandToHistory(KCommand *command);
+
+ void setDocument(RosegardenGUIDoc*);
+
+ // CompositionObserver interface
+ //
+ virtual void segmentRemoved(const Composition *,
+ Segment *);
+
+ virtual void showAdditionalControls(bool showThem);
+
+ virtual QString getPreviousBox(RosegardenParameterArea::Arrangement) const;
+
+public slots:
+ void slotRepeatPressed();
+ void slotQuantizeSelected(int);
+
+ void slotTransposeSelected(int);
+ void slotTransposeTextChanged(const QString &);
+
+ void slotDelaySelected(int);
+ void slotDelayTimeChanged(timeT delayValue);
+ void slotDelayTextChanged(const QString &);
+
+ void slotEditSegmentLabel();
+
+ void slotColourSelected(int);
+ void slotDocColoursChanged();
+
+ void slotAudioFadeChanged(int);
+ void slotFadeInChanged(int);
+ void slotFadeOutChanged(int);
+
+ void slotHighestPressed();
+ void slotLowestPressed();
+
+ virtual void update();
+
+signals:
+ void documentModified();
+ void canvasModified();
+
+protected:
+ void initBox();
+ void populateBoxFromSegments();
+ void updateHighLow();
+
+ QLabel *m_label;
+// QLabel *m_rangeLabel;
+ QPushButton *m_labelButton;
+// QPushButton *m_highButton;
+// QPushButton *m_lowButton;
+ TristateCheckBox *m_repeatValue;
+ KComboBox *m_quantizeValue;
+ KComboBox *m_transposeValue;
+ KComboBox *m_delayValue;
+ KComboBox *m_colourValue;
+
+ // Audio autofade
+ //
+// QLabel *m_autoFadeLabel;
+// QCheckBox *m_autoFadeBox;
+// QLabel *m_fadeInLabel;
+// QSpinBox *m_fadeInSpin;
+// QLabel *m_fadeOutLabel;
+// QSpinBox *m_fadeOutSpin;
+
+ int m_addColourPos;
+
+ // used to keep track of highest/lowest as there is no associated spinbox
+ // to query for its value
+ int m_highestPlayable;
+ int m_lowestPlayable;
+
+ std::vector<Segment*> m_segments;
+ std::vector<timeT> m_standardQuantizations;
+ std::vector<timeT> m_delays;
+ std::vector<int> m_realTimeDelays;
+ ColourTable::ColourList m_colourList;
+
+ RosegardenGUIDoc *m_doc;
+
+ MidiByte m_transposeRange;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/parameters/TrackParameterBox.cpp b/src/gui/editors/parameters/TrackParameterBox.cpp
new file mode 100644
index 0000000..fc85346
--- /dev/null
+++ b/src/gui/editors/parameters/TrackParameterBox.cpp
@@ -0,0 +1,1022 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ This file is Copyright 2006
+ Pedro Lopez-Cabanillas <[email protected]>
+ D. Michael McIntyre <[email protected]>
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TrackParameterBox.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "gui/general/ClefIndex.h"
+#include "document/ConfigGroups.h"
+#include "base/AudioPluginInstance.h"
+#include "base/Colour.h"
+#include "base/ColourMap.h"
+#include "base/Composition.h"
+#include "base/Device.h"
+#include "base/Exception.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/MidiProgram.h"
+#include "base/NotationTypes.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "base/StaffExportTypes.h"
+#include "commands/segment/SegmentSyncCommand.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/dialogs/PitchPickerDialog.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/PresetHandlerDialog.h"
+#include "gui/widgets/CollapsingFrame.h"
+#include "gui/widgets/ColourTable.h"
+#include "RosegardenParameterArea.h"
+#include "RosegardenParameterBox.h"
+#include "sound/PluginIdentifier.h"
+#include <kcolordialog.h>
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <klineeditdlg.h>
+#include <kmessagebox.h>
+#include <ksqueezedtextlabel.h>
+#include <ktabwidget.h>
+#include <qcolor.h>
+#include <qdialog.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <qscrollview.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qwidgetstack.h>
+#include <qcheckbox.h>
+
+
+namespace Rosegarden
+{
+
+TrackParameterBox::TrackParameterBox( RosegardenGUIDoc *doc,
+ QWidget *parent)
+ : RosegardenParameterBox(i18n("Track"),
+ i18n("Track Parameters"),
+ parent),
+ m_doc(doc),
+ m_highestPlayable(127),
+ m_lowestPlayable(0),
+ m_selectedTrackId( -1)
+{
+ QFont font(m_font);
+ QFont title_font(m_font);
+ QFontMetrics metrics(font);
+ int width11 = metrics.width("12345678901");
+ int width20 = metrics.width("12345678901234567890");
+ int width22 = metrics.width("1234567890123456789012");
+ int width25 = metrics.width("1234567890123456789012345");
+ setFont(m_font);
+ title_font.setBold(true);
+
+ // Set up default expansions for the collapsing elements
+ KConfig *config = kapp->config();
+ QString groupTemp = config->group();
+ config->setGroup("CollapsingFrame");
+ bool expanded = config->readBoolEntry("trackparametersplayback", true);
+ config->writeEntry("trackparametersplayback", expanded);
+ expanded = config->readBoolEntry("trackparametersrecord", false);
+ config->writeEntry("trackparametersrecord", expanded);
+ expanded = config->readBoolEntry("trackparametersdefaults", false);
+ config->writeEntry("trackparametersdefaults", expanded);
+ expanded = config->readBoolEntry("trackstaffgroup", false);
+ config->writeEntry("trackstaffgroup", expanded);
+ config->setGroup(groupTemp);
+
+ QGridLayout *mainLayout = new QGridLayout(this, 5, 1, 2, 1);
+
+ int row = 0;
+
+ // track label
+ //
+ m_trackLabel = new KSqueezedTextLabel(i18n("<untitled>"), this);
+ m_trackLabel->setAlignment(Qt::AlignCenter);
+ //mainLayout->addMultiCellWidget(m_trackLabel, 0, 0, 0, 5, AlignCenter);
+ mainLayout->addWidget(m_trackLabel, 0, 0);
+
+ // playback group
+ //
+ CollapsingFrame *cframe = new CollapsingFrame(i18n("Playback parameters"),
+ this, "trackparametersplayback");
+ m_playbackGroup = new QFrame(cframe);
+ cframe->setWidget(m_playbackGroup);
+ QGridLayout *groupLayout = new QGridLayout(m_playbackGroup, 3, 3, 3, 2);
+
+ // playback group title
+ //
+ row = 0;
+
+ // playback device
+ //
+ // row++;
+ QLabel *devLabel = new QLabel(i18n("Device"), m_playbackGroup);
+ groupLayout->addWidget(devLabel, row, 0);
+ m_playDevice = new KComboBox(m_playbackGroup);
+ m_playDevice->setMinimumWidth(width25);
+ groupLayout->addMultiCellWidget(m_playDevice, row, row, 1, 2);
+
+ // playback instrument
+ //
+ row++;
+ QLabel *insLabel = new QLabel(i18n("Instrument"), m_playbackGroup);
+ groupLayout->addMultiCellWidget(insLabel, row, row, 0, 1);
+ m_instrument = new KComboBox(m_playbackGroup);
+ m_instrument->setSizeLimit( 16 );
+ m_instrument->setMinimumWidth(width22);
+ groupLayout->addWidget(m_instrument, row, 2);
+
+ groupLayout->setColStretch(groupLayout->numCols() - 1, 1);
+
+ mainLayout->addWidget(cframe, 1, 0);
+
+ // record group
+ //
+ cframe = new CollapsingFrame(i18n("Recording filters"), this,
+ "trackparametersrecord");
+ m_recordGroup = new QFrame(cframe);
+ cframe->setWidget(m_recordGroup);
+ groupLayout = new QGridLayout(m_recordGroup, 3, 3, 3, 2);
+
+ // recording group title
+ //
+ row = 0;
+
+ // recording device
+ groupLayout->addWidget(new QLabel(i18n("Device"), m_recordGroup), row, 0);
+ m_recDevice = new KComboBox(m_recordGroup);
+ m_recDevice->setMinimumWidth(width25);
+ groupLayout->addMultiCellWidget(m_recDevice, row, row, 1, 2);
+
+ // recording channel
+ //
+ row++;
+ groupLayout->addMultiCellWidget(new QLabel(i18n("Channel"), m_recordGroup), row, row, 0, 1);
+ m_recChannel = new KComboBox(m_recordGroup);
+ m_recChannel->setSizeLimit( 17 );
+ m_recChannel->setMinimumWidth(width11);
+ groupLayout->addWidget(m_recChannel, row, 2);
+
+ groupLayout->setColStretch(groupLayout->numCols() - 1, 1);
+
+ mainLayout->addWidget(cframe, 2, 0);
+
+ // staff group
+ //
+ cframe = new CollapsingFrame(i18n("Staff export options"), this,
+ "staffoptions");
+ m_staffGroup = new QFrame(cframe);
+ cframe->setWidget(m_staffGroup);
+ groupLayout = new QGridLayout(m_staffGroup, 2, 2, 2, 2);
+
+ groupLayout->setColStretch(1, 1);
+
+ row = 0;
+
+ // Notation size (export only)
+ //
+ // NOTE: This is the only way to get a \small or \tiny inserted before the
+ // first note in LilyPond export. Setting the actual staff size on a
+ // per-staff (rather than per-score) basis is something the author of the
+ // LilyPond documentation has no idea how to do, so we settle for this,
+ // which is not as nice, but actually a lot easier to implement.
+ m_staffGrpLbl = new QLabel(i18n("Notation size:"), m_staffGroup);
+ groupLayout->addWidget(m_staffGrpLbl, row, 0, AlignLeft);
+ m_staffSizeCombo = new KComboBox(m_staffGroup);
+ m_staffSizeCombo->setMinimumWidth(width11);
+ m_staffSizeCombo->insertItem(i18n("Normal"), StaffTypes::Normal);
+ m_staffSizeCombo->insertItem(i18n("Small"), StaffTypes::Small);
+ m_staffSizeCombo->insertItem(i18n("Tiny"), StaffTypes::Tiny);
+
+ groupLayout->addMultiCellWidget(m_staffSizeCombo, row, row, 1, 2);
+
+ // Staff bracketing (export only at the moment, but using this for GUI
+ // rendering would be nice in the future!) //!!!
+ row++;
+ m_grandStaffLbl = new QLabel(i18n("Bracket type:"), m_staffGroup);
+ groupLayout->addWidget(m_grandStaffLbl, row, 0, AlignLeft);
+ m_staffBracketCombo = new KComboBox(m_staffGroup);
+ m_staffBracketCombo->setMinimumWidth(width11);
+ m_staffBracketCombo->insertItem(i18n("-----"), Brackets::None);
+ m_staffBracketCombo->insertItem(i18n("[----"), Brackets::SquareOn);
+ m_staffBracketCombo->insertItem(i18n("----]"), Brackets::SquareOff);
+ m_staffBracketCombo->insertItem(i18n("[---]"), Brackets::SquareOnOff);
+ m_staffBracketCombo->insertItem(i18n("{----"), Brackets::CurlyOn);
+ m_staffBracketCombo->insertItem(i18n("----}"), Brackets::CurlyOff);
+ m_staffBracketCombo->insertItem(i18n("{[---"), Brackets::CurlySquareOn);
+ m_staffBracketCombo->insertItem(i18n("---]}"), Brackets::CurlySquareOff);
+
+ groupLayout->addMultiCellWidget(m_staffBracketCombo, row, row, 1, 2);
+
+ mainLayout->addWidget(cframe, 3, 0);
+
+
+ // default segment group
+ //
+ cframe = new CollapsingFrame(i18n("Create segments with"), this,
+ "trackparametersdefaults");
+ m_defaultsGroup = new QFrame(cframe);
+ cframe->setWidget(m_defaultsGroup);
+ groupLayout = new QGridLayout(m_defaultsGroup, 6, 6, 3, 2);
+
+ groupLayout->setColStretch(1, 1);
+
+ row = 0;
+
+ // preset picker
+ m_psetLbl = new QLabel(i18n("Preset"), m_defaultsGroup);
+ groupLayout->addWidget(m_psetLbl, row, 0, AlignLeft);
+
+ m_presetLbl = new QLabel(i18n("<none>"), m_defaultsGroup);
+ m_presetLbl->setFrameStyle(QFrame::Panel | QFrame::Sunken);
+ m_presetLbl->setFixedWidth(width20);
+ groupLayout->addMultiCellWidget(m_presetLbl, row, row, 1, 3);
+
+ m_presetButton = new QPushButton(i18n("Load"), m_defaultsGroup);
+ groupLayout->addMultiCellWidget(m_presetButton, row, row, 4, 5);
+
+ // default clef
+ //
+ row++;
+ m_clefLbl = new QLabel(i18n("Clef"), m_defaultsGroup);
+ groupLayout->addWidget(m_clefLbl, row, 0, AlignLeft);
+ m_defClef = new KComboBox(m_defaultsGroup);
+ m_defClef->setMinimumWidth(width11);
+ m_defClef->insertItem(i18n("treble"), TrebleClef);
+ m_defClef->insertItem(i18n("bass"), BassClef);
+ m_defClef->insertItem(i18n("crotales"), CrotalesClef);
+ m_defClef->insertItem(i18n("xylophone"), XylophoneClef);
+ m_defClef->insertItem(i18n("guitar"), GuitarClef);
+ m_defClef->insertItem(i18n("contrabass"), ContrabassClef);
+ m_defClef->insertItem(i18n("celesta"), CelestaClef);
+ m_defClef->insertItem(i18n("old celesta"), OldCelestaClef);
+ m_defClef->insertItem(i18n("french"), FrenchClef);
+ m_defClef->insertItem(i18n("soprano"), SopranoClef);
+ m_defClef->insertItem(i18n("mezzosoprano"), MezzosopranoClef);
+ m_defClef->insertItem(i18n("alto"), AltoClef);
+ m_defClef->insertItem(i18n("tenor"), TenorClef);
+ m_defClef->insertItem(i18n("baritone"), BaritoneClef);
+ m_defClef->insertItem(i18n("varbaritone"), VarbaritoneClef);
+ m_defClef->insertItem(i18n("subbass"), SubbassClef);
+ /* clef types in the datbase that are not yet supported must be ignored for
+ * now:
+ m_defClef->insertItem(i18n("two bar"), TwoBarClef); */
+ groupLayout->addMultiCellWidget(m_defClef, row, row, 1, 2);
+
+ // default transpose
+ //
+ m_transpLbl = new QLabel(i18n("Transpose"), m_defaultsGroup);
+ groupLayout->addMultiCellWidget(m_transpLbl, row, row, 3, 4, AlignRight);
+ m_defTranspose = new KComboBox(m_defaultsGroup);
+
+ connect(m_defTranspose, SIGNAL(activated(int)),
+ SLOT(slotTransposeIndexChanged(int)));
+
+ int transposeRange = 48;
+ for (int i = -transposeRange; i < transposeRange + 1; i++) {
+ m_defTranspose->insertItem(QString("%1").arg(i));
+ if (i == 0)
+ m_defTranspose->setCurrentItem(m_defTranspose->count() - 1);
+ }
+
+ groupLayout->addMultiCellWidget(m_defTranspose, row, row, 5, 5);
+
+ // highest/lowest playable note
+ //
+ row++;
+ m_rangeLbl = new QLabel(i18n("Pitch"), m_defaultsGroup);
+ groupLayout->addMultiCellWidget(m_rangeLbl, row, row, 0, 0);
+
+ groupLayout->addWidget(new QLabel(i18n("Lowest"), m_defaultsGroup), row, 1, AlignRight);
+
+ m_lowButton = new QPushButton(i18n("---"), m_defaultsGroup);
+ QToolTip::add
+ (m_lowButton, i18n("Choose the lowest suggested playable note, using a staff"));
+ groupLayout->addMultiCellWidget(m_lowButton, row, row, 2, 2);
+
+ groupLayout->addWidget(new QLabel(i18n("Highest"), m_defaultsGroup), row, 3, AlignRight);
+
+ m_highButton = new QPushButton(i18n("---"), m_defaultsGroup);
+ QToolTip::add
+ (m_highButton, i18n("Choose the highest suggested playable note, using a staff"));
+ groupLayout->addMultiCellWidget(m_highButton, row, row, 4, 5);
+
+ updateHighLow();
+
+ // default color
+ //
+ row++;
+ m_colorLbl = new QLabel(i18n("Color"), m_defaultsGroup);
+ groupLayout->addWidget(m_colorLbl, row, 0, AlignLeft);
+ m_defColor = new KComboBox(false, m_defaultsGroup);
+ m_defColor->setSizeLimit(20);
+ groupLayout->addMultiCellWidget(m_defColor, row, row, 1, 5);
+
+ // populate combo from doc colors
+ slotDocColoursChanged();
+
+ mainLayout->addWidget(cframe, 4, 0);
+
+
+ // Configure the empty final row to accomodate any extra vertical space.
+ //
+// mainLayout->setColStretch(mainLayout->numCols() - 1, 1);
+ mainLayout->setRowStretch(mainLayout->numRows() - 1, 1);
+
+ // Connections
+ connect( m_playDevice, SIGNAL(activated(int)),
+ this, SLOT(slotPlaybackDeviceChanged(int)));
+
+ connect( m_instrument, SIGNAL(activated(int)),
+ this, SLOT(slotInstrumentChanged(int)));
+
+ connect( m_recDevice, SIGNAL(activated(int)),
+ this, SLOT(slotRecordingDeviceChanged(int)));
+
+ connect( m_recChannel, SIGNAL(activated(int)),
+ this, SLOT(slotRecordingChannelChanged(int)));
+
+ connect( m_defClef, SIGNAL(activated(int)),
+ this, SLOT(slotClefChanged(int)));
+
+ // Detect when the document colours are updated
+ connect(m_doc, SIGNAL(docColoursChanged()),
+ this, SLOT(slotDocColoursChanged()));
+
+ // handle colour combo changes
+ connect(m_defColor, SIGNAL(activated(int)),
+ SLOT(slotColorChanged(int)));
+
+ connect(m_highButton, SIGNAL(released()),
+ SLOT(slotHighestPressed()));
+
+ connect(m_lowButton, SIGNAL(released()),
+ SLOT(slotLowestPressed()));
+
+ connect(m_presetButton, SIGNAL(released()),
+ SLOT(slotPresetPressed()));
+
+ connect(m_staffSizeCombo, SIGNAL(activated(int)),
+ this, SLOT(slotStaffSizeChanged(int)));
+
+ connect(m_staffBracketCombo, SIGNAL(activated(int)),
+ this, SLOT(slotStaffBracketChanged(int)));
+}
+
+TrackParameterBox::~TrackParameterBox()
+{}
+
+void
+
+TrackParameterBox::setDocument( RosegardenGUIDoc *doc )
+{
+ if (m_doc != doc) {
+ RG_DEBUG << "TrackParameterBox::setDocument\n";
+ m_doc = doc;
+ populateDeviceLists();
+ }
+}
+
+void
+TrackParameterBox::populateDeviceLists()
+{
+ RG_DEBUG << "TrackParameterBox::populateDeviceLists()\n";
+ populatePlaybackDeviceList();
+ //populateRecordingDeviceList();
+ slotUpdateControls( -1);
+ m_lastInstrumentType = -1;
+}
+
+void
+TrackParameterBox::populatePlaybackDeviceList()
+{
+ RG_DEBUG << "TrackParameterBox::populatePlaybackDeviceList()\n";
+ m_playDevice->clear();
+ m_playDeviceIds.clear();
+ m_instrument->clear();
+ m_instrumentIds.clear();
+ m_instrumentNames.clear();
+
+ Studio &studio = m_doc->getStudio();
+
+ // Get the list
+ InstrumentList list = studio.getPresentationInstruments();
+ InstrumentList::iterator it;
+ int currentDevId = -1;
+
+ for (it = list.begin(); it != list.end(); it++) {
+
+ if (! (*it))
+ continue; // sanity check
+
+ //QString iname(strtoqstr((*it)->getPresentationName()));
+ QString iname(strtoqstr((*it)->getName()));
+ QString pname(strtoqstr((*it)->getProgramName()));
+ Device *device = (*it)->getDevice();
+ DeviceId devId = device->getId();
+
+ if ((*it)->getType() == Instrument::SoftSynth) {
+ iname.replace("Synth plugin ", "");
+ pname = "";
+ AudioPluginInstance *plugin = (*it)->getPlugin
+ (Instrument::SYNTH_PLUGIN_POSITION);
+ if (plugin) {
+ pname = strtoqstr(plugin->getProgram());
+ QString identifier = strtoqstr(plugin->getIdentifier());
+ if (identifier != "") {
+ QString type, soName, label;
+ PluginIdentifier::parseIdentifier
+ (identifier, type, soName, label);
+ if (pname == "") {
+ pname = strtoqstr(plugin->getDistinctiveConfigurationText());
+ }
+ if (pname != "") {
+ pname = QString("%1: %2").arg(label).arg(pname);
+ } else {
+ pname = label;
+ }
+ }
+ }
+ }
+
+ if (devId != (DeviceId)(currentDevId)) {
+ currentDevId = int(devId);
+ QString deviceName = strtoqstr(device->getName());
+ m_playDevice->insertItem(deviceName);
+ m_playDeviceIds.push_back(currentDevId);
+ }
+
+ if (pname != "")
+ iname += " (" + pname + ")";
+ m_instrumentIds[currentDevId].push_back((*it)->getId());
+ m_instrumentNames[currentDevId].append(iname);
+
+ }
+
+ m_playDevice->setCurrentItem( -1);
+ m_instrument->setCurrentItem( -1);
+}
+
+void
+TrackParameterBox::populateRecordingDeviceList()
+{
+ RG_DEBUG << "TrackParameterBox::populateRecordingDeviceList()\n";
+
+ if (m_selectedTrackId < 0)
+ return ;
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(m_selectedTrackId);
+ if (!trk)
+ return ;
+
+ Instrument *inst = m_doc->getStudio().getInstrumentById(trk->getInstrument());
+ if (!inst)
+ return ;
+
+ if (m_lastInstrumentType != (char)inst->getInstrumentType()) {
+ m_lastInstrumentType = (char)inst->getInstrumentType();
+
+ m_recDevice->clear();
+ m_recDeviceIds.clear();
+ m_recChannel->clear();
+
+ if (inst->getInstrumentType() == Instrument::Audio) {
+
+ m_recDeviceIds.push_back(Device::NO_DEVICE);
+ m_recDevice->insertItem(i18n("Audio"));
+ m_recChannel->insertItem(i18n("Audio"));
+
+ m_recDevice->setEnabled(false);
+ m_recChannel->setEnabled(false);
+
+ // hide these for audio instruments
+ m_defaultsGroup->parentWidget()->setShown(false);
+
+ } else { // InstrumentType::Midi and InstrumentType::SoftSynth
+
+ // show these if not audio instrument
+ m_defaultsGroup->parentWidget()->setShown(true);
+
+ m_recDeviceIds.push_back(Device::ALL_DEVICES);
+ m_recDevice->insertItem(i18n("All"));
+
+ DeviceList *devices = m_doc->getStudio().getDevices();
+ DeviceListConstIterator it;
+ for (it = devices->begin(); it != devices->end(); it++) {
+ MidiDevice *dev =
+ dynamic_cast<MidiDevice*>(*it);
+ if (dev) {
+ if (dev->getDirection() == MidiDevice::Record
+ && dev->isRecording()) {
+ QString connection = strtoqstr(dev->getConnection());
+ // remove trailing "(duplex)", "(read only)", "(write only)" etc
+ connection.replace(QRegExp("\\s*\\([^)0-9]+\\)\\s*$"), "");
+ m_recDevice->insertItem(connection);
+ m_recDeviceIds.push_back(dev->getId());
+ }
+ }
+ }
+
+ m_recChannel->insertItem(i18n("All"));
+ for (int i = 1; i < 17; ++i) {
+ m_recChannel->insertItem(QString::number(i));
+ }
+
+ m_recDevice->setEnabled(true);
+ m_recChannel->setEnabled(true);
+ }
+ }
+
+ if (inst->getInstrumentType() == Instrument::Audio) {
+ m_recDevice->setCurrentItem(0);
+ m_recChannel->setCurrentItem(0);
+ } else {
+ m_recDevice->setCurrentItem(0);
+ m_recChannel->setCurrentItem((int)trk->getMidiInputChannel() + 1);
+ for (unsigned int i = 0; i < m_recDeviceIds.size(); ++i) {
+ if (m_recDeviceIds[i] == trk->getMidiInputDevice()) {
+ m_recDevice->setCurrentItem(i);
+ break;
+ }
+ }
+ }
+}
+
+void
+TrackParameterBox::updateHighLow()
+{
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(comp.getSelectedTrack());
+ if (!trk)
+ return ;
+
+ trk->setHighestPlayable(m_highestPlayable);
+ trk->setLowestPlayable(m_lowestPlayable);
+
+ Accidental accidental = Accidentals::NoAccidental;
+
+ Pitch highest(m_highestPlayable, accidental);
+ Pitch lowest(m_lowestPlayable, accidental);
+
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ int base = config->readNumEntry("midipitchoctave", -2);
+ bool useSharps = true;
+ bool includeOctave = true;
+
+// m_highButton->setText(i18n("High: %1").arg(highest.getAsString(useSharps, includeOctave, base)));
+// m_lowButton->setText(i18n("Low: %1").arg(lowest.getAsString(useSharps, includeOctave, base)));
+ m_highButton->setText(QString("%1").arg(highest.getAsString(useSharps, includeOctave, base)));
+ m_lowButton->setText(QString("%1").arg(lowest.getAsString(useSharps, includeOctave, base)));
+
+ m_presetLbl->setEnabled(false);
+}
+
+void
+TrackParameterBox::slotUpdateControls(int /*dummy*/)
+{
+ RG_DEBUG << "TrackParameterBox::slotUpdateControls()\n";
+ slotPlaybackDeviceChanged( -1);
+ slotInstrumentChanged( -1);
+
+ if (m_selectedTrackId < 0)
+ return ;
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(m_selectedTrackId);
+ if (!trk)
+ return ;
+
+ m_defClef->setCurrentItem(trk->getClef());
+ m_defTranspose->setCurrentItem(QString("%1").arg(trk->getTranspose()), true);
+ m_defColor->setCurrentItem(trk->getColor());
+ m_highestPlayable = trk->getHighestPlayable();
+ m_lowestPlayable = trk->getLowestPlayable();
+ updateHighLow();
+ // set this down here because updateHighLow just disabled the label
+ m_presetLbl->setText(trk->getPresetLabel());
+ m_presetLbl->setEnabled(true);
+
+ m_staffSizeCombo->setCurrentItem(trk->getStaffSize());
+ m_staffBracketCombo->setCurrentItem(trk->getStaffBracket());
+}
+
+void
+TrackParameterBox::slotSelectedTrackChanged()
+{
+ RG_DEBUG << "TrackParameterBox::slotSelectedTrackChanged()\n";
+ Composition &comp = m_doc->getComposition();
+ TrackId newTrack = comp.getSelectedTrack();
+ if ( newTrack != m_selectedTrackId ) {
+ m_presetLbl->setEnabled(true);
+ m_selectedTrackId = newTrack;
+ slotSelectedTrackNameChanged();
+ slotUpdateControls( -1);
+ }
+}
+
+void
+TrackParameterBox::slotSelectedTrackNameChanged()
+{
+ RG_DEBUG << "TrackParameterBox::sotSelectedTrackNameChanged()\n";
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(m_selectedTrackId);
+ QString m_trackName = trk->getLabel();
+ if (m_trackName.isEmpty())
+ m_trackName = i18n("<untitled>");
+ else
+ m_trackName.truncate(20);
+ int trackNum = trk->getPosition() + 1;
+ m_trackLabel->setText(i18n("[ Track %1 - %2 ]").arg(trackNum).arg(m_trackName));
+}
+
+void
+TrackParameterBox::slotPlaybackDeviceChanged(int index)
+{
+ RG_DEBUG << "TrackParameterBox::slotPlaybackDeviceChanged(" << index << ")\n";
+ DeviceId devId;
+ if (index == -1) {
+ if (m_selectedTrackId < 0)
+ return ;
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(m_selectedTrackId);
+ if (!trk)
+ return ;
+ Instrument *inst = m_doc->getStudio().getInstrumentById(trk->getInstrument());
+ if (!inst)
+ return ;
+ devId = inst->getDevice()->getId();
+ int pos = -1;
+ IdsVector::const_iterator it;
+ for ( it = m_playDeviceIds.begin(); it != m_playDeviceIds.end(); ++it) {
+ pos++;
+ if ((*it) == devId)
+ break;
+ }
+ m_playDevice->setCurrentItem(pos);
+ } else {
+ devId = m_playDeviceIds[index];
+ }
+
+ m_instrument->clear();
+ m_instrument->insertStringList(m_instrumentNames[devId]);
+
+ populateRecordingDeviceList();
+
+ if (index != -1) {
+ m_instrument->setCurrentItem(0);
+ slotInstrumentChanged(0);
+ }
+}
+
+void
+TrackParameterBox::slotInstrumentChanged(int index)
+{
+ RG_DEBUG << "TrackParameterBox::slotInstrumentChanged(" << index << ")\n";
+ DeviceId devId;
+ Instrument *inst;
+ if (index == -1) {
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(comp.getSelectedTrack());
+ if (!trk)
+ return ;
+ inst = m_doc->getStudio().getInstrumentById(trk->getInstrument());
+ if (!inst)
+ return ;
+ devId = inst->getDevice()->getId();
+
+ int pos = -1;
+ IdsVector::const_iterator it;
+ for ( it = m_instrumentIds[devId].begin(); it != m_instrumentIds[devId].end(); ++it ) {
+ pos++;
+ if ((*it) == trk->getInstrument())
+ break;
+ }
+ m_instrument->setCurrentItem(pos);
+ } else {
+ devId = m_playDeviceIds[m_playDevice->currentItem()];
+ // set the new selected instrument for the track
+ int item = 0;
+ std::map<DeviceId, IdsVector>::const_iterator it;
+ for ( it = m_instrumentIds.begin(); it != m_instrumentIds.end(); ++it) {
+ if ( (*it).first == devId )
+ break;
+ item += (*it).second.size();
+ }
+ item += index;
+ RG_DEBUG << "TrackParameterBox::slotInstrumentChanged() item = " << item << "\n";
+ emit instrumentSelected( m_selectedTrackId, item );
+ }
+}
+
+void
+TrackParameterBox::slotRecordingDeviceChanged(int index)
+{
+ RG_DEBUG << "TrackParameterBox::slotRecordingDeviceChanged(" << index << ")" << endl;
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(comp.getSelectedTrack());
+ if (!trk)
+ return ;
+ Instrument *inst = m_doc->getStudio().getInstrumentById(trk->getInstrument());
+ if (!inst)
+ return ;
+ if (inst->getInstrumentType() == Instrument::Audio) {
+ //Not implemented yet
+ } else {
+ trk->setMidiInputDevice(m_recDeviceIds[index]);
+ }
+}
+
+void
+TrackParameterBox::slotRecordingChannelChanged(int index)
+{
+ RG_DEBUG << "TrackParameterBox::slotRecordingChannelChanged(" << index << ")" << endl;
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(comp.getSelectedTrack());
+ if (!trk)
+ return ;
+ Instrument *inst = m_doc->getStudio().getInstrumentById(trk->getInstrument());
+ if (!inst)
+ return ;
+ if (inst->getInstrumentType() == Instrument::Audio) {
+ //Not implemented yet
+ } else {
+ trk->setMidiInputChannel(index - 1);
+ }
+}
+
+void
+TrackParameterBox::slotInstrumentLabelChanged(InstrumentId id, QString label)
+{
+ RG_DEBUG << "TrackParameterBox::slotInstrumentLabelChanged(" << id << ") = " << label << "\n";
+ populatePlaybackDeviceList();
+ slotUpdateControls( -1);
+}
+
+void
+TrackParameterBox::showAdditionalControls(bool showThem)
+{
+ // m_defaultsGroup->parentWidget()->setShown(showThem);
+}
+
+void
+TrackParameterBox::slotClefChanged(int clef)
+{
+ RG_DEBUG << "TrackParameterBox::slotClefChanged(" << clef << ")" << endl;
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(comp.getSelectedTrack());
+ trk->setClef(clef);
+ m_presetLbl->setEnabled(false);
+}
+
+void
+TrackParameterBox::slotTransposeChanged(int transpose)
+{
+ RG_DEBUG << "TrackParameterBox::slotTransposeChanged(" << transpose << ")" << endl;
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(comp.getSelectedTrack());
+ trk->setTranspose(transpose);
+ m_presetLbl->setEnabled(false);
+}
+
+void
+TrackParameterBox::slotTransposeIndexChanged(int index)
+{
+ slotTransposeTextChanged(m_defTranspose->text(index));
+}
+
+void
+TrackParameterBox::slotTransposeTextChanged(QString text)
+{
+ if (text.isEmpty())
+ return ;
+ int value = text.toInt();
+ slotTransposeChanged(value);
+}
+
+void
+TrackParameterBox::slotDocColoursChanged()
+{
+ RG_DEBUG << "TrackParameterBox::slotDocColoursChanged()" << endl;
+
+ m_defColor->clear();
+ m_colourList.clear();
+ // Populate it from composition.m_segmentColourMap
+ ColourMap temp = m_doc->getComposition().getSegmentColourMap();
+
+ unsigned int i = 0;
+
+ for (RCMap::const_iterator it = temp.begin(); it != temp.end(); ++it) {
+ QString qtrunc(strtoqstr(it->second.second));
+ QPixmap colour(15, 15);
+ colour.fill(GUIPalette::convertColour(it->second.first));
+ if (qtrunc == "") {
+ m_defColor->insertItem(colour, i18n("Default"), i);
+ } else {
+ // truncate name to 15 characters to avoid the combo forcing the
+ // whole kit and kaboodle too wide
+ if (qtrunc.length() > 15)
+ qtrunc = qtrunc.left(12) + "...";
+ m_defColor->insertItem(colour, qtrunc, i);
+ }
+ m_colourList[it->first] = i; // maps colour number to menu index
+ ++i;
+ }
+
+ m_addColourPos = i;
+ m_defColor->insertItem(i18n("Add New Color"), m_addColourPos);
+
+ m_defColor->setCurrentItem(0);
+}
+
+void
+TrackParameterBox::slotColorChanged(int index)
+{
+ RG_DEBUG << "TrackParameterBox::slotColorChanged(" << index << ")" << endl;
+
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(comp.getSelectedTrack());
+
+ trk->setColor(index);
+
+ if (index == m_addColourPos) {
+ ColourMap newMap = m_doc->getComposition().getSegmentColourMap();
+ QColor newColour;
+ bool ok = false;
+ QString newName = KLineEditDlg::getText(i18n("New Color Name"), i18n("Enter new name"),
+ i18n("New"), &ok);
+ if ((ok == true) && (!newName.isEmpty())) {
+ KColorDialog box(this, "", true);
+
+ int result = box.getColor(newColour);
+
+ if (result == KColorDialog::Accepted) {
+ Colour newRColour = GUIPalette::convertColour(newColour);
+ newMap.addItem(newRColour, qstrtostr(newName));
+ slotDocColoursChanged();
+ }
+ }
+ // Else we don't do anything as they either didn't give a name�
+ // or didn't give a colour
+ }
+}
+
+void
+TrackParameterBox::slotHighestPressed()
+{
+ RG_DEBUG << "TrackParameterBox::slotHighestPressed()" << endl;
+
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(comp.getSelectedTrack());
+ if (!trk)
+ return ;
+
+ PitchPickerDialog dialog(0, m_highestPlayable, i18n("Highest playable note"));
+
+ if (dialog.exec() == QDialog::Accepted) {
+ m_highestPlayable = dialog.getPitch();
+ updateHighLow();
+ }
+
+ m_presetLbl->setEnabled(false);
+}
+
+void
+TrackParameterBox::slotLowestPressed()
+{
+ RG_DEBUG << "TrackParameterBox::slotLowestPressed()" << endl;
+
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(comp.getSelectedTrack());
+ if (!trk)
+ return ;
+
+ PitchPickerDialog dialog(0, m_lowestPlayable, i18n("Lowest playable note"));
+
+ if (dialog.exec() == QDialog::Accepted) {
+ m_lowestPlayable = dialog.getPitch();
+ updateHighLow();
+ }
+
+ m_presetLbl->setEnabled(false);
+}
+
+void
+TrackParameterBox::slotPresetPressed()
+{
+ RG_DEBUG << "TrackParameterBox::slotPresetPressed()" << endl;
+
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(comp.getSelectedTrack());
+ if (!trk)
+ return ;
+
+ PresetHandlerDialog dialog(this);
+
+ try {
+ if (dialog.exec() == QDialog::Accepted) {
+ m_presetLbl->setText(dialog.getName());
+ trk->setPresetLabel(dialog.getName());
+ if (dialog.getConvertAllSegments()) {
+ SegmentSyncCommand* command = new SegmentSyncCommand(
+ comp.getSegments(), comp.getSelectedTrack(),
+ dialog.getTranspose(), dialog.getLowRange(),
+ dialog.getHighRange(),
+ clefIndexToClef(dialog.getClef()));
+ m_doc->getCommandHistory()->addCommand(command);
+ }
+ m_defClef->setCurrentItem(dialog.getClef());
+ m_defTranspose->setCurrentItem(QString("%1").arg
+ (dialog.getTranspose()), true);
+
+ m_highestPlayable = dialog.getHighRange();
+ m_lowestPlayable = dialog.getLowRange();
+ updateHighLow();
+ slotClefChanged(dialog.getClef());
+ slotTransposeChanged(dialog.getTranspose());
+
+ // the preceding slots will have set this disabled, so we
+ // re-enable it until it is subsequently re-disabled by the
+ // user overriding the preset, calling one of the above slots
+ // in the normal course
+ m_presetLbl->setEnabled(true);
+ }
+ } catch (Exception e) {
+ //!!! This should be a more verbose error to pass along the
+ // row/column of the corruption, but I can't be bothered to work
+ // that out just at the moment. Hopefully this code will never
+ // execute anyway.
+ KMessageBox::sorry(0, i18n("The instrument preset database is corrupt. Check your installation."));
+ }
+
+}
+
+void
+TrackParameterBox::slotStaffSizeChanged(int index)
+{
+ RG_DEBUG << "TrackParameterBox::sotStaffSizeChanged()" << endl;
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(m_selectedTrackId);
+
+ trk->setStaffSize(index);
+}
+
+
+void
+TrackParameterBox::slotStaffBracketChanged(int index)
+{
+ RG_DEBUG << "TrackParameterBox::sotStaffBracketChanged()" << endl;
+ Composition &comp = m_doc->getComposition();
+ Track *trk = comp.getTrackById(m_selectedTrackId);
+
+ trk->setStaffBracket(index);
+}
+
+QString
+TrackParameterBox::getPreviousBox(RosegardenParameterArea::Arrangement arrangement) const
+{
+ if (arrangement == RosegardenParameterArea::CLASSIC_STYLE) {
+ return i18n("Segment");
+ } else {
+ return "";
+ }
+}
+
+}
+#include "TrackParameterBox.moc"
diff --git a/src/gui/editors/parameters/TrackParameterBox.h b/src/gui/editors/parameters/TrackParameterBox.h
new file mode 100644
index 0000000..c5fa0f9
--- /dev/null
+++ b/src/gui/editors/parameters/TrackParameterBox.h
@@ -0,0 +1,161 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ This file is Copyright 2006
+ Pedro Lopez-Cabanillas <[email protected]>
+ D. Michael McIntyre <[email protected]>
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRACKPARAMETERBOX_H_
+#define _RG_TRACKPARAMETERBOX_H_
+
+#include "base/MidiProgram.h"
+#include "base/Track.h"
+#include "gui/widgets/ColourTable.h"
+#include <map>
+#include "RosegardenParameterArea.h"
+#include "RosegardenParameterBox.h"
+#include <qstring.h>
+#include <qcheckbox.h> // #include <QCheckBox> in QT4, thinking ahead
+#include <vector>
+
+
+class QWidget;
+class QPushButton;
+class QLabel;
+class QFrame;
+class KComboBox;
+class QCheckBox;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+class TrackParameterBox : public RosegardenParameterBox
+{
+Q_OBJECT
+
+public:
+ TrackParameterBox( RosegardenGUIDoc *doc,
+ QWidget *parent=0);
+ ~TrackParameterBox();
+
+ void setDocument( RosegardenGUIDoc *doc );
+ void populateDeviceLists();
+ virtual void showAdditionalControls(bool showThem);
+
+ virtual QString getPreviousBox(RosegardenParameterArea::Arrangement) const;
+
+public slots:
+ void slotSelectedTrackChanged();
+ void slotSelectedTrackNameChanged();
+ void slotPlaybackDeviceChanged(int index);
+ void slotInstrumentChanged(int index);
+ void slotRecordingDeviceChanged(int index);
+ void slotRecordingChannelChanged(int index);
+ void slotUpdateControls(int);
+ void slotInstrumentLabelChanged(InstrumentId id, QString label);
+
+ void slotClefChanged(int clef);
+ void slotTransposeChanged(int transpose);
+ void slotTransposeIndexChanged(int index);
+ void slotTransposeTextChanged(QString text);
+ void slotDocColoursChanged();
+ void slotColorChanged(int index);
+ void slotHighestPressed();
+ void slotLowestPressed();
+ void slotPresetPressed();
+
+ void slotStaffSizeChanged(int index);
+ void slotStaffBracketChanged(int index);
+
+signals:
+ void instrumentSelected(TrackId, int);
+
+protected:
+ void populatePlaybackDeviceList();
+ void populateRecordingDeviceList();
+ void updateHighLow();
+
+private:
+ RosegardenGUIDoc *m_doc;
+
+ KComboBox *m_playDevice;
+ KComboBox *m_instrument;
+ KComboBox *m_recDevice;
+ KComboBox *m_recChannel;
+
+ QPushButton *m_presetButton;
+ QPushButton *m_highButton;
+ QPushButton *m_lowButton;
+
+ KComboBox *m_defClef;
+ KComboBox *m_defColor;
+ KComboBox *m_defTranspose;
+ KComboBox *m_staffSizeCombo;
+ KComboBox *m_staffBracketCombo;
+
+
+ int m_addColourPos;
+ int m_highestPlayable;
+ int m_lowestPlayable;
+ ColourTable::ColourList m_colourList;
+
+ QLabel *m_trackLabel;
+
+ typedef std::vector<DeviceId> IdsVector;
+
+ IdsVector m_playDeviceIds;
+ IdsVector m_recDeviceIds;
+
+ std::map<DeviceId, IdsVector> m_instrumentIds;
+ std::map<DeviceId, QStringList> m_instrumentNames;
+
+ int m_selectedTrackId;
+
+ char m_lastInstrumentType;
+
+ // Additional elements that may be hidden in vertical stacked mode
+ //QFrame *m_separator2;
+ QFrame *m_playbackGroup;
+ QFrame *m_recordGroup;
+ QFrame *m_defaultsGroup;
+ QFrame *m_staffGroup;
+ QLabel *m_segHeader;
+ QLabel *m_presetLbl;
+ QLabel *m_staffGrpLbl;
+ QLabel *m_grandStaffLbl;
+ QLabel *m_clefLbl;
+ QLabel *m_transpLbl;
+ QLabel *m_colorLbl;
+ QLabel *m_rangeLbl;
+ QLabel *m_psetLbl;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/ControlEditorDialog.cpp b/src/gui/editors/segment/ControlEditorDialog.cpp
new file mode 100644
index 0000000..3c4cc47
--- /dev/null
+++ b/src/gui/editors/segment/ControlEditorDialog.cpp
@@ -0,0 +1,446 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "ControlEditorDialog.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Colour.h"
+#include "base/Composition.h"
+#include "base/ControlParameter.h"
+#include "base/Device.h"
+#include "base/Event.h"
+#include "base/MidiDevice.h"
+#include "base/MidiTypes.h"
+#include "base/Studio.h"
+#include "commands/studio/AddControlParameterCommand.h"
+#include "commands/studio/ModifyControlParameterCommand.h"
+#include "commands/studio/RemoveControlParameterCommand.h"
+#include "ControlParameterEditDialog.h"
+#include "ControlParameterItem.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include <kaction.h>
+#include <kcommand.h>
+#include <klistview.h>
+#include <kmainwindow.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <qcolor.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qlistview.h>
+#include <qpixmap.h>
+#include <qptrlist.h>
+#include <qpushbutton.h>
+#include <qsizepolicy.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+const QString notShowing(i18n("<not showing>"));
+
+ControlEditorDialog::ControlEditorDialog(QWidget *parent,
+ RosegardenGUIDoc *doc,
+ DeviceId device):
+ KMainWindow(parent, "controleditordialog"),
+ m_studio(&doc->getStudio()),
+ m_doc(doc),
+ m_device(device),
+ m_modified(false)
+{
+ RG_DEBUG << "ControlEditorDialog::ControlEditorDialog: device is " << m_device << endl;
+
+ QVBox* mainFrame = new QVBox(this);
+ setCentralWidget(mainFrame);
+
+ setCaption(i18n("Manage Control Events"));
+
+ QString deviceName(i18n("<no device>"));
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(m_studio->getDevice(m_device));
+ if (md)
+ deviceName = strtoqstr(md->getName());
+
+ // spacing hack!
+ new QLabel("", mainFrame);
+ new QLabel(i18n(" Control Events for %1 (device %2)").arg(deviceName).
+ arg(device), mainFrame);
+ new QLabel("", mainFrame);
+
+ m_listView = new KListView(mainFrame);
+ m_listView->addColumn(i18n("Control Event name "));
+ m_listView->addColumn(i18n("Control Event type "));
+ m_listView->addColumn(i18n("Control Event value "));
+ m_listView->addColumn(i18n("Description "));
+ m_listView->addColumn(i18n("Min "));
+ m_listView->addColumn(i18n("Max "));
+ m_listView->addColumn(i18n("Default "));
+ m_listView->addColumn(i18n("Color "));
+ m_listView->addColumn(i18n("Position on instrument panel"));
+
+ m_listView->setColumnAlignment(0, Qt::AlignLeft);
+
+ // Align remaining columns centrally
+ for (int i = 1; i < 9; ++i)
+ m_listView->setColumnAlignment(i, Qt::AlignHCenter);
+
+ m_listView->restoreLayout(kapp->config(), ControlEditorConfigGroup);
+
+ QFrame* btnBox = new QFrame(mainFrame);
+
+ btnBox->setSizePolicy(
+ QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
+
+ QHBoxLayout* layout = new QHBoxLayout(btnBox, 4, 10);
+
+ m_addButton = new QPushButton(i18n("Add"), btnBox);
+ m_deleteButton = new QPushButton(i18n("Delete"), btnBox);
+
+ m_closeButton = new QPushButton(i18n("Close"), btnBox);
+
+ QToolTip::add
+ (m_addButton,
+ i18n("Add a Control Parameter to the Studio"));
+
+ QToolTip::add
+ (m_deleteButton,
+ i18n("Delete a Control Parameter from the Studio"));
+
+ QToolTip::add
+ (m_closeButton,
+ i18n("Close the Control Parameter editor"));
+
+ layout->addStretch(10);
+ layout->addWidget(m_addButton);
+ layout->addWidget(m_deleteButton);
+ layout->addSpacing(30);
+
+ layout->addWidget(m_closeButton);
+ layout->addSpacing(5);
+
+ connect(m_addButton, SIGNAL(released()),
+ SLOT(slotAdd()));
+
+ connect(m_deleteButton, SIGNAL(released()),
+ SLOT(slotDelete()));
+
+ setupActions();
+
+ m_doc->getCommandHistory()->attachView(actionCollection());
+ connect(m_doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(slotUpdate()));
+
+ connect(m_listView, SIGNAL(doubleClicked(QListViewItem *)),
+ SLOT(slotEdit(QListViewItem *)));
+
+ // Highlight all columns - enable extended selection mode
+ //
+ m_listView->setAllColumnsShowFocus(true);
+ m_listView->setSelectionMode(QListView::Extended);
+
+ initDialog();
+
+ setAutoSaveSettings(ControlEditorConfigGroup, true);
+}
+
+ControlEditorDialog::~ControlEditorDialog()
+{
+ RG_DEBUG << "\n*** ControlEditorDialog::~ControlEditorDialog\n" << endl;
+
+ m_listView->saveLayout(kapp->config(), ControlEditorConfigGroup);
+
+ if (m_doc)
+ m_doc->getCommandHistory()->detachView(actionCollection());
+}
+
+void
+ControlEditorDialog::initDialog()
+{
+ RG_DEBUG << "ControlEditorDialog::initDialog" << endl;
+ slotUpdate();
+}
+
+void
+ControlEditorDialog::slotUpdate()
+{
+ RG_DEBUG << "ControlEditorDialog::slotUpdate" << endl;
+
+ //QPtrList<QListViewItem> selection = m_listView->selectedItems();
+
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(m_studio->getDevice(m_device));
+ if (!md)
+ return ;
+
+ ControlList::const_iterator it = md->beginControllers();
+ QListViewItem *item;
+ int i = 0;
+
+ m_listView->clear();
+
+ for (; it != md->endControllers(); ++it) {
+ Composition &comp = m_doc->getComposition();
+
+ QString colour =
+ strtoqstr(comp.getGeneralColourMap().getNameByIndex(it->getColourIndex()));
+
+ if (colour == "")
+ colour = i18n("<default>");
+
+ QString position = QString("%1").arg(it->getIPBPosition());
+ if (position.toInt() == -1)
+ position = notShowing;
+
+ QString value;
+ value.sprintf("%d (0x%x)", it->getControllerValue(),
+ it->getControllerValue());
+
+ if (it->getType() == PitchBend::EventType) {
+ item = new ControlParameterItem(i++,
+ m_listView,
+ strtoqstr(it->getName()),
+ strtoqstr(it->getType()),
+ QString("-"),
+ strtoqstr(it->getDescription()),
+ QString("%1").arg(it->getMin()),
+ QString("%1").arg(it->getMax()),
+ QString("%1").arg(it->getDefault()),
+ colour,
+ position);
+ } else {
+ item = new ControlParameterItem(i++,
+ m_listView,
+ strtoqstr(it->getName()),
+ strtoqstr(it->getType()),
+ value,
+ strtoqstr(it->getDescription()),
+ QString("%1").arg(it->getMin()),
+ QString("%1").arg(it->getMax()),
+ QString("%1").arg(it->getDefault()),
+ colour,
+ position);
+ }
+
+
+ // create and set a colour pixmap
+ //
+ QPixmap colourPixmap(16, 16);
+ Colour c = comp.getGeneralColourMap().getColourByIndex(it->getColourIndex());
+ colourPixmap.fill(QColor(c.getRed(), c.getGreen(), c.getBlue()));
+ item->setPixmap(7, colourPixmap);
+
+ m_listView->insertItem(item);
+ }
+
+ if (m_listView->childCount() == 0) {
+ QListViewItem *item = new QListViewItem(m_listView, i18n("<none>"));
+ m_listView->insertItem(item);
+
+ m_listView->setSelectionMode(QListView::NoSelection);
+ } else {
+ m_listView->setSelectionMode(QListView::Extended);
+ }
+
+
+}
+
+/*
+void
+ControlEditorDialog::slotEditCopy()
+{
+ RG_DEBUG << "ControlEditorDialog::slotEditCopy" << endl;
+}
+
+void
+ControlEditorDialog::slotEditPaste()
+{
+ RG_DEBUG << "ControlEditorDialog::slotEditPaste" << endl;
+}
+*/
+
+void
+ControlEditorDialog::slotAdd()
+{
+ RG_DEBUG << "ControlEditorDialog::slotAdd to device " << m_device << endl;
+
+ AddControlParameterCommand *command =
+ new AddControlParameterCommand(m_studio, m_device,
+ ControlParameter());
+
+ addCommandToHistory(command);
+}
+
+void
+ControlEditorDialog::slotDelete()
+{
+ RG_DEBUG << "ControlEditorDialog::slotDelete" << endl;
+
+ if (!m_listView->currentItem())
+ return ;
+
+ ControlParameterItem *item =
+ dynamic_cast<ControlParameterItem*>(m_listView->currentItem());
+
+ if (item) {
+ RemoveControlParameterCommand *command =
+ new RemoveControlParameterCommand(m_studio, m_device, item->getId());
+
+ addCommandToHistory(command);
+ }
+}
+
+void
+ControlEditorDialog::slotClose()
+{
+ RG_DEBUG << "ControlEditorDialog::slotClose" << endl;
+
+ if (m_doc)
+ m_doc->getCommandHistory()->detachView(actionCollection());
+ m_doc = 0;
+
+ close();
+}
+
+void
+ControlEditorDialog::setupActions()
+{
+ KAction* close = KStdAction::close(this,
+ SLOT(slotClose()),
+ actionCollection());
+
+ m_closeButton->setText(close->text());
+ connect(m_closeButton, SIGNAL(released()), this, SLOT(slotClose()));
+
+ // some adjustments
+ new KToolBarPopupAction(i18n("Und&o"),
+ "undo",
+ KStdAccel::key(KStdAccel::Undo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Undo));
+
+ new KToolBarPopupAction(i18n("Re&do"),
+ "redo",
+ KStdAccel::key(KStdAccel::Redo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Redo));
+
+ createGUI("controleditor.rc");
+}
+
+void
+ControlEditorDialog::addCommandToHistory(KCommand *command)
+{
+ getCommandHistory()->addCommand(command);
+ setModified(false);
+}
+
+MultiViewCommandHistory*
+ControlEditorDialog::getCommandHistory()
+{
+ return m_doc->getCommandHistory();
+}
+
+void
+ControlEditorDialog::setModified(bool modified)
+{
+ RG_DEBUG << "ControlEditorDialog::setModified(" << modified << ")" << endl;
+
+ if (modified) {}
+ else {}
+
+ m_modified = modified;
+}
+
+void
+ControlEditorDialog::checkModified()
+{
+ RG_DEBUG << "ControlEditorDialog::checkModified(" << m_modified << ")"
+ << endl;
+
+}
+
+void
+ControlEditorDialog::slotEdit()
+{}
+
+void
+ControlEditorDialog::slotEdit(QListViewItem *i)
+{
+ RG_DEBUG << "ControlEditorDialog::slotEdit" << endl;
+
+ ControlParameterItem *item =
+ dynamic_cast<ControlParameterItem*>(i);
+
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(m_studio->getDevice(m_device));
+
+ if (item && md) {
+ ControlParameterEditDialog dialog
+ (this,
+ md->getControlParameter(item->getId()), m_doc);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ ModifyControlParameterCommand *command =
+ new ModifyControlParameterCommand(m_studio,
+ m_device,
+ dialog.getControl(),
+ item->getId());
+
+ addCommandToHistory(command);
+ }
+ }
+}
+
+void
+ControlEditorDialog::closeEvent(QCloseEvent *e)
+{
+ emit closing();
+ KMainWindow::closeEvent(e);
+}
+
+void
+ControlEditorDialog::setDocument(RosegardenGUIDoc *doc)
+{
+ // reset our pointers
+ m_doc = doc;
+ m_studio = &doc->getStudio();
+ m_modified = false;
+
+ slotUpdate();
+}
+
+}
+#include "ControlEditorDialog.moc"
diff --git a/src/gui/editors/segment/ControlEditorDialog.h b/src/gui/editors/segment/ControlEditorDialog.h
new file mode 100644
index 0000000..9270d2c
--- /dev/null
+++ b/src/gui/editors/segment/ControlEditorDialog.h
@@ -0,0 +1,122 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_CONTROLEDITORDIALOG_H_
+#define _RG_CONTROLEDITORDIALOG_H_
+
+#include "base/Device.h"
+#include "base/MidiDevice.h"
+#include <kmainwindow.h>
+
+
+class QWidget;
+class QPushButton;
+class QListViewItem;
+class QCloseEvent;
+class KListView;
+class KCommand;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class RosegardenGUIDoc;
+class MultiViewCommandHistory;
+
+
+class ControlEditorDialog : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ ControlEditorDialog(QWidget *parent,
+ RosegardenGUIDoc *doc,
+ DeviceId device);
+
+ ~ControlEditorDialog();
+
+ void initDialog();
+
+ void addCommandToHistory(KCommand *command);
+ MultiViewCommandHistory* getCommandHistory();
+
+ void setModified(bool value);
+ void checkModified();
+
+ // reset the document
+ void setDocument(RosegardenGUIDoc *doc);
+
+ DeviceId getDevice() { return m_device; }
+
+public slots:
+ void slotUpdate();
+
+/*
+ void slotEditCopy();
+ void slotEditPaste();
+*/
+
+ void slotAdd();
+ void slotDelete();
+ void slotClose();
+
+ void slotEdit();
+ void slotEdit(QListViewItem *);
+
+signals:
+ void closing();
+
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+
+ void setupActions();
+
+ //--------------- Data members ---------------------------------
+ Studio *m_studio;
+ RosegardenGUIDoc *m_doc;
+ DeviceId m_device;
+
+ QPushButton *m_closeButton;
+
+ QPushButton *m_copyButton;
+ QPushButton *m_pasteButton;
+
+ QPushButton *m_addButton;
+ QPushButton *m_deleteButton;
+
+ KListView *m_listView;
+
+ bool m_modified;
+
+ ControlList m_clipboard; // local clipboard only
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/ControlParameterEditDialog.cpp b/src/gui/editors/segment/ControlParameterEditDialog.cpp
new file mode 100644
index 0000000..bc779f5
--- /dev/null
+++ b/src/gui/editors/segment/ControlParameterEditDialog.cpp
@@ -0,0 +1,325 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "ControlParameterEditDialog.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Colour.h"
+#include "base/ColourMap.h"
+#include "base/ControlParameter.h"
+#include "base/Event.h"
+#include "base/MidiTypes.h"
+#include "document/RosegardenGUIDoc.h"
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <qcolor.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qpixmap.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+const QString notShowing(i18n("<not showing>"));
+
+ControlParameterEditDialog::ControlParameterEditDialog(
+ QWidget *parent,
+ ControlParameter *control,
+ RosegardenGUIDoc *doc):
+ KDialogBase(parent, 0, true,
+ i18n("Edit Control Parameter"), Ok | Cancel),
+ m_doc(doc),
+ m_control(control)
+{
+ m_dialogControl = *control; // copy in the ControlParameter
+
+ QVBox *vbox = makeVBoxMainWidget();
+
+ QGroupBox *groupBox = new QGroupBox
+ (1, Horizontal, i18n("Control Event Properties"), vbox);
+
+ QFrame *frame = new QFrame(groupBox);
+
+ QGridLayout *layout = new QGridLayout(frame, 4, 3, 10, 5);
+
+ layout->addWidget(new QLabel(i18n("Name:"), frame), 0, 0);
+ m_nameEdit = new QLineEdit(frame);
+ layout->addWidget(m_nameEdit, 0, 1);
+
+ layout->addWidget(new QLabel(i18n("Type:"), frame), 1, 0);
+ m_typeCombo = new KComboBox(frame);
+ layout->addMultiCellWidget(m_typeCombo, 1, 1, 1, 2);
+
+ layout->addWidget(new QLabel(i18n("Description:"), frame), 2, 0);
+ m_description = new QLineEdit(frame);
+ layout->addMultiCellWidget(m_description, 2, 2, 1, 2);
+
+ // hex value alongside decimal value
+ m_hexValue = new QLabel(frame);
+ layout->addWidget(m_hexValue, 3, 1);
+
+ layout->addWidget(new QLabel(i18n("Control Event value:"), frame), 3, 0);
+ m_controllerBox = new QSpinBox(frame);
+ layout->addWidget(m_controllerBox, 3, 2);
+
+ layout->addWidget(new QLabel(i18n("Minimum value:"), frame), 4, 0);
+ m_minBox = new QSpinBox(frame);
+ layout->addMultiCellWidget(m_minBox, 4, 4, 1, 2);
+
+ layout->addWidget(new QLabel(i18n("Maximum value:"), frame), 5, 0);
+ m_maxBox = new QSpinBox(frame);
+ layout->addMultiCellWidget(m_maxBox, 5, 5, 1, 2);
+
+ layout->addWidget(new QLabel(i18n("Default value:"), frame), 6, 0);
+ m_defaultBox = new QSpinBox(frame);
+ layout->addMultiCellWidget(m_defaultBox, 6, 6, 1, 2);
+
+ layout->addWidget(new QLabel(i18n("Color:"), frame), 7, 0);
+ m_colourCombo = new KComboBox(frame);
+ layout->addMultiCellWidget(m_colourCombo, 7, 7, 1, 2);
+
+ layout->addWidget(new QLabel(i18n("Instrument Parameter Box position:"), frame), 8, 0);
+ m_ipbPosition = new KComboBox(frame);
+ layout->addMultiCellWidget(m_ipbPosition, 8, 8, 1, 2);
+
+ connect(m_nameEdit, SIGNAL(textChanged(const QString&)),
+ SLOT(slotNameChanged(const QString&)));
+
+ connect(m_typeCombo, SIGNAL(activated(int)),
+ SLOT(slotTypeChanged(int)));
+
+ connect(m_description, SIGNAL(textChanged(const QString&)),
+ SLOT(slotDescriptionChanged(const QString &)));
+
+ connect(m_controllerBox, SIGNAL(valueChanged(int)),
+ SLOT(slotControllerChanged(int)));
+
+ connect(m_minBox, SIGNAL(valueChanged(int)),
+ SLOT(slotMinChanged(int)));
+
+ connect(m_maxBox, SIGNAL(valueChanged(int)),
+ SLOT(slotMaxChanged(int)));
+
+ connect(m_defaultBox, SIGNAL(valueChanged(int)),
+ SLOT(slotDefaultChanged(int)));
+
+ connect(m_colourCombo, SIGNAL(activated(int)),
+ SLOT(slotColourChanged(int)));
+
+ connect(m_ipbPosition, SIGNAL(activated(int)),
+ SLOT(slotIPBPositionChanged(int)));
+
+ //m_nameEdit->selectAll();
+ //m_description->selectAll();
+
+ // set limits
+ m_controllerBox->setMinValue(0);
+ m_controllerBox->setMaxValue(127);
+
+ m_minBox->setMinValue(INT_MIN);
+ m_minBox->setMaxValue(INT_MAX);
+
+ m_maxBox->setMinValue(INT_MIN);
+ m_maxBox->setMaxValue(INT_MAX);
+
+ m_defaultBox->setMinValue(INT_MIN);
+ m_defaultBox->setMaxValue(INT_MAX);
+
+ // populate combos
+ m_typeCombo->insertItem(strtoqstr(Controller::EventType));
+ m_typeCombo->insertItem(strtoqstr(PitchBend::EventType));
+ /*
+ m_typeCombo->insertItem(strtoqstr(KeyPressure::EventType));
+ m_typeCombo->insertItem(strtoqstr(ChannelPressure::EventType));
+ */
+
+ // Populate colour combo
+ //
+ //
+ ColourMap &colourMap = m_doc->getComposition().getGeneralColourMap();
+ RCMap::const_iterator it;
+ QPixmap colourPixmap(16, 16);
+
+ for (it = colourMap.begin(); it != colourMap.end(); ++it) {
+ Colour c = it->second.first;
+ colourPixmap.fill(QColor(c.getRed(), c.getGreen(), c.getBlue()));
+ m_colourCombo->insertItem(colourPixmap, strtoqstr(it->second.second));
+ }
+
+ // Populate IPB position combo
+ //
+ m_ipbPosition->insertItem(notShowing);
+ for (unsigned int i = 0; i < 32; i++)
+ m_ipbPosition->insertItem(QString("%1").arg(i));
+
+ if (m_control->getType() == Controller::EventType)
+ m_typeCombo->setCurrentItem(0);
+ else if (m_control->getType() == PitchBend::EventType)
+ m_typeCombo->setCurrentItem(1);
+ /*
+ else if (m_control->getType() == KeyPressure::EventType)
+ m_typeCombo->setCurrentItem(2);
+ else if (m_control->getType() == ChannelPressure::EventType)
+ m_typeCombo->setCurrentItem(3);
+ */
+
+ populate();
+}
+
+void
+ControlParameterEditDialog::populate()
+{
+ m_nameEdit->setText(strtoqstr(m_control->getName()));
+
+ m_description->setText(strtoqstr(m_control->getDescription()));
+ m_controllerBox->setValue(int(m_control->getControllerValue()));
+
+ QString hexValue;
+ hexValue.sprintf("(0x%x)", m_control->getControllerValue());
+ m_hexValue->setText(hexValue);
+
+ m_minBox->setValue(m_control->getMin());
+ m_maxBox->setValue(m_control->getMax());
+ m_defaultBox->setValue(m_control->getDefault());
+
+ int pos = 0, setItem = 0;
+ ColourMap &colourMap = m_doc->getComposition().getGeneralColourMap();
+ RCMap::const_iterator it;
+ for (it = colourMap.begin(); it != colourMap.end(); ++it)
+ if (m_control->getColourIndex() == it->first)
+ setItem = pos++;
+
+ m_colourCombo->setCurrentItem(setItem);
+
+ // set combo position
+ m_ipbPosition->setCurrentItem(m_control->getIPBPosition() + 1);
+
+ // If the type has changed and there are no defaults then we have to
+ // supply some.
+ //
+ if (qstrtostr(m_typeCombo->currentText()) == PitchBend::EventType ||
+ qstrtostr(m_typeCombo->currentText()) == KeyPressure::EventType ||
+ qstrtostr(m_typeCombo->currentText()) == ChannelPressure::EventType) {
+ m_controllerBox->setEnabled(false);
+ m_ipbPosition->setEnabled(false);
+ m_colourCombo->setEnabled(false);
+ m_hexValue->setEnabled(false);
+ m_minBox->setEnabled(false);
+ m_maxBox->setEnabled(false);
+ m_defaultBox->setEnabled(false);
+ } else if (qstrtostr(m_typeCombo->currentText()) == Controller::EventType) {
+ m_controllerBox->setEnabled(true);
+ m_ipbPosition->setEnabled(true);
+ m_colourCombo->setEnabled(true);
+ m_hexValue->setEnabled(true);
+ m_minBox->setEnabled(true);
+ m_maxBox->setEnabled(true);
+ m_defaultBox->setEnabled(true);
+ }
+
+}
+
+void
+ControlParameterEditDialog::slotNameChanged(const QString &str)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotNameChanged" << endl;
+ m_dialogControl.setName(qstrtostr(str));
+}
+
+void
+ControlParameterEditDialog::slotTypeChanged(int value)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotTypeChanged" << endl;
+ m_dialogControl.setType(qstrtostr(m_typeCombo->text(value)));
+
+ populate();
+}
+
+void
+ControlParameterEditDialog::slotDescriptionChanged(const QString &str)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotDescriptionChanged" << endl;
+ m_dialogControl.setDescription(qstrtostr(str));
+}
+
+void
+ControlParameterEditDialog::slotControllerChanged(int value)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotControllerChanged" << endl;
+ m_dialogControl.setControllerValue(value);
+
+ // set hex value
+ QString hexValue;
+ hexValue.sprintf("(0x%x)", value);
+ m_hexValue->setText(hexValue);
+}
+
+void
+ControlParameterEditDialog::slotMinChanged(int value)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotMinChanged" << endl;
+ m_dialogControl.setMin(value);
+}
+
+void
+ControlParameterEditDialog::slotMaxChanged(int value)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotMaxChanged" << endl;
+ m_dialogControl.setMax(value);
+}
+
+void
+ControlParameterEditDialog::slotDefaultChanged(int value)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotDefaultChanged" << endl;
+ m_dialogControl.setDefault(value);
+}
+
+void
+ControlParameterEditDialog::slotColourChanged(int value)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotColourChanged" << endl;
+ m_dialogControl.setColourIndex(value);
+}
+
+void
+ControlParameterEditDialog::slotIPBPositionChanged(int value)
+{
+ RG_DEBUG << "ControlParameterEditDialog::slotIPBPositionChanged" << endl;
+ m_dialogControl.setIPBPosition(value - 1);
+}
+
+}
+#include "ControlParameterEditDialog.moc"
diff --git a/src/gui/editors/segment/ControlParameterEditDialog.h b/src/gui/editors/segment/ControlParameterEditDialog.h
new file mode 100644
index 0000000..b9f4606
--- /dev/null
+++ b/src/gui/editors/segment/ControlParameterEditDialog.h
@@ -0,0 +1,92 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_CONTROLPARAMETEREDITDIALOG_H_
+#define _RG_CONTROLPARAMETEREDITDIALOG_H_
+
+#include "base/ControlParameter.h"
+#include <kdialogbase.h>
+
+
+class QWidget;
+class QString;
+class QSpinBox;
+class QLineEdit;
+class QLabel;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+class ControlParameterEditDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ ControlParameterEditDialog(QWidget *parent,
+ ControlParameter *control,
+ RosegardenGUIDoc *doc);
+
+ ControlParameter& getControl() { return m_dialogControl; }
+
+public slots:
+
+ void slotNameChanged(const QString &);
+ void slotTypeChanged(int);
+ void slotDescriptionChanged(const QString &);
+ void slotControllerChanged(int);
+ void slotMinChanged(int);
+ void slotMaxChanged(int);
+ void slotDefaultChanged(int);
+ void slotColourChanged(int);
+ void slotIPBPositionChanged(int);
+
+protected:
+ void populate(); // populate the dialog
+
+ RosegardenGUIDoc *m_doc;
+ ControlParameter *m_control;
+ ControlParameter m_dialogControl;
+
+ QLineEdit *m_nameEdit;
+ KComboBox *m_typeCombo;
+ QLineEdit *m_description;
+ QSpinBox *m_controllerBox;
+ QSpinBox *m_minBox;
+ QSpinBox *m_maxBox;
+ QSpinBox *m_defaultBox;
+ KComboBox *m_colourCombo;
+ KComboBox *m_ipbPosition;
+ QLabel *m_hexValue;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/ControlParameterItem.cpp b/src/gui/editors/segment/ControlParameterItem.cpp
new file mode 100644
index 0000000..beb0922
--- /dev/null
+++ b/src/gui/editors/segment/ControlParameterItem.cpp
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "ControlParameterItem.h"
+
+#include <qlistview.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/editors/segment/ControlParameterItem.h b/src/gui/editors/segment/ControlParameterItem.h
new file mode 100644
index 0000000..6746ca2
--- /dev/null
+++ b/src/gui/editors/segment/ControlParameterItem.h
@@ -0,0 +1,65 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_CONTROLPARAMETERITEM_H_
+#define _RG_CONTROLPARAMETERITEM_H_
+
+#include <qstring.h>
+#include <klistview.h>
+
+
+namespace Rosegarden
+{
+
+
+class ControlParameterItem : public KListViewItem
+{
+public:
+ ControlParameterItem(int id,
+ QListView *parent,
+ QString str1,
+ QString str2,
+ QString str3,
+ QString str4,
+ QString str5,
+ QString str6,
+ QString str7,
+ QString str8,
+ QString str9):
+ KListViewItem(parent, str1, str2, str3, str4, str5, str6, str7, str8),
+ m_id(id) { setText(8, str9); }
+
+ int getId() const { return m_id; }
+
+protected:
+
+ int m_id;
+ QString m_string9;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/MarkerEditor.cpp b/src/gui/editors/segment/MarkerEditor.cpp
new file mode 100644
index 0000000..61caaa7
--- /dev/null
+++ b/src/gui/editors/segment/MarkerEditor.cpp
@@ -0,0 +1,594 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MarkerEditor.h"
+#include "MarkerEditorViewItem.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include <kstdaccel.h>
+#include <kconfig.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Composition.h"
+#include "base/Marker.h"
+#include "base/RealTime.h"
+#include "commands/edit/AddMarkerCommand.h"
+#include "commands/edit/ModifyMarkerCommand.h"
+#include "commands/edit/RemoveMarkerCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/dialogs/MarkerModifyDialog.h"
+#include <kaction.h>
+#include <kcommand.h>
+#include <kglobal.h>
+#include <klistview.h>
+#include <kmainwindow.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <qaccel.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qiconset.h>
+#include <qlabel.h>
+#include <qlistview.h>
+#include <qptrlist.h>
+#include <qpushbutton.h>
+#include <qsizepolicy.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qcanvas.h>
+
+
+namespace Rosegarden
+{
+
+MarkerEditor::MarkerEditor(QWidget *parent,
+ RosegardenGUIDoc *doc):
+ KMainWindow(parent, "markereditordialog"),
+ m_doc(doc),
+ m_modified(false)
+{
+ QVBox* mainFrame = new QVBox(this);
+ setCentralWidget(mainFrame);
+
+ setCaption(i18n("Manage Markers"));
+
+ m_listView = new KListView(mainFrame);
+ m_listView->addColumn(i18n("Marker time "));
+ m_listView->addColumn(i18n("Marker text "));
+ m_listView->addColumn(i18n("Marker description "));
+
+ // Align centrally
+ for (int i = 0; i < 3; ++i)
+ m_listView->setColumnAlignment(i, Qt::AlignHCenter);
+
+ QGroupBox *posGroup = new QGroupBox(2, Horizontal,
+ i18n("Pointer position"), mainFrame);
+
+ new QLabel(i18n("Absolute time:"), posGroup);
+ m_absoluteTime = new QLabel(posGroup);
+
+ new QLabel(i18n("Real time:"), posGroup);
+ m_realTime = new QLabel(posGroup);
+
+ new QLabel(i18n("In measure:"), posGroup);
+ m_barTime = new QLabel(posGroup);
+
+ QFrame* btnBox = new QFrame(mainFrame);
+
+ btnBox->setSizePolicy(
+ QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
+
+ QHBoxLayout* layout = new QHBoxLayout(btnBox, 4, 10);
+
+ m_addButton = new QPushButton(i18n("Add"), btnBox);
+ m_deleteButton = new QPushButton(i18n("Delete"), btnBox);
+ m_deleteAllButton = new QPushButton(i18n("Delete All"), btnBox);
+
+ m_closeButton = new QPushButton(i18n("Close"), btnBox);
+
+ QToolTip::add
+ (m_addButton,
+ i18n("Add a Marker"));
+
+ QToolTip::add
+ (m_deleteButton,
+ i18n("Delete a Marker"));
+
+ QToolTip::add
+ (m_deleteAllButton,
+ i18n("Delete All Markers"));
+
+ QToolTip::add
+ (m_closeButton,
+ i18n("Close the Marker Editor"));
+
+ layout->addStretch(10);
+ layout->addWidget(m_addButton);
+ layout->addWidget(m_deleteButton);
+ layout->addWidget(m_deleteAllButton);
+ layout->addSpacing(30);
+
+ layout->addWidget(m_closeButton);
+ layout->addSpacing(5);
+
+ connect(m_addButton, SIGNAL(released()),
+ SLOT(slotAdd()));
+
+ connect(m_deleteButton, SIGNAL(released()),
+ SLOT(slotDelete()));
+
+ connect(m_closeButton, SIGNAL(released()),
+ SLOT(slotClose()));
+
+ connect(m_deleteAllButton, SIGNAL(released()),
+ SLOT(slotDeleteAll()));
+
+ setupActions();
+
+ m_doc->getCommandHistory()->attachView(actionCollection());
+ connect(m_doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(slotUpdate()));
+
+ connect(m_listView, SIGNAL(doubleClicked(QListViewItem *)),
+ SLOT(slotEdit(QListViewItem *)));
+
+ connect(m_listView, SIGNAL(pressed(QListViewItem *)),
+ this, SLOT(slotItemClicked(QListViewItem *)));
+
+ // Highlight all columns - enable extended selection mode
+ //
+ m_listView->setAllColumnsShowFocus(true);
+ m_listView->setSelectionMode(QListView::Extended);
+ m_listView->setItemsRenameable(true);
+
+ initDialog();
+
+ setAutoSaveSettings(MarkerEditorConfigGroup, true);
+
+ m_accelerators = new QAccel(this);
+}
+
+void
+MarkerEditor::updatePosition()
+{
+ timeT pos = m_doc->getComposition().getPosition();
+ m_absoluteTime->setText(QString("%1").arg(pos));
+
+ RealTime rT = m_doc->getComposition().getElapsedRealTime(pos);
+ long hours = rT.sec / (60 * 60);
+ long mins = rT.sec / 60;
+ long secs = rT.sec;
+ long msecs = rT.msec();
+
+ QString realTime, secsStr;
+ if (hours)
+ realTime += QString("%1h ").arg(hours);
+ if (mins)
+ realTime += QString("%1m ").arg(mins);
+ secsStr.sprintf("%ld.%03lds", secs, msecs);
+ realTime += secsStr;
+
+ // only update if we need to to try and avoid flickering
+ if (m_realTime->text() != realTime)
+ m_realTime->setText(realTime);
+
+ QString barTime =
+ QString("%1").arg(m_doc->getComposition().getBarNumber(pos) + 1);
+
+ // again only update if needed
+ if (m_barTime->text() != barTime)
+ m_barTime->setText(barTime);
+
+ /*
+ // Don't allow us to add another marker if there's already one
+ // at the current position.
+ //
+ if (m_doc->getComposition().
+ isMarkerAtPosition(m_doc->getComposition().getPosition()))
+ m_addButton->setEnabled(false);
+ else
+ m_addButton->setEnabled(true);
+ */
+}
+
+MarkerEditor::~MarkerEditor()
+{
+ RG_DEBUG << "MarkerEditor::~MarkerEditor" << endl;
+
+ m_listView->saveLayout(kapp->config(), MarkerEditorConfigGroup);
+
+ if (m_doc)
+ m_doc->getCommandHistory()->detachView(actionCollection());
+}
+
+void
+MarkerEditor::initDialog()
+{
+ RG_DEBUG << "MarkerEditor::initDialog" << endl;
+ slotUpdate();
+}
+
+void
+MarkerEditor::slotUpdate()
+{
+ RG_DEBUG << "MarkerEditor::slotUpdate" << endl;
+
+ //QPtrList<QListViewItem> selection = m_listView->selectedItems();
+
+ MarkerEditorViewItem *item;
+
+ m_listView->clear();
+
+ Composition::markercontainer markers =
+ m_doc->getComposition().getMarkers();
+
+ Composition::markerconstiterator it;
+
+ kapp->config()->setGroup(MarkerEditorConfigGroup);
+ int timeMode = kapp->config()->readNumEntry("timemode", 0);
+
+ for (it = markers.begin(); it != markers.end(); ++it) {
+ QString timeString = makeTimeString((*it)->getTime(), timeMode);
+
+ item = new
+ MarkerEditorViewItem(m_listView,
+ (*it)->getID(),
+ timeString,
+ strtoqstr((*it)->getName()),
+ strtoqstr((*it)->getDescription()));
+
+ // Set this for the MarkerEditor
+ //
+ item->setRawTime((*it)->getTime());
+
+ m_listView->insertItem(item);
+ }
+
+ if (m_listView->childCount() == 0) {
+ QListViewItem *item =
+ new MarkerEditorViewItem(m_listView, 0, i18n("<none>"));
+ ((MarkerEditorViewItem *)item)->setFake(true);
+ m_listView->insertItem(item);
+
+ m_listView->setSelectionMode(QListView::NoSelection);
+ } else {
+ m_listView->setSelectionMode(QListView::Extended);
+ }
+
+ updatePosition();
+
+}
+
+void
+MarkerEditor::slotDeleteAll()
+{
+ RG_DEBUG << "MarkerEditor::slotDeleteAll" << endl;
+ KMacroCommand *command = new KMacroCommand(i18n("Remove all markers"));
+
+ QListViewItem *item = m_listView->firstChild();
+
+ do {
+ MarkerEditorViewItem *ei =
+ dynamic_cast<MarkerEditorViewItem *>(item);
+ if (!ei || ei->isFake())
+ continue;
+
+ RemoveMarkerCommand *rc =
+ new RemoveMarkerCommand(&m_doc->getComposition(),
+ ei->getID(),
+ ei->getRawTime(),
+ qstrtostr(item->text(1)),
+ qstrtostr(item->text(2)));
+ command->addCommand(rc);
+ } while ((item = item->nextSibling()));
+
+ addCommandToHistory(command);
+}
+
+void
+MarkerEditor::slotAdd()
+{
+ RG_DEBUG << "MarkerEditor::slotAdd" << endl;
+
+ AddMarkerCommand *command =
+ new AddMarkerCommand(&m_doc->getComposition(),
+ m_doc->getComposition().getPosition(),
+ std::string("new marker"),
+ std::string("no description"));
+
+ addCommandToHistory(command);
+}
+
+void
+MarkerEditor::slotDelete()
+{
+ RG_DEBUG << "MarkerEditor::slotDelete" << endl;
+ QListViewItem *item = m_listView->currentItem();
+
+ MarkerEditorViewItem *ei =
+ dynamic_cast<MarkerEditorViewItem *>(item);
+
+ if (!ei || ei->isFake())
+ return ;
+
+ RemoveMarkerCommand *command =
+ new RemoveMarkerCommand(&m_doc->getComposition(),
+ ei->getID(),
+ ei->getRawTime(),
+ qstrtostr(item->text(1)),
+ qstrtostr(item->text(2)));
+
+ addCommandToHistory(command);
+
+}
+
+void
+MarkerEditor::slotClose()
+{
+ RG_DEBUG << "MarkerEditor::slotClose" << endl;
+
+ if (m_doc)
+ m_doc->getCommandHistory()->detachView(actionCollection());
+ m_doc = 0;
+
+ close();
+}
+
+void
+MarkerEditor::setupActions()
+{
+ KAction* close = KStdAction::close(this,
+ SLOT(slotClose()),
+ actionCollection());
+
+ m_closeButton->setText(close->text());
+ connect(m_closeButton, SIGNAL(released()), this, SLOT(slotClose()));
+
+ // some adjustments
+ new KToolBarPopupAction(i18n("Und&o"),
+ "undo",
+ KStdAccel::key(KStdAccel::Undo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Undo));
+
+ new KToolBarPopupAction(i18n("Re&do"),
+ "redo",
+ KStdAccel::key(KStdAccel::Redo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Redo));
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ kapp->config()->setGroup(MarkerEditorConfigGroup);
+ int timeMode = kapp->config()->readNumEntry("timemode", 0);
+
+ KRadioAction *action;
+
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/time-musical.png");
+ QIconSet icon(pixmap);
+
+ action = new KRadioAction(i18n("&Musical Times"), icon, 0, this,
+ SLOT(slotMusicalTime()),
+ actionCollection(), "time_musical");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 0)
+ action->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/time-real.png");
+ icon = QIconSet(pixmap);
+
+ action = new KRadioAction(i18n("&Real Times"), icon, 0, this,
+ SLOT(slotRealTime()),
+ actionCollection(), "time_real");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 1)
+ action->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/time-raw.png");
+ icon = QIconSet(pixmap);
+
+ action = new KRadioAction(i18n("Ra&w Times"), icon, 0, this,
+ SLOT(slotRawTime()),
+ actionCollection(), "time_raw");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 2)
+ action->setChecked(true);
+
+ createGUI("markereditor.rc");
+}
+
+void
+MarkerEditor::addCommandToHistory(KCommand *command)
+{
+ getCommandHistory()->addCommand(command);
+ setModified(false);
+}
+
+MultiViewCommandHistory*
+MarkerEditor::getCommandHistory()
+{
+ return m_doc->getCommandHistory();
+}
+
+void
+MarkerEditor::setModified(bool modified)
+{
+ RG_DEBUG << "MarkerEditor::setModified(" << modified << ")" << endl;
+
+ if (modified) {}
+ else {}
+
+ m_modified = modified;
+}
+
+void
+MarkerEditor::checkModified()
+{
+ RG_DEBUG << "MarkerEditor::checkModified(" << m_modified << ")"
+ << endl;
+
+}
+
+void
+MarkerEditor::slotEdit(QListViewItem *i)
+{
+ RG_DEBUG << "MarkerEditor::slotEdit" << endl;
+
+ if (m_listView->selectionMode() == QListView::NoSelection) {
+ // The marker list is empty, so we shouldn't allow editing the
+ // <none> placeholder
+ return ;
+ }
+
+ // Need to get the raw time from the ListViewItem
+ //
+ MarkerEditorViewItem *item =
+ dynamic_cast<MarkerEditorViewItem*>(i);
+
+ if (!item || item->isFake())
+ return ;
+
+ MarkerModifyDialog dialog(this,
+ &m_doc->getComposition(),
+ item->getRawTime(),
+ item->text(1),
+ item->text(2));
+
+ if (dialog.exec() == QDialog::Accepted) {
+ ModifyMarkerCommand *command =
+ new ModifyMarkerCommand(&m_doc->getComposition(),
+ item->getID(),
+ dialog.getOriginalTime(),
+ dialog.getTime(),
+ qstrtostr(dialog.getName()),
+ qstrtostr(dialog.getDescription()));
+
+ addCommandToHistory(command);
+ }
+
+
+}
+
+void
+MarkerEditor::closeEvent(QCloseEvent *e)
+{
+ emit closing();
+ KMainWindow::closeEvent(e);
+}
+
+void
+MarkerEditor::setDocument(RosegardenGUIDoc *doc)
+{
+ // reset our pointers
+ m_doc = doc;
+ m_modified = false;
+
+ slotUpdate();
+}
+
+void
+MarkerEditor::slotItemClicked(QListViewItem *item)
+{
+ RG_DEBUG << "MarkerEditor::slotItemClicked" << endl;
+ MarkerEditorViewItem *ei =
+ dynamic_cast<MarkerEditorViewItem *>(item);
+
+ if (ei && !ei->isFake()) {
+ RG_DEBUG << "MarkerEditor::slotItemClicked - "
+ << "jump to marker at " << ei->getRawTime() << endl;
+
+ emit jumpToMarker(timeT(ei->getRawTime()));
+ }
+}
+
+QString
+MarkerEditor::makeTimeString(timeT time, int timeMode)
+{
+ switch (timeMode) {
+
+ case 0: // musical time
+ {
+ int bar, beat, fraction, remainder;
+ m_doc->getComposition().getMusicalTimeForAbsoluteTime
+ (time, bar, beat, fraction, remainder);
+ ++bar;
+ return QString("%1%2%3-%4%5-%6%7-%8%9 ")
+ .arg(bar / 100)
+ .arg((bar % 100) / 10)
+ .arg(bar % 10)
+ .arg(beat / 10)
+ .arg(beat % 10)
+ .arg(fraction / 10)
+ .arg(fraction % 10)
+ .arg(remainder / 10)
+ .arg(remainder % 10);
+ }
+
+ case 1: // real time
+ {
+ RealTime rt =
+ m_doc->getComposition().getElapsedRealTime(time);
+ // return QString("%1 ").arg(rt.toString().c_str());
+ return QString("%1 ").arg(rt.toText().c_str());
+ }
+
+ default:
+ return QString("%1 ").arg(time);
+ }
+}
+
+void
+MarkerEditor::slotMusicalTime()
+{
+ kapp->config()->setGroup(MarkerEditorConfigGroup);
+ kapp->config()->writeEntry("timemode", 0);
+ slotUpdate();
+}
+
+void
+MarkerEditor::slotRealTime()
+{
+ kapp->config()->setGroup(MarkerEditorConfigGroup);
+ kapp->config()->writeEntry("timemode", 1);
+ slotUpdate();
+}
+
+void
+MarkerEditor::slotRawTime()
+{
+ kapp->config()->setGroup(MarkerEditorConfigGroup);
+ kapp->config()->writeEntry("timemode", 2);
+ slotUpdate();
+}
+
+}
+#include "MarkerEditor.moc"
diff --git a/src/gui/editors/segment/MarkerEditor.h b/src/gui/editors/segment/MarkerEditor.h
new file mode 100644
index 0000000..d3c9ac7
--- /dev/null
+++ b/src/gui/editors/segment/MarkerEditor.h
@@ -0,0 +1,124 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MARKEREDITOR_H_
+#define _RG_MARKEREDITOR_H_
+
+#include <kmainwindow.h>
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QPushButton;
+class QListViewItem;
+class QLabel;
+class QCloseEvent;
+class QAccel;
+class KListView;
+class KCommand;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class MultiViewCommandHistory;
+
+
+class MarkerEditor : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ MarkerEditor(QWidget *parent,
+ RosegardenGUIDoc *doc);
+ ~MarkerEditor();
+
+ void initDialog();
+
+ void addCommandToHistory(KCommand *command);
+ MultiViewCommandHistory* getCommandHistory();
+
+ void setModified(bool value);
+ void checkModified();
+
+ // reset the document
+ void setDocument(RosegardenGUIDoc *doc);
+
+ // update pointer position
+ void updatePosition();
+
+ QAccel* getAccelerators() { return m_accelerators; }
+
+public slots:
+ void slotUpdate();
+
+ void slotAdd();
+ void slotDelete();
+ void slotDeleteAll();
+ void slotClose();
+ void slotEdit(QListViewItem *);
+ void slotItemClicked(QListViewItem *);
+
+ void slotMusicalTime();
+ void slotRealTime();
+ void slotRawTime();
+
+signals:
+ void closing();
+ void jumpToMarker(timeT);
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+
+ void setupActions();
+ QString makeTimeString(timeT time, int timeMode);
+
+ //--------------- Data members ---------------------------------
+ RosegardenGUIDoc *m_doc;
+
+ QLabel *m_absoluteTime;
+ QLabel *m_realTime;
+ QLabel *m_barTime;
+
+ QPushButton *m_closeButton;
+
+
+ QPushButton *m_addButton;
+ QPushButton *m_deleteButton;
+ QPushButton *m_deleteAllButton;
+
+ KListView *m_listView;
+
+ bool m_modified;
+
+ QAccel *m_accelerators;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/MarkerEditorViewItem.cpp b/src/gui/editors/segment/MarkerEditorViewItem.cpp
new file mode 100644
index 0000000..9ff2bda
--- /dev/null
+++ b/src/gui/editors/segment/MarkerEditorViewItem.cpp
@@ -0,0 +1,51 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "MarkerEditorViewItem.h"
+
+namespace Rosegarden {
+
+int
+MarkerEditorViewItem::compare(QListViewItem * i, int col, bool ascending) const
+{
+ MarkerEditorViewItem *ei =
+ dynamic_cast<MarkerEditorViewItem *>(i);
+
+ if (!ei) return KListViewItem::compare(i, col, ascending);
+
+ // Raw time sorting on time column
+ //
+ if (col == 0) {
+
+ if (m_rawTime < ei->getRawTime()) return -1;
+ else if (ei->getRawTime() < m_rawTime) return 1;
+ else return 0;
+
+ } else {
+ return KListViewItem::compare(i, col, ascending);
+ }
+}
+
+}
+
diff --git a/src/gui/editors/segment/MarkerEditorViewItem.h b/src/gui/editors/segment/MarkerEditorViewItem.h
new file mode 100644
index 0000000..010d227
--- /dev/null
+++ b/src/gui/editors/segment/MarkerEditorViewItem.h
@@ -0,0 +1,70 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MARKEREDITORVIEWITEM_H_
+#define _RG_MARKEREDITORVIEWITEM_H_
+
+#include <klistview.h>
+
+#include "base/Event.h"
+
+namespace Rosegarden {
+
+
+class MarkerEditorViewItem : public KListViewItem
+{
+public:
+ MarkerEditorViewItem(QListView * parent, int id,
+ QString label1,
+ QString label2 = QString::null,
+ QString label3 = QString::null,
+ QString label4 = QString::null,
+ QString label5 = QString::null,
+ QString label6 = QString::null,
+ QString label7 = QString::null,
+ QString label8 = QString::null):
+ KListViewItem(parent, label1, label2, label3, label4,
+ label5, label6, label7, label8),
+ m_rawTime(0), m_fake(false), m_id(id) { ; }
+
+ virtual int compare(QListViewItem * i, int col, bool ascending) const;
+
+ void setRawTime(Rosegarden::timeT rawTime) { m_rawTime = rawTime; }
+ Rosegarden::timeT getRawTime() const { return m_rawTime; }
+
+ void setFake(bool fake) { m_fake = true; }
+ bool isFake() const { return m_fake; }
+
+ int getID() const { return m_id; }
+
+protected:
+ Rosegarden::timeT m_rawTime;
+ bool m_fake;
+ int m_id;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/PlayList.cpp b/src/gui/editors/segment/PlayList.cpp
new file mode 100644
index 0000000..bfc795c
--- /dev/null
+++ b/src/gui/editors/segment/PlayList.cpp
@@ -0,0 +1,254 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "PlayList.h"
+#include "PlayListView.h"
+#include "PlayListViewItem.h"
+#include "document/ConfigGroups.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <kurl.h>
+#include <qframe.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qstrlist.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qdragobject.h>
+
+
+namespace Rosegarden
+{
+
+PlayList::PlayList(QWidget *parent, const char *name)
+ : QVBox(parent, name),
+ m_listView(new PlayListView(this)),
+ m_buttonBar(new QFrame(this)),
+ m_barLayout(new QHBoxLayout(m_buttonBar)),
+ m_playButton(0),
+ m_moveUpButton(0),
+ m_moveDownButton(0),
+ m_deleteButton(0),
+ m_clearButton(0)
+{
+ m_openButton = new QPushButton(m_buttonBar);
+ m_playButton = new QPushButton(m_buttonBar);
+ m_moveUpButton = new QPushButton(m_buttonBar);
+ m_moveDownButton = new QPushButton(m_buttonBar);
+ m_deleteButton = new QPushButton(m_buttonBar);
+ m_clearButton = new QPushButton(m_buttonBar);
+ m_barLayout->addWidget(m_openButton);
+ m_barLayout->addWidget(m_playButton);
+ m_barLayout->addWidget(m_moveUpButton);
+ m_barLayout->addWidget(m_moveDownButton);
+ m_barLayout->addWidget(m_deleteButton);
+ m_barLayout->addWidget(m_clearButton);
+ m_barLayout->addStretch();
+
+
+ m_openButton ->setText(i18n("Add..."));
+ m_playButton ->setText(i18n("Play"));
+ m_moveUpButton ->setText(i18n("Move Up"));
+ m_moveDownButton->setText(i18n("Move Down"));
+ m_deleteButton ->setText(i18n("Delete"));
+ m_clearButton ->setText(i18n("Clear"));
+
+ connect(m_openButton, SIGNAL(clicked()),
+ SLOT(slotOpenFiles()));
+
+ connect(m_playButton, SIGNAL(clicked()),
+ SLOT(slotPlay()));
+
+ connect(m_deleteButton, SIGNAL(clicked()),
+ SLOT(slotDeleteCurrent()));
+
+ connect(m_clearButton, SIGNAL(clicked()),
+ SLOT(slotClear()));
+
+ connect(m_moveUpButton, SIGNAL(clicked()),
+ SLOT(slotMoveUp()));
+
+ connect(m_moveDownButton, SIGNAL(clicked()),
+ SLOT(slotMoveDown()));
+
+ connect(m_listView, SIGNAL(currentChanged(QListViewItem*)),
+ SLOT(slotCurrentItemChanged(QListViewItem*)));
+
+ connect(m_listView, SIGNAL(dropped(QDropEvent*, QListViewItem*)),
+ SLOT(slotDropped(QDropEvent*, QListViewItem*)));
+
+ restore();
+
+ enableButtons(0);
+
+}
+
+PlayList::~PlayList()
+{
+ save();
+}
+
+void PlayList::slotOpenFiles()
+{
+ KURL::List kurlList =
+ KFileDialog::getOpenURLs(":ROSEGARDEN",
+ "audio/x-rosegarden audio/x-midi audio/x-rosegarden21",
+ this,
+ i18n("Select one or more Rosegarden files"));
+
+ KURL::List::iterator it;
+
+ for (it = kurlList.begin(); it != kurlList.end(); ++it) {
+ new PlayListViewItem(m_listView, *it);
+ }
+
+ enableButtons(m_listView->currentItem());
+}
+
+void
+PlayList::slotDropped(QDropEvent *event, QListViewItem* after)
+{
+ QStrList uri;
+
+ // see if we can decode a URI.. if not, just ignore it
+ if (QUriDrag::decode(event, uri)) {
+
+ // okay, we have a URI.. process it
+ // weed out non-rg files
+ //
+ for (QString url = uri.first(); url; url = uri.next()) {
+ if (url.right(3).lower() == ".rg")
+ new PlayListViewItem(m_listView, after, KURL(url));
+
+ }
+ }
+
+ enableButtons(m_listView->currentItem());
+}
+
+void PlayList::slotPlay()
+{
+ PlayListViewItem *currentItem = dynamic_cast<PlayListViewItem*>(m_listView->currentItem());
+
+ if (currentItem)
+ emit play(currentItem->getURL().url());
+}
+
+void PlayList::slotMoveUp()
+{
+ QListViewItem *currentItem = m_listView->currentItem();
+ QListViewItem *previousItem = m_listView->previousSibling(currentItem);
+
+ if (previousItem)
+ previousItem->moveItem(currentItem);
+
+ enableButtons(currentItem);
+}
+
+void PlayList::slotMoveDown()
+{
+ QListViewItem *currentItem = m_listView->currentItem();
+ QListViewItem *nextItem = currentItem->nextSibling();
+
+ if (nextItem)
+ currentItem->moveItem(nextItem);
+
+ enableButtons(currentItem);
+}
+
+void PlayList::slotClear()
+{
+ m_listView->clear();
+ enableButtons(0);
+}
+
+void PlayList::slotDeleteCurrent()
+{
+ QListViewItem* currentItem = m_listView->currentItem();
+ if (currentItem)
+ delete currentItem;
+}
+
+void PlayList::slotCurrentItemChanged(QListViewItem* currentItem)
+{
+ enableButtons(currentItem);
+}
+
+void PlayList::enableButtons(QListViewItem* currentItem)
+{
+ bool enable = (currentItem != 0);
+
+ m_playButton->setEnabled(enable);
+ m_deleteButton->setEnabled(enable);
+
+ if (currentItem) {
+ m_moveUpButton->setEnabled(currentItem != m_listView->firstChild());
+ m_moveDownButton->setEnabled(currentItem != m_listView->lastItem());
+ } else {
+ m_moveUpButton->setEnabled(false);
+ m_moveDownButton->setEnabled(false);
+ }
+
+ m_clearButton->setEnabled(m_listView->childCount() > 0);
+}
+
+void PlayList::save()
+{
+ QStringList urlList;
+ PlayListViewItem* item = dynamic_cast<PlayListViewItem*>(getListView()->firstChild());
+
+ while (item) {
+ urlList << item->getURL().url();
+ item = dynamic_cast<PlayListViewItem*>(item->nextSibling());
+ }
+
+ KConfig *kc = KGlobal::config();
+ KConfigGroupSaver cs(kc, PlayListConfigGroup);
+ kc->writeEntry("Playlist Files", urlList);
+
+ getListView()->saveLayout(kc, PlayListConfigGroup);
+}
+
+void PlayList::restore()
+{
+ KConfig *kc = KGlobal::config();
+ getListView()->restoreLayout(kc, PlayListConfigGroup);
+
+ KConfigGroupSaver cs(kc, PlayListConfigGroup);
+ QStringList urlList = kc->readListEntry("Playlist Files");
+
+ for (QStringList::Iterator it = urlList.begin();
+ it != urlList.end(); ++it) {
+ new PlayListViewItem(getListView(), KURL(*it));
+ }
+}
+
+}
+#include "PlayList.moc"
diff --git a/src/gui/editors/segment/PlayList.h b/src/gui/editors/segment/PlayList.h
new file mode 100644
index 0000000..8e40c8c
--- /dev/null
+++ b/src/gui/editors/segment/PlayList.h
@@ -0,0 +1,93 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PLAYLIST_H_
+#define _RG_PLAYLIST_H_
+
+#include <qvbox.h>
+
+
+class QWidget;
+class QPushButton;
+class QListViewItem;
+class QHBoxLayout;
+class QFrame;
+class QDropEvent;
+
+
+namespace Rosegarden
+{
+
+class PlayListView;
+
+
+class PlayList : public QVBox
+{
+ Q_OBJECT
+
+public:
+ PlayList(QWidget *parent = 0, const char *name = 0);
+ ~PlayList();
+
+ PlayListView* getListView() { return m_listView; }
+
+ void enableButtons(QListViewItem*);
+
+
+signals:
+ void play(QString);
+
+protected slots:
+ void slotOpenFiles();
+ void slotPlay();
+ void slotMoveUp();
+ void slotMoveDown();
+ void slotDeleteCurrent();
+ void slotClear();
+ void slotCurrentItemChanged(QListViewItem*);
+ void slotDropped(QDropEvent*, QListViewItem*);
+
+protected:
+ void save();
+ void restore();
+
+ //--------------- Data members ---------------------------------
+ PlayListView* m_listView;
+ QFrame* m_buttonBar;
+ QHBoxLayout* m_barLayout;
+
+ QPushButton* m_openButton;
+ QPushButton* m_playButton;
+ QPushButton* m_moveUpButton;
+ QPushButton* m_moveDownButton;
+ QPushButton* m_deleteButton;
+ QPushButton* m_clearButton;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/PlayListDialog.cpp b/src/gui/editors/segment/PlayListDialog.cpp
new file mode 100644
index 0000000..7aa03a5
--- /dev/null
+++ b/src/gui/editors/segment/PlayListDialog.cpp
@@ -0,0 +1,76 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "PlayListDialog.h"
+
+#include "document/ConfigGroups.h"
+#include "PlayList.h"
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PlayListDialog::PlayListDialog(QString caption,
+ QWidget* parent, const char* name)
+ : KDialogBase(parent, name, false, caption,
+ KDialogBase::Close, // standard buttons
+ KDialogBase::Close, // default button
+ true),
+ m_playList(new PlayList(this))
+{
+ setWFlags(WDestructiveClose);
+ setMainWidget(m_playList);
+ restore();
+}
+
+void PlayListDialog::save()
+{
+ saveDialogSize(PlayListConfigGroup);
+}
+
+void PlayListDialog::restore()
+{
+ setInitialSize(configDialogSize(PlayListConfigGroup));
+}
+
+void PlayListDialog::closeEvent(QCloseEvent *e)
+{
+ save();
+ emit closing();
+ KDialogBase::closeEvent(e);
+}
+
+void PlayListDialog::slotClose()
+{
+ save();
+ emit closing();
+ KDialogBase::slotClose();
+}
+
+}
+#include "PlayListDialog.moc"
diff --git a/src/gui/editors/segment/PlayListDialog.h b/src/gui/editors/segment/PlayListDialog.h
new file mode 100644
index 0000000..51db8ca
--- /dev/null
+++ b/src/gui/editors/segment/PlayListDialog.h
@@ -0,0 +1,71 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PLAYLISTDIALOG_H_
+#define _RG_PLAYLISTDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qstring.h>
+
+
+class QWidget;
+class QCloseEvent;
+
+
+namespace Rosegarden
+{
+
+class PlayList;
+
+
+class PlayListDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ PlayListDialog(QString caption, QWidget* parent = 0, const char* name = 0);
+
+ PlayList* getPlayList() { return m_playList; }
+
+public slots:
+ void slotClose();
+
+signals:
+ void closing();
+
+protected:
+ virtual void closeEvent(QCloseEvent *e);
+
+ void save();
+ void restore();
+
+ //--------------- Data members ---------------------------------
+ PlayList* m_playList;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/PlayListView.cpp b/src/gui/editors/segment/PlayListView.cpp
new file mode 100644
index 0000000..8c17076
--- /dev/null
+++ b/src/gui/editors/segment/PlayListView.cpp
@@ -0,0 +1,66 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "PlayListView.h"
+
+#include <klocale.h>
+#include <qdragobject.h>
+
+namespace Rosegarden {
+
+PlayListView::PlayListView(QWidget *parent, const char *name)
+ : KListView(parent, name)
+{
+ addColumn(i18n("Title"));
+ addColumn(i18n("File name"));
+
+ setDragEnabled(true);
+ setAcceptDrops(true);
+ setDropVisualizer(true);
+
+ setShowToolTips(true);
+ setShowSortIndicator(true);
+ setAllColumnsShowFocus(true);
+ setItemsMovable(true);
+ setSorting(-1);
+}
+
+bool PlayListView::acceptDrag(QDropEvent* e) const
+{
+ return QUriDrag::canDecode(e) || KListView::acceptDrag(e);
+}
+
+
+QListViewItem* PlayListView::previousSibling(QListViewItem* item)
+{
+ QListViewItem* prevSib = firstChild();
+
+ while(prevSib && prevSib->nextSibling() != item)
+ prevSib = prevSib->nextSibling();
+
+ return prevSib;
+}
+
+}
+
diff --git a/src/gui/editors/segment/PlayListView.h b/src/gui/editors/segment/PlayListView.h
new file mode 100644
index 0000000..a18b8e8
--- /dev/null
+++ b/src/gui/editors/segment/PlayListView.h
@@ -0,0 +1,52 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PLAYLISTVIEW_H_
+#define _RG_PLAYLISTVIEW_H_
+
+#include <klistview.h>
+
+namespace Rosegarden {
+
+class PlayListView : public KListView
+{
+public:
+ PlayListView(QWidget *parent=0, const char *name=0);
+
+ QListViewItem* previousSibling(QListViewItem*);
+
+protected:
+// virtual void dragEnterEvent(QDragEnterEvent *event);
+// virtual void dropEvent(QDropEvent*);
+
+// virtual void dragEnterEvent(QDragEnterEvent*);
+ virtual bool acceptDrag(QDropEvent*) const;
+
+
+};
+
+}
+
+#endif
+
diff --git a/src/gui/editors/segment/PlayListViewItem.cpp b/src/gui/editors/segment/PlayListViewItem.cpp
new file mode 100644
index 0000000..df04a2e
--- /dev/null
+++ b/src/gui/editors/segment/PlayListViewItem.cpp
@@ -0,0 +1,42 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "PlayListViewItem.h"
+
+namespace Rosegarden {
+
+PlayListViewItem::PlayListViewItem(KListView* parent, KURL kurl)
+ : KListViewItem(parent, kurl.fileName(), kurl.prettyURL()),
+ m_kurl(kurl)
+{
+}
+
+PlayListViewItem::PlayListViewItem(KListView* parent, QListViewItem* after, KURL kurl)
+ : KListViewItem(parent, after, kurl.fileName(), kurl.prettyURL()),
+ m_kurl(kurl)
+{
+}
+
+}
+
diff --git a/src/gui/editors/segment/PlayListViewItem.h b/src/gui/editors/segment/PlayListViewItem.h
new file mode 100644
index 0000000..b88de0f
--- /dev/null
+++ b/src/gui/editors/segment/PlayListViewItem.h
@@ -0,0 +1,47 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PLAYLISTVIEWITEM_H_
+#define _RG_PLAYLISTVIEWITEM_H_
+
+#include <klistview.h>
+#include <kurl.h>
+
+namespace Rosegarden {
+
+class PlayListViewItem : public KListViewItem
+{
+public:
+ PlayListViewItem(KListView* parent, KURL);
+ PlayListViewItem(KListView* parent, QListViewItem*, KURL);
+
+ const KURL& getURL() { return m_kurl; }
+
+protected:
+ KURL m_kurl;
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TrackButtons.cpp b/src/gui/editors/segment/TrackButtons.cpp
new file mode 100644
index 0000000..5cf9908
--- /dev/null
+++ b/src/gui/editors/segment/TrackButtons.cpp
@@ -0,0 +1,1149 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TrackButtons.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/AudioPluginInstance.h"
+#include "base/Composition.h"
+#include "base/Device.h"
+#include "base/Instrument.h"
+#include "base/MidiProgram.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "commands/segment/RenameTrackCommand.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/MultiViewCommandHistory.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/kdeext/KLedButton.h"
+#include "sound/AudioFileManager.h"
+#include "sound/PluginIdentifier.h"
+#include "TrackLabel.h"
+#include "TrackVUMeter.h"
+#include <kglobal.h>
+#include <kled.h>
+#include <kmessagebox.h>
+#include <qcursor.h>
+#include <qframe.h>
+#include <qiconset.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qpixmap.h>
+#include <qpopupmenu.h>
+#include <qsignalmapper.h>
+#include <qstring.h>
+#include <qtimer.h>
+#include <qwidget.h>
+#include <qwidgetstack.h>
+#include <qtooltip.h>
+
+namespace Rosegarden
+{
+
+TrackButtons::TrackButtons(RosegardenGUIDoc* doc,
+ unsigned int trackCellHeight,
+ unsigned int trackLabelWidth,
+ bool showTrackLabels,
+ int overallHeight,
+ QWidget* parent,
+ const char* name,
+ WFlags f)
+ : QFrame(parent, name, f),
+ m_doc(doc),
+ m_layout(new QVBoxLayout(this)),
+ m_recordSigMapper(new QSignalMapper(this)),
+ m_muteSigMapper(new QSignalMapper(this)),
+ m_clickedSigMapper(new QSignalMapper(this)),
+ m_instListSigMapper(new QSignalMapper(this)),
+ m_tracks(doc->getComposition().getNbTracks()),
+ m_offset(4),
+ m_cellSize(trackCellHeight),
+ m_borderGap(1),
+ m_trackLabelWidth(trackLabelWidth),
+ m_popupItem(0),
+ m_lastSelected( -1)
+{
+ setFrameStyle(Plain);
+
+ // when we create the widget, what are we looking at?
+ if (showTrackLabels)
+ m_trackInstrumentLabels = TrackLabel::ShowTrack;
+ else
+ m_trackInstrumentLabels = TrackLabel::ShowInstrument;
+
+ // Set the spacing between vertical elements
+ //
+ m_layout->setSpacing(m_borderGap);
+
+ // Now draw the buttons and labels and meters
+ //
+ makeButtons();
+
+ m_layout->addStretch(20);
+
+ connect(m_recordSigMapper, SIGNAL(mapped(int)),
+ this, SLOT(slotToggleRecordTrack(int)));
+
+ connect(m_muteSigMapper, SIGNAL(mapped(int)),
+ this, SLOT(slotToggleMutedTrack(int)));
+
+ // connect signal mappers
+ connect(m_instListSigMapper, SIGNAL(mapped(int)),
+ this, SLOT(slotInstrumentSelection(int)));
+
+ connect(m_clickedSigMapper, SIGNAL(mapped(int)),
+ this, SIGNAL(trackSelected(int)));
+
+ // // Populate instrument popup menu just once at start-up
+ // //
+ // populateInstrumentPopup();
+
+ // We have to force the height for the moment
+ //
+ setMinimumHeight(overallHeight);
+
+}
+
+TrackButtons::~TrackButtons()
+{}
+
+void
+TrackButtons::makeButtons()
+{
+ if (!m_doc)
+ return ;
+
+ // Create a horizontal box for each track
+ // plus the two buttons
+ //
+ unsigned int nbTracks = m_doc->getComposition().getNbTracks();
+
+ for (unsigned int i = 0; i < nbTracks; ++i) {
+ Track *track = m_doc->getComposition().getTrackByPosition(i);
+
+ if (track) {
+ QFrame *trackHBox = makeButton(track->getId());
+
+ if (trackHBox) {
+ m_layout->addWidget(trackHBox);
+ m_trackHBoxes.push_back(trackHBox);
+ }
+ }
+ }
+
+ populateButtons();
+}
+
+void TrackButtons::setButtonMapping(QObject* obj, TrackId trackId)
+{
+ m_clickedSigMapper->setMapping(obj, trackId);
+ m_instListSigMapper->setMapping(obj, trackId);
+}
+
+void
+TrackButtons::populateButtons()
+{
+ Instrument *ins = 0;
+ Track *track;
+
+ for (unsigned int i = 0; i < m_trackLabels.size(); ++i) {
+ track = m_doc->getComposition().getTrackByPosition(i);
+
+ if (track) {
+ ins = m_doc->getStudio().getInstrumentById(track->getInstrument());
+
+ // Set mute button from track
+ //
+ if (track->isMuted())
+ m_muteLeds[i]->off();
+ else
+ m_muteLeds[i]->on();
+
+ // Set record button from track
+ //
+ bool recording =
+ m_doc->getComposition().isTrackRecording(track->getId());
+ setRecordTrack(track->getPosition(), recording);
+
+ // reset track tokens
+ m_trackLabels[i]->setId(track->getId());
+ setButtonMapping(m_trackLabels[i], track->getId());
+ m_trackLabels[i]->setPosition(i);
+ }
+
+ if (ins) {
+ m_trackLabels[i]->getInstrumentLabel()->setText
+ (strtoqstr(ins->getPresentationName()));
+ if (ins->sendsProgramChange()) {
+ m_trackLabels[i]->setAlternativeLabel(strtoqstr(ins->getProgramName()));
+ }
+
+ } else {
+ m_trackLabels[i]->getInstrumentLabel()->setText(i18n("<no instrument>"));
+ }
+
+ m_trackLabels[i]->update();
+ }
+
+}
+
+std::vector<int>
+TrackButtons::mutedTracks()
+{
+ std::vector<int> mutedTracks;
+
+ for (TrackId i = 0; i < m_tracks; i++) {
+ if (m_muteLeds[i]->state() == KLed::Off)
+ mutedTracks.push_back(i);
+ }
+
+ return mutedTracks;
+}
+
+void
+TrackButtons::slotToggleMutedTrack(int mutedTrackPos)
+{
+ RG_DEBUG << "TrackButtons::slotToggleMutedTrack(" << mutedTrackPos << ")\n";
+
+ if (mutedTrackPos < 0 || mutedTrackPos > (int)m_tracks )
+ return ;
+
+ Track *track =
+ m_doc->getComposition().getTrackByPosition(mutedTrackPos);
+
+ emit muteButton(track->getId(), !track->isMuted()); // will set the value
+}
+
+void
+TrackButtons::removeButtons(unsigned int position)
+{
+ RG_DEBUG << "TrackButtons::removeButtons - "
+ << "deleting track button at position "
+ << position << endl;
+
+ if (position >= m_trackHBoxes.size()) {
+ RG_DEBUG << "%%%%%%%%% BIG PROBLEM : TrackButtons::removeButtons() was passed a non-existing index\n";
+ return ;
+ }
+
+ std::vector<TrackLabel*>::iterator tit = m_trackLabels.begin();
+ tit += position;
+ m_trackLabels.erase(tit);
+
+ std::vector<TrackVUMeter*>::iterator vit = m_trackMeters.begin();
+ vit += position;
+ m_trackMeters.erase(vit);
+
+ std::vector<KLedButton*>::iterator mit = m_muteLeds.begin();
+ mit += position;
+ m_muteLeds.erase(mit);
+
+ mit = m_recordLeds.begin();
+ mit += position;
+ m_recordLeds.erase(mit);
+
+ delete m_trackHBoxes[position]; // deletes all child widgets (button, led, label...)
+
+ std::vector<QFrame*>::iterator it = m_trackHBoxes.begin();
+ it += position;
+ m_trackHBoxes.erase(it);
+
+}
+
+void
+TrackButtons::slotUpdateTracks()
+{
+ Composition &comp = m_doc->getComposition();
+ unsigned int newNbTracks = comp.getNbTracks();
+ Track *track = 0;
+
+ std::cerr << "TrackButtons::slotUpdateTracks" << std::endl;
+
+ if (newNbTracks < m_tracks) {
+ for (unsigned int i = m_tracks; i > newNbTracks; --i)
+ removeButtons(i - 1);
+ } else if (newNbTracks > m_tracks) {
+ for (unsigned int i = m_tracks; i < newNbTracks; ++i) {
+ track = m_doc->getComposition().getTrackByPosition(i);
+ if (track) {
+ QFrame *trackHBox = makeButton(track->getId());
+
+ if (trackHBox) {
+ trackHBox->show();
+ m_layout->insertWidget(i, trackHBox);
+ m_trackHBoxes.push_back(trackHBox);
+ }
+ } else
+ RG_DEBUG << "TrackButtons::slotUpdateTracks - can't find TrackId for position " << i << endl;
+ }
+ }
+
+ // Set height
+ //
+ for (unsigned int i = 0; i < m_trackHBoxes.size(); ++i) {
+
+ track = comp.getTrackByPosition(i);
+
+ if (track) {
+
+ int multiple = m_doc->getComposition()
+ .getMaxContemporaneousSegmentsOnTrack(track->getId());
+ if (multiple == 0) multiple = 1;
+
+ // nasty dupe from makeButton
+
+ int buttonGap = 8;
+ int vuWidth = 20;
+ int vuSpacing = 2;
+
+ int labelWidth = m_trackLabelWidth -
+ ((m_cellSize - buttonGap) * 2 +
+ vuSpacing * 2 + vuWidth);
+
+ m_trackHBoxes[i]->setMinimumSize
+ (labelWidth, m_cellSize * multiple - m_borderGap);
+
+ m_trackHBoxes[i]->setFixedHeight
+ (m_cellSize * multiple - m_borderGap);
+ }
+ }
+
+ // Renumber all the labels
+ //
+ for (unsigned int i = 0; i < m_trackLabels.size(); ++i) {
+ track = comp.getTrackByPosition(i);
+
+ if (track) {
+ m_trackLabels[i]->setId(track->getId());
+
+ QLabel *trackLabel = m_trackLabels[i]->getTrackLabel();
+
+ if (track->getLabel() == std::string("")) {
+ Instrument *ins =
+ m_doc->getStudio().getInstrumentById(track->getInstrument());
+ if (ins && ins->getType() == Instrument::Audio) {
+ trackLabel->setText(i18n("<untitled audio>"));
+ } else {
+ trackLabel->setText(i18n("<untitled>"));
+ }
+ } else {
+ trackLabel->setText(strtoqstr(track->getLabel()));
+ }
+
+ // RG_DEBUG << "TrackButtons::slotUpdateTracks - set button mapping at pos "
+ // << i << " to track id " << track->getId() << endl;
+ setButtonMapping(m_trackLabels[i], track->getId());
+ }
+ }
+ m_tracks = newNbTracks;
+
+ // Set record status and colour
+
+ for (unsigned int i = 0; i < m_trackLabels.size(); ++i) {
+
+ track = comp.getTrackByPosition(i);
+
+ if (track) {
+
+ setRecordTrack(i, comp.isTrackRecording(track->getId()));
+
+ Instrument *ins =
+ m_doc->getStudio().getInstrumentById(track->getInstrument());
+
+ if (ins &&
+ ins->getType() == Instrument::Audio) {
+ m_recordLeds[i]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordAudioTrackLED));
+ } else {
+ m_recordLeds[i]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordMIDITrackLED));
+ }
+ }
+ }
+
+ // repopulate the buttons
+ populateButtons();
+}
+
+void
+TrackButtons::slotToggleRecordTrack(int position)
+{
+ Composition &comp = m_doc->getComposition();
+ Track *track = comp.getTrackByPosition(position);
+
+ bool state = !comp.isTrackRecording(track->getId());
+
+ Instrument *instrument = m_doc->getStudio().getInstrumentById
+ (track->getInstrument());
+
+ bool audio = (instrument &&
+ instrument->getType() == Instrument::Audio);
+
+ if (audio && state) {
+ try {
+ m_doc->getAudioFileManager().testAudioPath();
+ } catch (AudioFileManager::BadAudioPathException e) {
+ if (KMessageBox::warningContinueCancel
+ (this,
+ i18n("The audio file path does not exist or is not writable.\nPlease set the audio file path to a valid directory in Document Properties before recording audio.\nWould you like to set it now?"),
+ i18n("Warning"),
+ i18n("Set audio file path")) == KMessageBox::Continue) {
+ RosegardenGUIApp::self()->slotOpenAudioPathSettings();
+ }
+ }
+ }
+
+ // can have any number of audio instruments armed, but only one
+ // track armed per instrument.
+
+ // Need to copy this container, as we're implicitly modifying it
+ // through calls to comp.setTrackRecording
+
+ Composition::recordtrackcontainer oldRecordTracks =
+ comp.getRecordTracks();
+
+ for (Composition::recordtrackcontainer::const_iterator i =
+ oldRecordTracks.begin();
+ i != oldRecordTracks.end(); ++i) {
+
+ if (!comp.isTrackRecording(*i)) {
+ // We've already reset this one
+ continue;
+ }
+
+ Track *otherTrack = comp.getTrackById(*i);
+
+ if (otherTrack &&
+ otherTrack != track) {
+
+ /* Obsolete code: audio, MIDI and plugin tracks behave the same now.
+ plcl, 06/2006 - Multitrack MIDI recording
+
+ bool unselect;
+
+ if (audio) {
+ unselect = (otherTrack->getInstrument() == track->getInstrument());
+ } else {
+ // our track is not an audio track, check that the
+ // other isn't either
+ Instrument *otherInstrument =
+ m_doc->getStudio().getInstrumentById(otherTrack->getInstrument());
+ bool otherAudio = (otherInstrument &&
+ otherInstrument->getType() ==
+ Instrument::Audio);
+
+ unselect = !otherAudio;
+ }
+
+ if (unselect) { */
+
+ if (otherTrack->getInstrument() == track->getInstrument()) {
+ // found another record track of the same type (and
+ // with the same instrument, if audio): unselect that
+
+ //!!! should we tell the user, particularly for the
+ //audio case? might seem odd otherwise
+
+ int otherPos = otherTrack->getPosition();
+ setRecordTrack(otherPos, false);
+ }
+ }
+ }
+
+ setRecordTrack(position, state);
+
+ emit recordButton(track->getId(), state);
+}
+
+void
+TrackButtons::setRecordTrack(int position, bool state)
+{
+ setRecordButton(position, state);
+ m_doc->getComposition().setTrackRecording
+ (m_trackLabels[position]->getId(), state);
+}
+
+void
+TrackButtons::setRecordButton(int position, bool state)
+{
+ if (position < 0 || position >= (int)m_tracks)
+ return ;
+
+ KLedButton* led = m_recordLeds[position];
+
+ led->setState(state ? KLed::On : KLed::Off);
+}
+
+void
+TrackButtons::selectLabel(int position)
+{
+ if (m_lastSelected >= 0 && m_lastSelected < (int)m_trackLabels.size()) {
+ m_trackLabels[m_lastSelected]->setSelected(false);
+ }
+
+ if (position >= 0 && position < (int)m_trackLabels.size()) {
+ m_trackLabels[position]->setSelected(true);
+ m_lastSelected = position;
+ }
+}
+
+std::vector<int>
+TrackButtons::getHighlightedTracks()
+{
+ std::vector<int> retList;
+
+ for (unsigned int i = 0; i < m_trackLabels.size(); ++i) {
+ if (m_trackLabels[i]->isSelected())
+ retList.push_back(i);
+ }
+
+ return retList;
+}
+
+void
+TrackButtons::slotRenameTrack(QString newName, TrackId trackId)
+{
+ m_doc->getCommandHistory()->addCommand
+ (new RenameTrackCommand(&m_doc->getComposition(),
+ trackId,
+ qstrtostr(newName)));
+
+ changeTrackLabel(trackId, newName);
+}
+
+void
+TrackButtons::slotSetTrackMeter(float value, int position)
+{
+ //Composition &comp = m_doc->getComposition();
+ //Studio &studio = m_doc->getStudio();
+ //Track *track;
+
+ for (unsigned int i = 0; i < m_trackMeters.size(); ++i) {
+ if (i == ((unsigned int)position)) {
+ m_trackMeters[i]->setLevel(value);
+ return ;
+ }
+ }
+}
+
+void
+TrackButtons::slotSetMetersByInstrument(float value,
+ InstrumentId id)
+{
+ Composition &comp = m_doc->getComposition();
+ //Studio &studio = m_doc->getStudio();
+ Track *track;
+
+ for (unsigned int i = 0; i < m_trackMeters.size(); ++i) {
+ track = comp.getTrackByPosition(i);
+
+ if (track != 0 && track->getInstrument() == id) {
+ m_trackMeters[i]->setLevel(value);
+ }
+ }
+}
+
+void
+TrackButtons::slotInstrumentSelection(int trackId)
+{
+ RG_DEBUG << "TrackButtons::slotInstrumentSelection(" << trackId << ")\n";
+
+ Composition &comp = m_doc->getComposition();
+ Studio &studio = m_doc->getStudio();
+
+ int position = comp.getTrackById(trackId)->getPosition();
+
+ QString instrumentName = i18n("<no instrument>");
+ Track *track = comp.getTrackByPosition(position);
+
+ Instrument *instrument = 0;
+ if (track != 0) {
+ instrument = studio.getInstrumentById(track->getInstrument());
+ if (instrument)
+ instrumentName = strtoqstr(instrument->getPresentationName());
+ }
+
+ //
+ // populate this instrument widget
+ m_trackLabels[position]->getInstrumentLabel()->setText(instrumentName);
+
+ // Ensure the instrument name is shown
+ m_trackLabels[position]->showLabel(TrackLabel::ShowInstrument);
+
+ // Yes, well as we might've changed the Device name in the
+ // Device/Bank dialog then we reload the whole menu here.
+ //
+
+ QPopupMenu instrumentPopup(this);
+
+ populateInstrumentPopup(instrument, &instrumentPopup);
+
+ // Store the popup item position
+ //
+ m_popupItem = position;
+
+ instrumentPopup.exec(QCursor::pos());
+
+ // Restore the label back to what it was showing
+ m_trackLabels[position]->showLabel(m_trackInstrumentLabels);
+
+ // Do this here as well as in slotInstrumentPopupActivated, so as
+ // to restore the correct alternative label even if no other
+ // program was selected from the menu
+ if (track != 0) {
+ instrument = studio.getInstrumentById(track->getInstrument());
+ if (instrument) {
+ m_trackLabels[position]->getInstrumentLabel()->
+ setText(strtoqstr(instrument->getPresentationName()));
+ m_trackLabels[position]->clearAlternativeLabel();
+ if (instrument->sendsProgramChange()) {
+ m_trackLabels[position]->setAlternativeLabel
+ (strtoqstr(instrument->getProgramName()));
+ }
+ }
+ }
+}
+
+void
+TrackButtons::populateInstrumentPopup(Instrument *thisTrackInstr, QPopupMenu* instrumentPopup)
+{
+ static QPixmap connectedPixmap, unconnectedPixmap,
+ connectedUsedPixmap, unconnectedUsedPixmap,
+ connectedSelectedPixmap, unconnectedSelectedPixmap;
+ static bool havePixmaps = false;
+
+ if (!havePixmaps) {
+
+ QString pixmapDir =
+ KGlobal::dirs()->findResource("appdata", "pixmaps/");
+
+ connectedPixmap.load
+ (QString("%1/misc/connected.xpm").arg(pixmapDir));
+ connectedUsedPixmap.load
+ (QString("%1/misc/connected-used.xpm").arg(pixmapDir));
+ connectedSelectedPixmap.load
+ (QString("%1/misc/connected-selected.xpm").arg(pixmapDir));
+ unconnectedPixmap.load
+ (QString("%1/misc/unconnected.xpm").arg(pixmapDir));
+ unconnectedUsedPixmap.load
+ (QString("%1/misc/unconnected-used.xpm").arg(pixmapDir));
+ unconnectedSelectedPixmap.load
+ (QString("%1/misc/unconnected-selected.xpm").arg(pixmapDir));
+
+ havePixmaps = true;
+ }
+
+ Composition &comp = m_doc->getComposition();
+ Studio &studio = m_doc->getStudio();
+
+ // clear the popup
+ instrumentPopup->clear();
+
+ std::vector<QPopupMenu*> instrumentSubMenus;
+
+ // position index
+ int i = 0;
+
+ // Get the list
+ InstrumentList list = studio.getPresentationInstruments();
+ InstrumentList::iterator it;
+ int currentDevId = -1;
+ bool deviceUsedByAnyone = false;
+
+ for (it = list.begin(); it != list.end(); it++) {
+
+ if (! (*it))
+ continue; // sanity check
+
+ QString iname(strtoqstr((*it)->getPresentationName()));
+ QString pname(strtoqstr((*it)->getProgramName()));
+ Device *device = (*it)->getDevice();
+ DeviceId devId = device->getId();
+ bool connected = false;
+
+ if ((*it)->getType() == Instrument::SoftSynth) {
+ pname = "";
+ AudioPluginInstance *plugin = (*it)->getPlugin
+ (Instrument::SYNTH_PLUGIN_POSITION);
+ if (plugin) {
+ pname = strtoqstr(plugin->getProgram());
+ QString identifier = strtoqstr(plugin->getIdentifier());
+ if (identifier != "") {
+ connected = true;
+ QString type, soName, label;
+ PluginIdentifier::parseIdentifier
+ (identifier, type, soName, label);
+ if (pname == "") {
+ pname = strtoqstr(plugin->getDistinctiveConfigurationText());
+ }
+ if (pname != "") {
+ pname = QString("%1: %2").arg(label).arg(pname);
+ } else {
+ pname = label;
+ }
+ } else {
+ connected = false;
+ }
+ }
+ } else if ((*it)->getType() == Instrument::Audio) {
+ connected = true;
+ } else {
+ connected = (device->getConnection() != "");
+ }
+
+ bool instrUsedByMe = false;
+ bool instrUsedByAnyone = false;
+
+ if (thisTrackInstr && thisTrackInstr->getId() == (*it)->getId()) {
+ instrUsedByMe = true;
+ instrUsedByAnyone = true;
+ }
+
+ if (devId != (DeviceId)(currentDevId)) {
+
+ deviceUsedByAnyone = false;
+
+ if (instrUsedByMe)
+ deviceUsedByAnyone = true;
+ else {
+ for (Composition::trackcontainer::iterator tit =
+ comp.getTracks().begin();
+ tit != comp.getTracks().end(); ++tit) {
+
+ if (tit->second->getInstrument() == (*it)->getId()) {
+ instrUsedByAnyone = true;
+ deviceUsedByAnyone = true;
+ break;
+ }
+
+ Instrument *instr =
+ studio.getInstrumentById(tit->second->getInstrument());
+ if (instr && (instr->getDevice()->getId() == devId)) {
+ deviceUsedByAnyone = true;
+ }
+ }
+ }
+
+ QIconSet iconSet
+ (connected ?
+ (deviceUsedByAnyone ?
+ connectedUsedPixmap : connectedPixmap) :
+ (deviceUsedByAnyone ?
+ unconnectedUsedPixmap : unconnectedPixmap));
+
+ currentDevId = int(devId);
+
+ QPopupMenu *subMenu = new QPopupMenu(instrumentPopup);
+ QString deviceName = strtoqstr(device->getName());
+ instrumentPopup->insertItem(iconSet, deviceName, subMenu);
+ instrumentSubMenus.push_back(subMenu);
+
+ // Connect up the submenu
+ //
+ connect(subMenu, SIGNAL(activated(int)),
+ SLOT(slotInstrumentPopupActivated(int)));
+
+ } else if (!instrUsedByMe) {
+
+ for (Composition::trackcontainer::iterator tit =
+ comp.getTracks().begin();
+ tit != comp.getTracks().end(); ++tit) {
+
+ if (tit->second->getInstrument() == (*it)->getId()) {
+ instrUsedByAnyone = true;
+ break;
+ }
+ }
+ }
+
+ QIconSet iconSet
+ (connected ?
+ (instrUsedByAnyone ?
+ instrUsedByMe ?
+ connectedSelectedPixmap :
+ connectedUsedPixmap : connectedPixmap) :
+ (instrUsedByAnyone ?
+ instrUsedByMe ?
+ unconnectedSelectedPixmap :
+ unconnectedUsedPixmap : unconnectedPixmap));
+
+ if (pname != "")
+ iname += " (" + pname + ")";
+
+ instrumentSubMenus[instrumentSubMenus.size() - 1]->insertItem(iconSet, iname, i++);
+ }
+
+}
+
+void
+TrackButtons::slotInstrumentPopupActivated(int item)
+{
+ RG_DEBUG << "TrackButtons::slotInstrumentPopupActivated " << item << endl;
+
+ Composition &comp = m_doc->getComposition();
+ Studio &studio = m_doc->getStudio();
+
+ Instrument *inst = studio.getInstrumentFromList(item);
+
+ RG_DEBUG << "TrackButtons::slotInstrumentPopupActivated: instrument " << inst << endl;
+
+ if (inst != 0) {
+ Track *track = comp.getTrackByPosition(m_popupItem);
+
+ if (track != 0) {
+ track->setInstrument(inst->getId());
+
+ // select instrument
+ emit instrumentSelected((int)inst->getId());
+
+ m_trackLabels[m_popupItem]->getInstrumentLabel()->
+ setText(strtoqstr(inst->getPresentationName()));
+
+ // reset the alternative label
+ m_trackLabels[m_popupItem]->clearAlternativeLabel();
+
+ // Now see if the program is being shown for this instrument
+ // and if so reset the label
+ //
+ if (inst->sendsProgramChange())
+ m_trackLabels[m_popupItem]->setAlternativeLabel(strtoqstr(inst->getProgramName()));
+
+ if (inst->getType() == Instrument::Audio) {
+ m_recordLeds[m_popupItem]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordAudioTrackLED));
+ } else {
+ m_recordLeds[m_popupItem]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordMIDITrackLED));
+ }
+ } else
+ RG_DEBUG << "slotInstrumentPopupActivated() - can't find item!\n";
+ } else
+ RG_DEBUG << "slotInstrumentPopupActivated() - can't find item!\n";
+
+}
+
+void
+TrackButtons::changeTrackInstrumentLabels(TrackLabel::InstrumentTrackLabels label)
+{
+ // Set new label
+ m_trackInstrumentLabels = label;
+
+ // update and reconnect with new value
+ for (int i = 0; i < (int)m_tracks; i++) {
+ m_trackLabels[i]->showLabel(label);
+ }
+}
+
+void
+TrackButtons::changeInstrumentLabel(InstrumentId id, QString label)
+{
+ Composition &comp = m_doc->getComposition();
+ Track *track;
+
+ for (int i = 0; i < (int)m_tracks; i++) {
+ track = comp.getTrackByPosition(i);
+
+ if (track && track->getInstrument() == id) {
+
+ m_trackLabels[i]->setAlternativeLabel(label);
+
+ Instrument *ins = m_doc->getStudio().
+ getInstrumentById(track->getInstrument());
+
+ if (ins && ins->getType() == Instrument::Audio) {
+ m_recordLeds[i]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordAudioTrackLED));
+ } else {
+ m_recordLeds[i]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordMIDITrackLED));
+ }
+ }
+ }
+}
+
+void
+TrackButtons::changeTrackLabel(TrackId id, QString label)
+{
+ Composition &comp = m_doc->getComposition();
+ Track *track;
+
+ for (int i = 0; i < (int)m_tracks; i++) {
+ track = comp.getTrackByPosition(i);
+ if (track && track->getId() == id) {
+ if (m_trackLabels[i]->getTrackLabel()->text() != label) {
+ m_trackLabels[i]->getTrackLabel()->setText(label);
+ emit widthChanged();
+ emit nameChanged();
+ }
+ return ;
+ }
+ }
+}
+
+void
+TrackButtons::slotSynchroniseWithComposition()
+{
+ Composition &comp = m_doc->getComposition();
+ Studio &studio = m_doc->getStudio();
+ Track *track;
+
+ for (int i = 0; i < (int)m_tracks; i++) {
+ track = comp.getTrackByPosition(i);
+
+ if (track) {
+ if (track->isMuted())
+ m_muteLeds[i]->off();
+ else
+ m_muteLeds[i]->on();
+
+ Instrument *ins = studio.
+ getInstrumentById(track->getInstrument());
+
+ QString instrumentName(i18n("<no instrument>"));
+ if (ins)
+ instrumentName = strtoqstr(ins->getPresentationName());
+
+ m_trackLabels[i]->getInstrumentLabel()->setText(instrumentName);
+
+ setRecordButton(i, comp.isTrackRecording(track->getId()));
+
+ if (ins && ins->getType() == Instrument::Audio) {
+ m_recordLeds[i]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordAudioTrackLED));
+ } else {
+ m_recordLeds[i]->setColor
+ (GUIPalette::getColour
+ (GUIPalette::RecordMIDITrackLED));
+ }
+ }
+ }
+}
+
+void
+TrackButtons::slotLabelSelected(int position)
+{
+ Track *track =
+ m_doc->getComposition().getTrackByPosition(position);
+
+ if (track) {
+ emit trackSelected(track->getId());
+ }
+}
+
+void
+TrackButtons::setMuteButton(TrackId track, bool value)
+{
+ Track *trackObj = m_doc->getComposition().getTrackById(track);
+ if (trackObj == 0)
+ return ;
+
+ int pos = trackObj->getPosition();
+
+ RG_DEBUG << "TrackButtons::setMuteButton() trackId = "
+ << track << ", pos = " << pos << endl;
+
+ m_muteLeds[pos]->setState(value ? KLed::Off : KLed::On);
+}
+
+void
+TrackButtons::slotTrackInstrumentSelection(TrackId trackId, int item)
+{
+ RG_DEBUG << "TrackButtons::slotTrackInstrumentSelection(" << trackId << ")\n";
+
+ Composition &comp = m_doc->getComposition();
+ int position = comp.getTrackById(trackId)->getPosition();
+ m_popupItem = position;
+ slotInstrumentPopupActivated( item );
+}
+
+QFrame* TrackButtons::makeButton(Rosegarden::TrackId trackId)
+{
+ // The buttonGap sets up the sizes of the buttons
+ //
+ static const int buttonGap = 8;
+
+ QFrame *trackHBox = 0;
+
+ KLedButton *mute = 0;
+ KLedButton *record = 0;
+
+ TrackVUMeter *vuMeter = 0;
+ TrackLabel *trackLabel = 0;
+
+ int vuWidth = 20;
+ int vuSpacing = 2;
+ int multiple = m_doc->getComposition()
+ .getMaxContemporaneousSegmentsOnTrack(trackId);
+ if (multiple == 0) multiple = 1;
+ int labelWidth = m_trackLabelWidth - ( (m_cellSize - buttonGap) * 2 +
+ vuSpacing * 2 + vuWidth );
+
+ // Set the label from the Track object on the Composition
+ //
+ Rosegarden::Track *track = m_doc->getComposition().getTrackById(trackId);
+
+ if (track == 0) return 0;
+
+ // Create a horizontal box for each track
+ //
+ trackHBox = new QFrame(this);
+ QHBoxLayout *hblayout = new QHBoxLayout(trackHBox);
+
+ trackHBox->setMinimumSize(labelWidth, m_cellSize * multiple - m_borderGap);
+ trackHBox->setFixedHeight(m_cellSize * multiple - m_borderGap);
+
+ // Try a style for the box
+ //
+ trackHBox->setFrameStyle(StyledPanel);
+ trackHBox->setFrameShape(StyledPanel);
+ trackHBox->setFrameShadow(Raised);
+
+ // Insert a little gap
+ hblayout->addSpacing(vuSpacing);
+
+ // Create a VU meter
+ vuMeter = new TrackVUMeter(trackHBox,
+ VUMeter::PeakHold,
+ vuWidth,
+ buttonGap,
+ track->getPosition());
+
+ m_trackMeters.push_back(vuMeter);
+
+ hblayout->addWidget(vuMeter);
+
+ // Create another little gap
+ hblayout->addSpacing(vuSpacing);
+
+ //
+ // 'mute' and 'record' leds
+ //
+
+ mute = new KLedButton(Rosegarden::GUIPalette::getColour
+ (Rosegarden::GUIPalette::MuteTrackLED), trackHBox);
+ QToolTip::add(mute, i18n("Mute track"));
+ hblayout->addWidget(mute);
+
+ record = new KLedButton(Rosegarden::GUIPalette::getColour
+ (Rosegarden::GUIPalette::RecordMIDITrackLED), trackHBox);
+ QToolTip::add(record, i18n("Record on this track"));
+ hblayout->addWidget(record);
+
+ record->setLook(KLed::Sunken);
+ mute->setLook(KLed::Sunken);
+ record->off();
+
+ // Connect them to their sigmappers
+ connect(record, SIGNAL(stateChanged(bool)),
+ m_recordSigMapper, SLOT(map()));
+ connect(mute, SIGNAL(stateChanged(bool)),
+ m_muteSigMapper, SLOT(map()));
+ m_recordSigMapper->setMapping(record, track->getPosition());
+ m_muteSigMapper->setMapping(mute, track->getPosition());
+
+ // Store the KLedButton
+ //
+ m_muteLeds.push_back(mute);
+ m_recordLeds.push_back(record);
+
+ //
+ // Track label
+ //
+ trackLabel = new TrackLabel(trackId, track->getPosition(), trackHBox);
+ hblayout->addWidget(trackLabel);
+ hblayout->addSpacing(vuSpacing);
+
+ if (track->getLabel() == std::string("")) {
+ Rosegarden::Instrument *ins =
+ m_doc->getStudio().getInstrumentById(track->getInstrument());
+ if (ins && ins->getType() == Rosegarden::Instrument::Audio) {
+ trackLabel->getTrackLabel()->setText(i18n("<untitled audio>"));
+ } else {
+ trackLabel->getTrackLabel()->setText(i18n("<untitled>"));
+ }
+ }
+ else
+ trackLabel->getTrackLabel()->setText(strtoqstr(track->getLabel()));
+
+ trackLabel->setFixedSize(labelWidth, m_cellSize - buttonGap);
+ trackLabel->setFixedHeight(m_cellSize - buttonGap);
+ trackLabel->setIndent(7);
+
+ connect(trackLabel, SIGNAL(renameTrack(QString, TrackId)),
+ SLOT(slotRenameTrack(QString, TrackId)));
+
+ // Store the TrackLabel pointer
+ //
+ m_trackLabels.push_back(trackLabel);
+
+ // Connect it
+ setButtonMapping(trackLabel, trackId);
+
+ connect(trackLabel, SIGNAL(changeToInstrumentList()),
+ m_instListSigMapper, SLOT(map()));
+ connect(trackLabel, SIGNAL(clicked()),
+ m_clickedSigMapper, SLOT(map()));
+
+ //
+ // instrument label
+ //
+ Rosegarden::Instrument *ins =
+ m_doc->getStudio().getInstrumentById(track->getInstrument());
+
+ QString instrumentName(i18n("<no instrument>"));
+ if (ins) instrumentName = strtoqstr(ins->getPresentationName());
+
+ // Set label to program change if it's being sent
+ //
+ if (ins != 0 && ins->sendsProgramChange())
+ trackLabel->setAlternativeLabel(strtoqstr(ins->getProgramName()));
+
+ trackLabel->showLabel(m_trackInstrumentLabels);
+
+ mute->setFixedSize(m_cellSize - buttonGap, m_cellSize - buttonGap);
+ record->setFixedSize(m_cellSize - buttonGap, m_cellSize - buttonGap);
+
+ // set the mute button
+ //
+ if (track->isMuted())
+ mute->off();
+
+ return trackHBox;
+}
+
+}
+#include "TrackButtons.moc"
diff --git a/src/gui/editors/segment/TrackButtons.h b/src/gui/editors/segment/TrackButtons.h
new file mode 100644
index 0000000..a61601d
--- /dev/null
+++ b/src/gui/editors/segment/TrackButtons.h
@@ -0,0 +1,228 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRACKBUTTONS_H_
+#define _RG_TRACKBUTTONS_H_
+
+#include "base/MidiProgram.h"
+#include "base/Track.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "TrackLabel.h"
+#include <qframe.h>
+#include <qstring.h>
+#include <vector>
+
+
+class QWidget;
+class QVBoxLayout;
+class QSignalMapper;
+class QPopupMenu;
+class QObject;
+
+
+namespace Rosegarden
+{
+
+class TrackVUMeter;
+class RosegardenGUIDoc;
+class KLedButton;
+class Instrument;
+
+
+class TrackButtons : public QFrame
+{
+ Q_OBJECT
+public:
+
+ TrackButtons(RosegardenGUIDoc* doc,
+ unsigned int trackCellHeight,
+ unsigned int trackLabelWidth,
+ bool showTrackLabels,
+ int overallHeight,
+ QWidget* parent = 0,
+ const char* name = 0,
+ WFlags f=0);
+
+ ~TrackButtons();
+
+ /// Return a vector of muted tracks
+ std::vector<int> mutedTracks();
+
+ /// Return a vector of highlighted tracks
+ std::vector<int> getHighlightedTracks();
+
+ void changeTrackInstrumentLabels(TrackLabel::InstrumentTrackLabels label);
+
+ /**
+ * Change the instrument label to something else like
+ * an actual program name rather than a meaningless
+ * device number and midi channel
+ */
+ void changeInstrumentLabel(InstrumentId id, QString label);
+
+ void changeTrackLabel(TrackId id, QString label);
+
+ // Select a label from outside this class by position
+ //
+ void selectLabel(int trackId);
+
+ /*
+ * Set the mute button down or up
+ */
+ void setMuteButton(TrackId track, bool value);
+
+ /*
+ * Make this available so that others can set record buttons down
+ */
+ void setRecordTrack(int position, bool value);
+
+ /**
+ * Precalculate the Instrument popup so we don't have to every
+ * time it appears
+ * not protected because also used by the RosegardenGUIApp
+ *
+ * @see RosegardenGUIApp#slotPopulateTrackInstrumentPopup()
+ */
+ void populateInstrumentPopup(Instrument *thisTrackInstr, QPopupMenu* instrumentPopup);
+
+signals:
+ // to emit what Track has been selected
+ //
+ void trackSelected(int);
+ void instrumentSelected(int);
+
+ void widthChanged();
+
+ // to tell the notation canvas &c when a name changes
+ //
+ void nameChanged();
+
+ // document modified (mute button)
+ //
+ void modified();
+
+ // A record button has been pressed - if we're setting to an audio
+ // track we need to tell the sequencer for live monitoring
+ // purposes.
+ //
+ void recordButton(TrackId track, bool state);
+
+ // A mute button has been pressed
+ //
+ void muteButton(TrackId track, bool state);
+
+public slots:
+
+ void slotToggleRecordTrack(int position);
+ void slotToggleMutedTrack(int mutedTrack);
+ void slotUpdateTracks();
+ void slotRenameTrack(QString newName, TrackId trackId);
+ void slotSetTrackMeter(float value, int position);
+ void slotSetMetersByInstrument(float value, InstrumentId id);
+
+ void slotInstrumentSelection(int);
+ void slotInstrumentPopupActivated(int);
+ void slotTrackInstrumentSelection(TrackId, int);
+
+ // ensure track buttons match the Composition
+ //
+ void slotSynchroniseWithComposition();
+
+ // Convert a positional selection into a track selection and re-emit
+ //
+ void slotLabelSelected(int position);
+
+protected:
+
+ /**
+ * Populate the track buttons themselves with Instrument information
+ */
+ void populateButtons();
+
+ /**
+ * Remove buttons and clear iterators for a position
+ */
+ void removeButtons(unsigned int position);
+
+ /**
+ * Set record button - graphically only
+ */
+ void setRecordButton(int position, bool down);
+
+ /**
+ * buttons, starting at the specified index
+ */
+ void makeButtons();
+
+ QFrame* makeButton(TrackId trackId);
+ QString getPresentationName(Instrument *);
+
+ void setButtonMapping(QObject*, TrackId);
+
+ //--------------- Data members ---------------------------------
+
+ RosegardenGUIDoc *m_doc;
+
+ QVBoxLayout *m_layout;
+
+ std::vector<KLedButton *> m_muteLeds;
+ std::vector<KLedButton *> m_recordLeds;
+ std::vector<TrackLabel *> m_trackLabels;
+ std::vector<TrackVUMeter *> m_trackMeters;
+ std::vector<QFrame *> m_trackHBoxes;
+
+ QSignalMapper *m_recordSigMapper;
+ QSignalMapper *m_muteSigMapper;
+ QSignalMapper *m_clickedSigMapper;
+ QSignalMapper *m_instListSigMapper;
+
+ // Number of tracks on our view
+ //
+ unsigned int m_tracks;
+
+ // The pixel offset from the top - just to overcome
+ // the borders
+ int m_offset;
+
+ // The height of the cells
+ //
+ int m_cellSize;
+
+ // gaps between elements
+ //
+ int m_borderGap;
+
+ int m_trackLabelWidth;
+ int m_popupItem;
+
+ TrackLabel::InstrumentTrackLabels m_trackInstrumentLabels;
+ int m_lastSelected;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TrackEditor.cpp b/src/gui/editors/segment/TrackEditor.cpp
new file mode 100644
index 0000000..32c2b02
--- /dev/null
+++ b/src/gui/editors/segment/TrackEditor.cpp
@@ -0,0 +1,827 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TrackEditor.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kstddirs.h>
+#include "misc/Debug.h"
+#include "document/ConfigGroups.h"
+#include "gui/application/RosegardenDCOP.h"
+#include "gui/seqmanager/SequenceManager.h"
+#include "gui/rulers/StandardRuler.h"
+#include "base/Composition.h"
+#include "base/MidiProgram.h"
+#include "base/RealTime.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "commands/segment/AddTracksCommand.h"
+#include "commands/segment/DeleteTracksCommand.h"
+#include "commands/segment/SegmentEraseCommand.h"
+#include "commands/segment/SegmentInsertCommand.h"
+#include "commands/segment/SegmentRepeatToCopyCommand.h"
+#include "segmentcanvas/CompositionModel.h"
+#include "segmentcanvas/CompositionModelImpl.h"
+#include "segmentcanvas/CompositionView.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/rulers/ChordNameRuler.h"
+#include "gui/rulers/TempoRuler.h"
+#include "gui/rulers/LoopRuler.h"
+#include "gui/widgets/ProgressDialog.h"
+#include "gui/widgets/QDeferScrollView.h"
+#include "sound/AudioFile.h"
+#include "TrackButtons.h"
+#include "TrackEditorIface.h"
+#include <dcopobject.h>
+#include <kcommand.h>
+#include <kglobal.h>
+#include <kmessagebox.h>
+#include <qapplication.h>
+#include <qcursor.h>
+#include <qfont.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qscrollview.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qstrlist.h>
+#include <qwidget.h>
+#include <qvalidator.h>
+#include <qdragobject.h>
+#include <qtextstream.h>
+
+
+namespace Rosegarden
+{
+
+TrackEditor::TrackEditor(RosegardenGUIDoc* doc,
+ QWidget* rosegardenguiview,
+ RulerScale *rulerScale,
+ bool showTrackLabels,
+ double initialUnitsPerPixel,
+ QWidget* parent, const char* name,
+ WFlags) :
+ DCOPObject("TrackEditorIface"),
+ QWidget(parent, name),
+ m_doc(doc),
+ m_rulerScale(rulerScale),
+ m_topStandardRuler(0),
+ m_bottomStandardRuler(0),
+ m_trackButtons(0),
+ m_segmentCanvas(0),
+ m_trackButtonScroll(0),
+ m_showTrackLabels(showTrackLabels),
+ m_canvasWidth(0),
+ m_compositionRefreshStatusId(doc->getComposition().getNewRefreshStatusId()),
+ m_playTracking(true),
+ m_initialUnitsPerPixel(initialUnitsPerPixel)
+{
+ // accept dnd
+ setAcceptDrops(true);
+
+ init(rosegardenguiview);
+ slotReadjustCanvasSize();
+}
+
+TrackEditor::~TrackEditor()
+{
+ delete m_chordNameRuler;
+ delete m_compositionModel;
+}
+
+void
+TrackEditor::init(QWidget* rosegardenguiview)
+{
+ QGridLayout *grid = new QGridLayout(this, 4, 2);
+
+ int trackLabelWidth = 230;
+ int barButtonsHeight = 25;
+
+ m_chordNameRuler = new ChordNameRuler(m_rulerScale,
+ m_doc,
+ 0.0,
+ 20,
+ this);
+ grid->addWidget(m_chordNameRuler, 0, 1);
+
+ m_tempoRuler = new TempoRuler(m_rulerScale,
+ m_doc,
+ RosegardenGUIApp::self(),
+ 0.0,
+ 24,
+ true,
+ this);
+
+ grid->addWidget(m_tempoRuler, 1, 1);
+
+ m_tempoRuler->connectSignals();
+
+ //
+ // Top Bar Buttons
+ //
+ m_topStandardRuler = new StandardRuler(m_doc,
+ m_rulerScale,
+ 0,
+ barButtonsHeight,
+ false,
+ this, "topbarbuttons");
+ m_topStandardRuler->connectRulerToDocPointer(m_doc);
+
+ grid->addWidget(m_topStandardRuler, 2, 1);
+
+ //
+ // Segment Canvas
+ //
+ m_compositionModel = new CompositionModelImpl(m_doc->getComposition(),
+ m_doc->getStudio(),
+ m_rulerScale, getTrackCellHeight());
+
+ connect(rosegardenguiview, SIGNAL(instrumentParametersChanged(InstrumentId)),
+ m_compositionModel, SLOT(slotInstrumentParametersChanged(InstrumentId)));
+ connect(rosegardenguiview->parent(), SIGNAL(instrumentParametersChanged(InstrumentId)),
+ m_compositionModel, SLOT(slotInstrumentParametersChanged(InstrumentId)));
+
+ m_segmentCanvas = new CompositionView(m_doc, m_compositionModel, this);
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ if (kapp->config()->readBoolEntry("backgroundtextures", true)) {
+ QPixmap background;
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ if (background.load(QString("%1/misc/bg-segmentcanvas.xpm").
+ arg(pixmapDir))) {
+ m_segmentCanvas->setBackgroundPixmap(background);
+ m_segmentCanvas->viewport()->setBackgroundPixmap(background);
+ }
+ }
+
+ //
+ // Bottom Bar Buttons
+ //
+ m_bottomStandardRuler = new StandardRuler(m_doc,
+ m_rulerScale,
+ 0,
+ barButtonsHeight,
+ true,
+ m_segmentCanvas, "bottombarbuttons");
+ m_bottomStandardRuler->connectRulerToDocPointer(m_doc);
+
+ m_segmentCanvas->setBottomFixedWidget(m_bottomStandardRuler);
+
+ grid->addWidget(m_segmentCanvas, 3, 1);
+
+ grid->setColStretch(1, 10); // to make sure the seg canvas doesn't leave a "blank" grey space when
+ // loading a file which has a low zoom factor
+
+ // Track Buttons
+ //
+ // (must be put in a QScrollView)
+ //
+ m_trackButtonScroll = new QDeferScrollView(this);
+ grid->addWidget(m_trackButtonScroll, 3, 0);
+
+ int canvasHeight = getTrackCellHeight() *
+ std::max(40u, m_doc->getComposition().getNbTracks());
+
+ m_trackButtons = new TrackButtons(m_doc,
+ getTrackCellHeight(),
+ trackLabelWidth,
+ m_showTrackLabels,
+ canvasHeight,
+ m_trackButtonScroll->viewport());
+ m_trackButtonScroll->addChild(m_trackButtons);
+ m_trackButtonScroll->setHScrollBarMode(QScrollView::AlwaysOff);
+ m_trackButtonScroll->setVScrollBarMode(QScrollView::AlwaysOff);
+ m_trackButtonScroll->setResizePolicy(QScrollView::AutoOneFit);
+ m_trackButtonScroll->setBottomMargin(m_bottomStandardRuler->height() +
+ m_segmentCanvas->horizontalScrollBar()->height());
+
+ connect(m_trackButtons, SIGNAL(widthChanged()),
+ this, SLOT(slotTrackButtonsWidthChanged()));
+
+ connect(m_trackButtons, SIGNAL(trackSelected(int)),
+ rosegardenguiview, SLOT(slotSelectTrackSegments(int)));
+
+ connect(m_trackButtons, SIGNAL(instrumentSelected(int)),
+ rosegardenguiview, SLOT(slotUpdateInstrumentParameterBox(int)));
+
+ connect(this, SIGNAL(stateChange(QString, bool)),
+ rosegardenguiview, SIGNAL(stateChange(QString, bool)));
+
+ connect(m_trackButtons, SIGNAL(modified()),
+ m_doc, SLOT(slotDocumentModified()));
+
+ connect(m_trackButtons, SIGNAL(muteButton(TrackId, bool)),
+ rosegardenguiview, SLOT(slotSetMuteButton(TrackId, bool)));
+
+ // connect loop rulers' follow-scroll signals
+ connect(m_topStandardRuler->getLoopRuler(), SIGNAL(startMouseMove(int)),
+ m_segmentCanvas, SLOT(startAutoScroll(int)));
+ connect(m_topStandardRuler->getLoopRuler(), SIGNAL(stopMouseMove()),
+ m_segmentCanvas, SLOT(stopAutoScroll()));
+ connect(m_bottomStandardRuler->getLoopRuler(), SIGNAL(startMouseMove(int)),
+ m_segmentCanvas, SLOT(startAutoScroll(int)));
+ connect(m_bottomStandardRuler->getLoopRuler(), SIGNAL(stopMouseMove()),
+ m_segmentCanvas, SLOT(stopAutoScroll()));
+
+ connect(m_segmentCanvas, SIGNAL(contentsMoving(int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+
+ // Synchronize bar buttons' scrollview with segment canvas' scrollbar
+ //
+ connect(m_segmentCanvas->verticalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(slotVerticalScrollTrackButtons(int)));
+
+ connect(m_segmentCanvas->verticalScrollBar(), SIGNAL(sliderMoved(int)),
+ this, SLOT(slotVerticalScrollTrackButtons(int)));
+
+ // scrolling with mouse wheel
+ connect(m_trackButtonScroll, SIGNAL(gotWheelEvent(QWheelEvent*)),
+ m_segmentCanvas, SLOT(slotExternalWheelEvent(QWheelEvent*)));
+
+ // Connect horizontal scrollbar
+ //
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ m_topStandardRuler, SLOT(slotScrollHoriz(int)));
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ m_topStandardRuler, SLOT(slotScrollHoriz(int)));
+
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ m_bottomStandardRuler, SLOT(slotScrollHoriz(int)));
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ m_bottomStandardRuler, SLOT(slotScrollHoriz(int)));
+
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ m_tempoRuler, SLOT(slotScrollHoriz(int)));
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ m_tempoRuler, SLOT(slotScrollHoriz(int)));
+
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ m_chordNameRuler, SLOT(slotScrollHoriz(int)));
+ connect(m_segmentCanvas->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ m_chordNameRuler, SLOT(slotScrollHoriz(int)));
+
+ connect(this, SIGNAL(needUpdate()), m_segmentCanvas, SLOT(slotUpdateSegmentsDrawBuffer()));
+
+ connect(m_segmentCanvas->getModel(),
+ SIGNAL(selectedSegments(const SegmentSelection &)),
+ rosegardenguiview,
+ SLOT(slotSelectedSegments(const SegmentSelection &)));
+
+ connect(m_segmentCanvas, SIGNAL(zoomIn()),
+ RosegardenGUIApp::self(), SLOT(slotZoomIn()));
+ connect(m_segmentCanvas, SIGNAL(zoomOut()),
+ RosegardenGUIApp::self(), SLOT(slotZoomOut()));
+
+ connect(getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(update()));
+
+ connect(m_doc, SIGNAL(pointerPositionChanged(timeT)),
+ this, SLOT(slotSetPointerPosition(timeT)));
+
+ //
+ // pointer and loop drag signals from top and bottom bar buttons (loop rulers actually)
+ //
+ connect(m_topStandardRuler, SIGNAL(dragPointerToPosition(timeT)),
+ this, SLOT(slotPointerDraggedToPosition(timeT)));
+ connect(m_bottomStandardRuler, SIGNAL(dragPointerToPosition(timeT)),
+ this, SLOT(slotPointerDraggedToPosition(timeT)));
+
+ connect(m_topStandardRuler, SIGNAL(dragLoopToPosition(timeT)),
+ this, SLOT(slotLoopDraggedToPosition(timeT)));
+ connect(m_bottomStandardRuler, SIGNAL(dragLoopToPosition(timeT)),
+ this, SLOT(slotLoopDraggedToPosition(timeT)));
+
+ connect(m_doc, SIGNAL(loopChanged(timeT,
+ timeT)),
+ this, SLOT(slotSetLoop(timeT, timeT)));
+}
+
+void TrackEditor::slotReadjustCanvasSize()
+{
+ m_segmentCanvas->slotUpdateSize();
+}
+
+void TrackEditor::slotTrackButtonsWidthChanged()
+{
+ // We need to make sure the trackButtons geometry is fully updated
+ //
+ ProgressDialog::processEvents();
+
+ m_trackButtonScroll->setMinimumWidth(m_trackButtons->width());
+ m_doc->slotDocumentModified();
+}
+
+int TrackEditor::getTrackCellHeight() const
+{
+ int size;
+ static QFont defaultFont;
+
+ // do some scrabbling around for a reasonable size
+ //
+ size = defaultFont.pixelSize();
+
+ if (size < 8) {
+ if (QApplication::font(this).pixelSize() < 8)
+ size = 12;
+ else
+ size = QApplication::font(this).pixelSize();
+ }
+
+ return size + 12;
+}
+
+bool TrackEditor::isCompositionModified()
+{
+ return m_doc->getComposition().getRefreshStatus
+ (m_compositionRefreshStatusId).needsRefresh();
+}
+
+void TrackEditor::setCompositionModified(bool c)
+{
+ m_doc->getComposition().getRefreshStatus
+ (m_compositionRefreshStatusId).setNeedsRefresh(c);
+}
+
+void TrackEditor::updateRulers()
+{
+ if (getTempoRuler() != 0)
+ getTempoRuler()->update();
+
+ if (getChordNameRuler() != 0)
+ getChordNameRuler()->update();
+
+ getTopStandardRuler()->update();
+ getBottomStandardRuler()->update();
+}
+
+void TrackEditor::paintEvent(QPaintEvent* e)
+{
+ if (isCompositionModified()) {
+
+ slotReadjustCanvasSize();
+ m_trackButtons->slotUpdateTracks();
+ m_segmentCanvas->clearSegmentRectsCache(true);
+ m_segmentCanvas->updateContents();
+
+ Composition &composition = m_doc->getComposition();
+
+ if (composition.getNbSegments() == 0) {
+ emit stateChange("have_segments", false); // no segments : reverse state
+ emit stateChange("have_selection", false); // no segments : reverse state
+ } else {
+ emit stateChange("have_segments", true);
+ if (m_segmentCanvas->haveSelection())
+ emit stateChange("have_selection", true);
+ else
+ emit stateChange("have_selection", false); // no selection : reverse state
+ }
+
+ setCompositionModified(false);
+ }
+
+ QWidget::paintEvent(e);
+}
+
+void TrackEditor::slotAddTracks(unsigned int nbNewTracks,
+ InstrumentId id,
+ int position)
+{
+ Composition &comp = m_doc->getComposition();
+
+ AddTracksCommand* command = new AddTracksCommand(&comp, nbNewTracks, id,
+ position);
+ addCommandToHistory(command);
+ slotReadjustCanvasSize();
+}
+
+void TrackEditor::slotDeleteTracks(std::vector<TrackId> tracks)
+{
+ Composition &comp = m_doc->getComposition();
+
+ DeleteTracksCommand* command = new DeleteTracksCommand(&comp, tracks);
+ addCommandToHistory(command);
+}
+
+void TrackEditor::addSegment(int track, int time, unsigned int duration)
+{
+ if (!m_doc)
+ return ; // sanity check
+
+ SegmentInsertCommand *command =
+ new SegmentInsertCommand(m_doc, track, time, duration);
+
+ addCommandToHistory(command);
+}
+
+void TrackEditor::slotSegmentOrderChanged(int section, int fromIdx, int toIdx)
+{
+ RG_DEBUG << QString("TrackEditor::segmentOrderChanged(section : %1, from %2, to %3)")
+ .arg(section).arg(fromIdx).arg(toIdx) << endl;
+
+ //!!! how do we get here? need to involve a command
+ emit needUpdate();
+}
+
+void
+TrackEditor::slotCanvasScrolled(int x, int y)
+{
+ // update the pointer position if the user is dragging it from the loop ruler
+ if ((m_topStandardRuler && m_topStandardRuler->getLoopRuler() &&
+ m_topStandardRuler->getLoopRuler()->hasActiveMousePress() &&
+ !m_topStandardRuler->getLoopRuler()->getLoopingMode()) ||
+ (m_bottomStandardRuler && m_bottomStandardRuler->getLoopRuler() &&
+ m_bottomStandardRuler->getLoopRuler()->hasActiveMousePress() &&
+ !m_bottomStandardRuler->getLoopRuler()->getLoopingMode())) {
+
+ int mx = m_segmentCanvas->viewport()->mapFromGlobal(QCursor::pos()).x();
+ m_segmentCanvas->setPointerPos(x + mx);
+
+ // bad idea, creates a feedback loop
+ // timeT t = m_segmentCanvas->grid().getRulerScale()->getTimeForX(x + mx);
+ // slotSetPointerPosition(t);
+ }
+}
+
+void
+TrackEditor::slotSetPointerPosition(timeT position)
+{
+ SimpleRulerScale *ruler =
+ dynamic_cast<SimpleRulerScale*>(m_rulerScale);
+
+ if (!ruler)
+ return ;
+
+ double pos = m_segmentCanvas->grid().getRulerScale()->getXForTime(position);
+
+ int currentPointerPos = m_segmentCanvas->getPointerPos();
+
+ double distance = pos - currentPointerPos;
+ if (distance < 0.0)
+ distance = -distance;
+
+ if (distance >= 1.0) {
+
+ if (m_doc && m_doc->getSequenceManager() &&
+ (m_doc->getSequenceManager()->getTransportStatus() != STOPPED)) {
+
+ if (m_playTracking) {
+ getSegmentCanvas()->slotScrollHoriz(int(double(position) / ruler->getUnitsPerPixel()));
+ }
+ } else if (!getSegmentCanvas()->isAutoScrolling()) {
+ int newpos = int(double(position) / ruler->getUnitsPerPixel());
+ // RG_DEBUG << "TrackEditor::slotSetPointerPosition("
+ // << position
+ // << ") : calling canvas->slotScrollHoriz() "
+ // << newpos << endl;
+ getSegmentCanvas()->slotScrollHoriz(newpos);
+ }
+
+ m_segmentCanvas->setPointerPos(pos);
+ }
+
+}
+
+void
+TrackEditor::slotPointerDraggedToPosition(timeT position)
+{
+ int currentPointerPos = m_segmentCanvas->getPointerPos();
+
+ double newPosition;
+
+ if (handleAutoScroll(currentPointerPos, position, newPosition))
+ m_segmentCanvas->setPointerPos(int(newPosition));
+}
+
+void
+TrackEditor::slotLoopDraggedToPosition(timeT position)
+{
+ if (m_doc) {
+ int currentEndLoopPos = m_doc->getComposition().getLoopEnd();
+ double dummy;
+ handleAutoScroll(currentEndLoopPos, position, dummy);
+ }
+}
+
+bool TrackEditor::handleAutoScroll(int currentPosition, timeT newTimePosition, double &newPosition)
+{
+ SimpleRulerScale *ruler =
+ dynamic_cast<SimpleRulerScale*>(m_rulerScale);
+
+ if (!ruler)
+ return false;
+
+ newPosition = m_segmentCanvas->grid().getRulerScale()->getXForTime(newTimePosition);
+
+ double distance = fabs(newPosition - currentPosition);
+
+ bool moveDetected = distance >= 1.0;
+
+ if (moveDetected) {
+
+ if (m_doc && m_doc->getSequenceManager() &&
+ (m_doc->getSequenceManager()->getTransportStatus() != STOPPED)) {
+
+ if (m_playTracking) {
+ getSegmentCanvas()->slotScrollHoriz(int(double(newTimePosition) / ruler->getUnitsPerPixel()));
+ }
+ } else {
+ int newpos = int(double(newTimePosition) / ruler->getUnitsPerPixel());
+ getSegmentCanvas()->slotScrollHorizSmallSteps(newpos);
+ getSegmentCanvas()->doAutoScroll();
+ }
+
+ }
+
+ return moveDetected;
+}
+
+void
+TrackEditor::slotToggleTracking()
+{
+ m_playTracking = !m_playTracking;
+}
+
+void
+TrackEditor::slotSetLoop(timeT start, timeT end)
+{
+ getTopStandardRuler()->getLoopRuler()->slotSetLoopMarker(start, end);
+ getBottomStandardRuler()->getLoopRuler()->slotSetLoopMarker(start, end);
+}
+
+MultiViewCommandHistory*
+TrackEditor::getCommandHistory()
+{
+ return m_doc->getCommandHistory();
+}
+
+void
+TrackEditor::addCommandToHistory(KCommand *command)
+{
+ getCommandHistory()->addCommand(command);
+}
+
+void
+TrackEditor::slotScrollToTrack(int track)
+{
+ // Find the vertical track pos
+ int newY = track * getTrackCellHeight();
+
+ RG_DEBUG << "TrackEditor::scrollToTrack(" << track <<
+ ") scrolling to Y " << newY << endl;
+
+ // Scroll the segment view; it will scroll tracks by connected signals
+ // slotVerticalScrollTrackButtons(newY);
+ m_segmentCanvas->slotScrollVertSmallSteps(newY);
+}
+
+void
+TrackEditor::slotDeleteSelectedSegments()
+{
+ KMacroCommand *macro = new KMacroCommand("Delete Segments");
+
+ SegmentSelection segments =
+ m_segmentCanvas->getSelectedSegments();
+
+ if (segments.size() == 0)
+ return ;
+
+ SegmentSelection::iterator it;
+
+ // Clear the selection before erasing the Segments
+ // the selection points to
+ //
+ m_segmentCanvas->getModel()->clearSelected();
+
+ // Create the compound command
+ //
+ for (it = segments.begin(); it != segments.end(); it++) {
+ macro->addCommand(new SegmentEraseCommand(*it,
+ &m_doc->getAudioFileManager()));
+ }
+
+ addCommandToHistory(macro);
+
+}
+
+void
+TrackEditor::slotTurnRepeatingSegmentToRealCopies()
+{
+ RG_DEBUG << "TrackEditor::slotTurnRepeatingSegmentToRealCopies" << endl;
+
+ SegmentSelection segments =
+ m_segmentCanvas->getSelectedSegments();
+
+ if (segments.size() == 0)
+ return ;
+
+ QString text;
+
+ if (segments.size() == 1)
+ text = i18n("Turn Repeating Segment into Real Copies");
+ else
+ text = i18n("Turn Repeating Segments into Real Copies");
+
+ KMacroCommand *macro = new KMacroCommand(text);
+
+ SegmentSelection::iterator it = segments.begin();
+ for (; it != segments.end(); it++) {
+ if ((*it)->isRepeating()) {
+ macro->addCommand(new SegmentRepeatToCopyCommand(*it));
+ }
+ }
+
+ addCommandToHistory(macro);
+
+}
+
+void
+TrackEditor::slotVerticalScrollTrackButtons(int y)
+{
+ m_trackButtonScroll->setContentsPos(0, y);
+}
+
+void TrackEditor::dragEnterEvent(QDragEnterEvent *event)
+{
+ event->accept(QUriDrag::canDecode(event) ||
+ QTextDrag::canDecode(event));
+}
+
+void TrackEditor::dropEvent(QDropEvent* event)
+{
+ QStrList uri;
+ QString text;
+
+ int heightAdjust = 0;
+ //int widthAdjust = 0;
+
+ // Adjust any drop event height position by visible rulers
+ //
+ if (m_topStandardRuler && m_topStandardRuler->isVisible())
+ heightAdjust += m_topStandardRuler->height();
+
+ if (m_tempoRuler && m_tempoRuler->isVisible())
+ heightAdjust += m_tempoRuler->height();
+
+ if (m_chordNameRuler && m_chordNameRuler->isVisible())
+ heightAdjust += m_chordNameRuler->height();
+
+ QPoint posInSegmentCanvas =
+ m_segmentCanvas->viewportToContents
+ (m_segmentCanvas->
+ viewport()->mapFrom(this, event->pos()));
+
+ int trackPos = m_segmentCanvas->grid().getYBin(posInSegmentCanvas.y());
+
+ timeT time =
+// m_segmentCanvas->grid().getRulerScale()->
+// getTimeForX(posInSegmentCanvas.x());
+ m_segmentCanvas->grid().snapX(posInSegmentCanvas.x());
+
+
+ if (QUriDrag::decode(event, uri)) {
+ RG_DEBUG << "TrackEditor::dropEvent() : got URI :"
+ << uri.first() << endl;
+ QString uriPath = uri.first();
+
+ if (uriPath.endsWith(".rg")) {
+ emit droppedDocument(uriPath);
+ } else {
+
+ QStrList uris;
+ QString uri;
+ if (QUriDrag::decode(event, uris)) uri = uris.first();
+// QUriDrag::decodeLocalFiles(event, files);
+// QString filePath = files.first();
+
+ RG_DEBUG << "TrackEditor::dropEvent() : got URI: "
+ << uri << endl;
+
+ RG_DEBUG << "TrackEditor::dropEvent() : dropping at track pos = "
+ << trackPos
+ << ", time = "
+ << time
+ << ", x = "
+ << event->pos().x()
+ << ", mapped x = "
+ << posInSegmentCanvas.x()
+ << endl;
+
+ Track* track = m_doc->getComposition().getTrackByPosition(trackPos);
+ if (track) {
+ QString audioText;
+ QTextOStream t(&audioText);
+
+ t << uri << "\n";
+ t << track->getId() << "\n";
+ t << time << "\n";
+
+ emit droppedNewAudio(audioText);
+ }
+
+ }
+
+ } else if (QTextDrag::decode(event, text)) {
+ RG_DEBUG << "TrackEditor::dropEvent() : got text info " << endl;
+ //<< text << endl;
+
+ if (text.endsWith(".rg")) {
+ emit droppedDocument(text);
+ //
+ // WARNING
+ //
+ // DO NOT PERFORM ANY OPERATIONS AFTER THAT
+ // EMITTING THIS SIGNAL TRIGGERS THE LOADING OF A NEW DOCUMENT
+ // AND AS A CONSEQUENCE THE DELETION OF THIS TrackEditor OBJECT
+ //
+ } else {
+
+ QTextIStream s(&text);
+
+ QString id;
+ AudioFileId audioFileId;
+ RealTime startTime, endTime;
+
+ // read the audio info checking for end of stream
+ s >> id;
+ s >> audioFileId;
+ s >> startTime.sec;
+ s >> startTime.nsec;
+ s >> endTime.sec;
+ s >> endTime.nsec;
+
+ if (id == "AudioFileManager") { // only create something if this is data from the right client
+
+
+ // Drop this audio segment if we have a valid track number
+ // (could also check for time limits too)
+ //
+ Track* track = m_doc->getComposition().getTrackByPosition(trackPos);
+ if (track) {
+
+ RG_DEBUG << "TrackEditor::dropEvent() : dropping at track pos = "
+ << trackPos
+ << ", time = "
+ << time
+ << ", x = "
+ << event->pos().x()
+ << ", map = "
+ << posInSegmentCanvas.x()
+ << endl;
+
+ QString audioText;
+ QTextOStream t(&audioText);
+ t << audioFileId << "\n";
+ t << track->getId() << "\n";
+ t << time << "\n"; // time on canvas
+ t << startTime.sec << "\n";
+ t << startTime.nsec << "\n";
+ t << endTime.sec << "\n";
+ t << endTime.nsec << "\n";
+
+ emit droppedAudio(audioText);
+ }
+
+ } else {
+
+ KMessageBox::sorry(this, i18n("You can't drop files into Rosegarden from this client. Try using Konqueror instead."));
+
+ }
+
+ }
+
+ // SEE WARNING ABOVE - DON'T DO ANYTHING, THIS OBJECT MAY NOT
+ // EXIST AT THIS POINT.
+
+ }
+}
+
+}
+#include "TrackEditor.moc"
diff --git a/src/gui/editors/segment/TrackEditor.h b/src/gui/editors/segment/TrackEditor.h
new file mode 100644
index 0000000..6670a15
--- /dev/null
+++ b/src/gui/editors/segment/TrackEditor.h
@@ -0,0 +1,248 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRACKEDITOR_H_
+#define _RG_TRACKEDITOR_H_
+
+#include "base/MidiProgram.h"
+#include <map>
+#include "TrackEditorIface.h"
+#include <qstring.h>
+#include <qwidget.h>
+#include "base/Event.h"
+#include "gui/editors/segment/TrackButtons.h"
+
+
+class QPaintEvent;
+class QDropEvent;
+class QDragEnterEvent;
+class KCommand;
+
+
+namespace Rosegarden
+{
+
+class TrackButtons;
+class TempoRuler;
+class Segment;
+class RulerScale;
+class RosegardenGUIDoc;
+class QDeferScrollView;
+class MultiViewCommandHistory;
+class CompositionView;
+class CompositionModel;
+class ChordNameRuler;
+class StandardRuler;
+
+
+/**
+ * Global widget for segment edition.
+ *
+ * Shows a global overview of the composition, and lets the user
+ * manipulate the segments
+ *
+ * @see CompositionView
+ */
+class TrackEditor : public QWidget, virtual public TrackEditorIface
+{
+ Q_OBJECT
+public:
+ /**
+ * Create a new TrackEditor representing the document \a doc
+ */
+ TrackEditor(RosegardenGUIDoc* doc,
+ QWidget* rosegardenguiview,
+ RulerScale *rulerScale,
+ bool showTrackLabels,
+ double initialUnitsPerPixel = 0,
+ QWidget* parent = 0, const char* name = 0,
+ WFlags f=0);
+
+ ~TrackEditor();
+
+ CompositionView* getSegmentCanvas() { return m_segmentCanvas; }
+ TempoRuler* getTempoRuler() { return m_tempoRuler; }
+ ChordNameRuler*getChordNameRuler() { return m_chordNameRuler; }
+ StandardRuler* getTopStandardRuler() { return m_topStandardRuler; }
+ StandardRuler* getBottomStandardRuler() { return m_bottomStandardRuler; }
+ TrackButtons* getTrackButtons() { return m_trackButtons; }
+ RulerScale* getRulerScale() { return m_rulerScale; }
+
+ int getTrackCellHeight() const;
+
+ /**
+ * Add a new segment - DCOP interface
+ */
+ virtual void addSegment(int track, int start, unsigned int duration);
+
+ /**
+ * Manage command history
+ */
+ MultiViewCommandHistory *getCommandHistory();
+ void addCommandToHistory(KCommand *command);
+
+ void updateRulers();
+
+ bool isTracking() const { return m_playTracking; }
+
+public slots:
+
+ /**
+ * Scroll the view such that the numbered track is on-screen
+ */
+ void slotScrollToTrack(int trackPosition);
+
+ /**
+ * Set the position pointer during playback
+ */
+ void slotSetPointerPosition(timeT position);
+
+ /**
+ * Update the pointer position as it is being dragged along
+ * This changes how the segment canvas will scroll to follow the pointer
+ */
+ void slotPointerDraggedToPosition(timeT position);
+
+ /**
+ * Update the loop end position as it is being dragged along
+ * This changes how the segment canvas will scroll to follow the pointer
+ */
+ void slotLoopDraggedToPosition(timeT position);
+
+ /**
+ * Act on a canvas scroll event
+ */
+ void slotCanvasScrolled(int, int);
+
+ /**
+ * Adjust the canvas size to that required for the composition
+ */
+ void slotReadjustCanvasSize();
+
+ /**
+ * Show the given loop on the ruler or wherever
+ */
+ void slotSetLoop(timeT start, timeT end);
+
+ /**
+ * Add given number of tracks
+ */
+ void slotAddTracks(unsigned int nbTracks, InstrumentId id, int position);
+
+ /*
+ * Delete a given track
+ */
+ void slotDeleteTracks(std::vector<TrackId> tracks);
+
+ void slotDeleteSelectedSegments();
+ void slotTurnRepeatingSegmentToRealCopies();
+
+ void slotToggleTracking();
+
+protected slots:
+ void slotSegmentOrderChanged(int section, int fromIdx, int toIdx);
+
+ void slotTrackButtonsWidthChanged();
+
+ /// Scroll the track buttons along with the segment canvas
+ void slotVerticalScrollTrackButtons(int y);
+
+signals:
+ /**
+ * Emitted when the represented data changed and the SegmentCanvas
+ * needs to update itself
+ *
+ * @see SegmentCanvas::update()
+ */
+ void needUpdate();
+
+ /**
+ * sent back to RosegardenGUI
+ */
+ void stateChange(QString, bool);
+
+ /**
+ * A URI to a Rosegarden document was dropped on the canvas
+ *
+ * @see RosegardenGUI#slotOpenURL()
+ */
+ void droppedDocument(QString uri);
+
+ /**
+ * An audio file was dropped from the audio manager dialog
+ */
+ void droppedAudio(QString audioDesc);
+
+ /**
+ * And audio file was dropped from konqi say and needs to be
+ * inserted into AudioManagerDialog before adding to the
+ * composition.
+ */
+ void droppedNewAudio(QString audioDesc);
+
+protected:
+
+ virtual void dragEnterEvent(QDragEnterEvent *event);
+ virtual void dropEvent(QDropEvent*);
+
+ virtual void paintEvent(QPaintEvent* e);
+
+ void init(QWidget *);
+
+ bool isCompositionModified();
+ void setCompositionModified(bool);
+
+ /// return true if an actual move occurred between current and new position, newPosition contains the horiz. pos corresponding to newTimePosition
+ bool handleAutoScroll(int currentPosition, timeT newTimePosition, double& newPosition);
+
+ //--------------- Data members ---------------------------------
+
+ RosegardenGUIDoc *m_doc;
+ RulerScale *m_rulerScale;
+ TempoRuler *m_tempoRuler;
+ ChordNameRuler *m_chordNameRuler;
+ StandardRuler *m_topStandardRuler;
+ StandardRuler *m_bottomStandardRuler;
+ TrackButtons *m_trackButtons;
+ CompositionView *m_segmentCanvas;
+ CompositionModel *m_compositionModel;
+ QDeferScrollView *m_trackButtonScroll;
+
+ bool m_showTrackLabels;
+ unsigned int m_canvasWidth;
+ unsigned int m_compositionRefreshStatusId;
+ bool m_playTracking;
+
+ typedef std::map<Segment *, unsigned int>
+ SegmentRefreshStatusIdMap;
+ SegmentRefreshStatusIdMap m_segmentsRefreshStatusIds;
+
+ double m_initialUnitsPerPixel;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TrackEditorIface.cpp b/src/gui/editors/segment/TrackEditorIface.cpp
new file mode 100644
index 0000000..8238c1d
--- /dev/null
+++ b/src/gui/editors/segment/TrackEditorIface.cpp
@@ -0,0 +1,33 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TrackEditorIface.h"
+
+#include <dcopobject.h>
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/editors/segment/TrackEditorIface.h b/src/gui/editors/segment/TrackEditorIface.h
new file mode 100644
index 0000000..1637591
--- /dev/null
+++ b/src/gui/editors/segment/TrackEditorIface.h
@@ -0,0 +1,55 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRACKEDITORIFACE_H_
+#define _RG_TRACKEDITORIFACE_H_
+
+#include <dcopobject.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * TrackEditor DCOP Interface
+ *
+ * @see TrackEditor
+ */
+class TrackEditorIface : virtual public DCOPObject
+{
+ K_DCOP
+public:
+k_dcop:
+ virtual void addSegment(int instrument, int start, unsigned int length) = 0;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TrackHeader.cpp b/src/gui/editors/segment/TrackHeader.cpp
new file mode 100644
index 0000000..d7ca6d3
--- /dev/null
+++ b/src/gui/editors/segment/TrackHeader.cpp
@@ -0,0 +1,64 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TrackHeader.h"
+
+#include <qheader.h>
+#include <qpainter.h>
+#include <qrect.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+TrackHeader::~TrackHeader()
+{}
+
+void
+TrackHeader::paintEvent(QPaintEvent *e)
+{
+ QPainter p( this );
+ p.setPen( colorGroup().buttonText() );
+ int pos = (orientation() == Horizontal)
+ ? e->rect().left()
+ : e->rect().top();
+ int id = mapToIndex( sectionAt( pos + offset() ) );
+ if ( id < 0 )
+ if ( pos > 0 )
+ return ;
+ else
+ id = 0;
+ for ( int i = id; i < count(); i++ ) {
+ QRect r = sRect( i );
+ paintSection( &p, i, r );
+ if ( orientation() == Horizontal && r. right() >= e->rect().right() ||
+ orientation() == Vertical && r. bottom() >= e->rect().bottom() )
+ return ;
+ }
+
+}
+
+}
diff --git a/src/gui/editors/segment/TrackHeader.h b/src/gui/editors/segment/TrackHeader.h
new file mode 100644
index 0000000..fe404c3
--- /dev/null
+++ b/src/gui/editors/segment/TrackHeader.h
@@ -0,0 +1,65 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRACKHEADER_H_
+#define _RG_TRACKHEADER_H_
+
+#include <qheader.h>
+
+
+class QWidget;
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+class TrackHeader : public QHeader
+{
+
+public:
+ TrackHeader(int number,
+ QWidget *parent=0,
+ const char *name=0 ):
+ QHeader(number, parent, name) {;}
+ ~TrackHeader();
+
+protected:
+ virtual void paintEvent(QPaintEvent *pe);
+// void paintSection(QPainter * p, int index, QRect fr);
+// void paintSectionLabel (QPainter * p, int index, const QRect & fr);
+// QRect sRect (int index);
+
+private:
+
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TrackLabel.cpp b/src/gui/editors/segment/TrackLabel.cpp
new file mode 100644
index 0000000..90561d1
--- /dev/null
+++ b/src/gui/editors/segment/TrackLabel.cpp
@@ -0,0 +1,203 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TrackLabel.h"
+
+#include <klocale.h>
+#include "base/Track.h"
+#include <klineeditdlg.h>
+#include <qfont.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+#include <qwidgetstack.h>
+#include <qvalidator.h>
+
+
+namespace Rosegarden
+{
+
+TrackLabel::TrackLabel(TrackId id,
+ int position,
+ QWidget *parent,
+ const char *name):
+ QWidgetStack(parent, name),
+ m_instrumentLabel(new QLabel(this)),
+ m_trackLabel(new QLabel(this)),
+ m_id(id),
+ m_position(position)
+{
+ QFont font;
+ font.setPointSize(font.pointSize() * 95 / 100);
+ if (font.pixelSize() > 14)
+ font.setPixelSize(14);
+ font.setBold(false);
+ m_instrumentLabel->setFont(font);
+ m_trackLabel->setFont(font);
+
+ addWidget(m_instrumentLabel, ShowInstrument);
+ addWidget(m_trackLabel, ShowTrack);
+ raiseWidget(ShowTrack);
+
+ m_instrumentLabel->setFrameShape(QFrame::NoFrame);
+ m_trackLabel->setFrameShape(QFrame::NoFrame);
+
+ m_pressTimer = new QTimer(this);
+
+ connect(m_pressTimer, SIGNAL(timeout()),
+ this, SIGNAL(changeToInstrumentList()));
+
+ QToolTip::add
+ (this, i18n("Click and hold with left mouse button to assign this Track to an Instrument."));
+
+}
+
+TrackLabel::~TrackLabel()
+{}
+
+void TrackLabel::setIndent(int i)
+{
+ m_instrumentLabel->setIndent(i);
+ m_trackLabel->setIndent(i);
+}
+
+void TrackLabel::setAlternativeLabel(const QString &label)
+{
+ // recover saved original
+ if (label.isEmpty()) {
+
+ if (!m_alternativeLabel.isEmpty())
+ m_instrumentLabel->setText(m_alternativeLabel);
+
+ // do nothing if we've got nothing to swap
+ return ;
+ }
+
+ // Store the current (first) label
+ //
+ if (m_alternativeLabel.isEmpty())
+ m_alternativeLabel = m_instrumentLabel->text();
+
+ // set new label
+ m_instrumentLabel->setText(label);
+}
+
+void TrackLabel::clearAlternativeLabel()
+{
+ m_alternativeLabel = "";
+}
+
+void TrackLabel::showLabel(InstrumentTrackLabels l)
+{
+ raiseWidget(l);
+}
+
+void
+TrackLabel::setSelected(bool on)
+{
+ if (on) {
+ m_selected = true;
+
+ m_instrumentLabel->setPaletteBackgroundColor(colorGroup().highlight());
+ m_instrumentLabel->setPaletteForegroundColor(colorGroup().highlightedText());
+ m_trackLabel->setPaletteBackgroundColor(colorGroup().highlight());
+ m_trackLabel->setPaletteForegroundColor(colorGroup().highlightedText());
+
+ } else {
+ m_selected = false;
+
+ m_instrumentLabel->setPaletteBackgroundColor(colorGroup().background());
+ m_trackLabel->setPaletteBackgroundColor(colorGroup().background());
+ m_instrumentLabel->setPaletteForegroundColor(colorGroup().text());
+ m_trackLabel->setPaletteForegroundColor(colorGroup().text());
+ }
+ if (visibleWidget())
+ visibleWidget()->update();
+}
+
+void
+TrackLabel::mousePressEvent(QMouseEvent *e)
+{
+ if (e->button() == RightButton) {
+
+ emit clicked();
+ emit changeToInstrumentList();
+
+ } else if (e->button() == LeftButton) {
+
+ // start a timer on this hold
+ m_pressTimer->start(200, true); // 200ms, single shot
+ }
+}
+
+void
+TrackLabel::mouseReleaseEvent(QMouseEvent *e)
+{
+ // stop the timer if running
+ if (m_pressTimer->isActive())
+ m_pressTimer->stop();
+
+ if (e->button() == LeftButton) {
+ emit clicked();
+ }
+}
+
+void
+TrackLabel::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ if (e->button() != LeftButton)
+ return ;
+
+ // Highlight this label alone and cheat using
+ // the clicked signal
+ //
+ emit clicked();
+
+ // Just in case we've got our timing wrong - reapply
+ // this label highlight
+ //
+ setSelected(true);
+
+ bool ok = false;
+
+ QRegExpValidator validator(QRegExp(".*"), this); // empty is OK
+
+ QString newText = KLineEditDlg::getText(i18n("Change track name"),
+ i18n("Enter new track name"),
+ m_trackLabel->text(),
+ &ok,
+ this,
+ &validator);
+
+ if ( ok )
+ emit renameTrack(newText, m_id);
+}
+
+}
+#include "TrackLabel.moc"
diff --git a/src/gui/editors/segment/TrackLabel.h b/src/gui/editors/segment/TrackLabel.h
new file mode 100644
index 0000000..e56d0e5
--- /dev/null
+++ b/src/gui/editors/segment/TrackLabel.h
@@ -0,0 +1,122 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRACKLABEL_H_
+#define _RG_TRACKLABEL_H_
+
+#include "base/Track.h"
+#include <qstring.h>
+#include <qwidgetstack.h>
+
+
+class QWidget;
+class QTimer;
+class QMouseEvent;
+class QLabel;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * Specialises QLabel to create in effect a toggleable and hence
+ * selectable label/label list. In conjunction with TrackButtons
+ * provides a framework for Track selection on the TrackCanvas.
+ */
+class TrackLabel : public QWidgetStack
+{
+Q_OBJECT
+public:
+
+ enum InstrumentTrackLabels
+ {
+ ShowTrack,
+ ShowInstrument,
+ ShowBoth
+ };
+
+ TrackLabel(TrackId id,
+ int position,
+ QWidget *parent,
+ const char *name=0);
+
+ ~TrackLabel();
+
+ // QLabel API delegation - applies on both labels
+ void setIndent(int);
+
+ QLabel* getInstrumentLabel() { return m_instrumentLabel; }
+ QLabel* getTrackLabel() { return m_trackLabel; }
+ void setAlternativeLabel(const QString &label);
+ void clearAlternativeLabel();
+ void showLabel(InstrumentTrackLabels);
+
+ // Encapsulates setting the label to highlighted or not
+ //
+ void setSelected(bool on);
+ bool isSelected() const { return m_selected; }
+
+ void setId(TrackId id) { m_id = id; }
+ TrackId getId() const { return m_id; }
+
+ int getPosition() const { return m_position; }
+ void setPosition(int position) { m_position = position; }
+
+signals:
+ void clicked();
+
+ // We emit this once we've renamed a track
+ //
+ void renameTrack(QString, TrackId);
+
+ void changeToInstrumentList();
+
+protected:
+
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseReleaseEvent(QMouseEvent *e);
+ virtual void mouseDoubleClickEvent(QMouseEvent *e);
+
+ QLabel* getVisibleLabel();
+
+ //--------------- Data members ---------------------------------
+
+ QLabel *m_instrumentLabel;
+ QLabel *m_trackLabel;
+ QString m_alternativeLabel;
+
+ TrackId m_id;
+ int m_position;
+ bool m_selected;
+
+ QTimer *m_pressTimer;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TrackVUMeter.cpp b/src/gui/editors/segment/TrackVUMeter.cpp
new file mode 100644
index 0000000..a638ee7
--- /dev/null
+++ b/src/gui/editors/segment/TrackVUMeter.cpp
@@ -0,0 +1,77 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TrackVUMeter.h"
+
+#include "gui/widgets/VUMeter.h"
+#include <qfont.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+TrackVUMeter::TrackVUMeter(QWidget *parent,
+ VUMeterType type,
+ int width,
+ int height,
+ int position,
+ const char *name):
+ VUMeter(parent, type, false, false, width, height, VUMeter::Horizontal, name),
+ m_position(position), m_textHeight(12)
+{
+ setAlignment(AlignCenter);
+
+ QFont font;
+ font.setPointSize(font.pointSize() * 95 / 100);
+ if (font.pointSize() > 14)
+ font.setPointSize(14);
+ font.setBold(false);
+ setFont(font);
+}
+
+void
+TrackVUMeter::meterStart()
+{
+ clear();
+ setMinimumHeight(m_originalHeight);
+ setMaximumHeight(m_originalHeight);
+ m_active = true;
+}
+
+void
+TrackVUMeter::meterStop()
+{
+ setMinimumHeight(m_textHeight);
+ setMaximumHeight(m_textHeight);
+ setText(QString("%1").arg(m_position + 1));
+ if (m_active) {
+ m_active = false;
+ update();
+ }
+}
+
+}
diff --git a/src/gui/editors/segment/TrackVUMeter.h b/src/gui/editors/segment/TrackVUMeter.h
new file mode 100644
index 0000000..26b8e4e
--- /dev/null
+++ b/src/gui/editors/segment/TrackVUMeter.h
@@ -0,0 +1,65 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRACKVUMETER_H_
+#define _RG_TRACKVUMETER_H_
+
+#include "gui/widgets/VUMeter.h"
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+
+
+class TrackVUMeter: public VUMeter
+{
+public:
+ TrackVUMeter(QWidget *parent = 0,
+ VUMeterType type = Plain,
+ int width = 0,
+ int height = 0,
+ int position = 0,
+ const char *name = 0);
+
+ int getPosition() const { return m_position; }
+
+protected:
+ virtual void meterStart();
+ virtual void meterStop();
+
+private:
+ int m_position;
+ int m_textHeight;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TriggerManagerItem.cpp b/src/gui/editors/segment/TriggerManagerItem.cpp
new file mode 100644
index 0000000..2e7402d
--- /dev/null
+++ b/src/gui/editors/segment/TriggerManagerItem.cpp
@@ -0,0 +1,60 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "TriggerManagerItem.h"
+
+namespace Rosegarden {
+
+int
+TriggerManagerItem::compare(QListViewItem * i, int col, bool ascending) const
+{
+ TriggerManagerItem *ei =
+ dynamic_cast<TriggerManagerItem *>(i);
+
+ if (!ei) return QListViewItem::compare(i, col, ascending);
+
+ // col 0 -> index -- numeric compare
+ // col 1 -> ID -- numeric compare
+ // col 2 -> label -- default string compare
+ // col 3 -> duration -- raw duration compare
+ // col 4 -> base pitch -- pitch compare
+ // col 5 -> base velocity -- numeric compare
+ // col 6 -> usage count -- numeric compare
+ //
+ if (col == 2) {
+ return QListViewItem::compare(i, col, ascending);
+ } else if (col == 3) {
+ if (m_rawDuration < ei->getRawDuration()) return -1;
+ else if (ei->getRawDuration() < m_rawDuration) return 1;
+ else return 0;
+ } else if (col == 4) {
+ if (m_pitch < ei->getPitch()) return -1;
+ else if (ei->getPitch() < m_pitch) return 1;
+ else return 0;
+ } else {
+ return key(col, ascending).toInt() - i->key(col, ascending).toInt();
+ }
+}
+
+}
diff --git a/src/gui/editors/segment/TriggerManagerItem.h b/src/gui/editors/segment/TriggerManagerItem.h
new file mode 100644
index 0000000..c1eb95a
--- /dev/null
+++ b/src/gui/editors/segment/TriggerManagerItem.h
@@ -0,0 +1,72 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRIGGERMANAGERITEM_H_
+#define _RG_TRIGGERMANAGERITEM_H_
+
+#include <klistview.h>
+
+#include "base/Event.h"
+#include "base/TriggerSegment.h"
+
+namespace Rosegarden {
+
+class TriggerManagerItem : public QListViewItem
+{
+public:
+ TriggerManagerItem(QListView * parent, QString label1,
+ QString label2 = QString::null,
+ QString label3 = QString::null,
+ QString label4 = QString::null,
+ QString label5 = QString::null,
+ QString label6 = QString::null,
+ QString label7 = QString::null,
+ QString label8 = QString::null):
+ QListViewItem(parent, label1, label2, label3, label4,
+ label5, label6, label7, label8) { ; }
+
+ virtual int compare(QListViewItem * i, int col, bool ascending) const;
+
+ void setRawDuration(timeT raw) { m_rawDuration = raw; }
+ timeT getRawDuration() const { return m_rawDuration; }
+
+ void setId(TriggerSegmentId id) { m_id = id; }
+ TriggerSegmentId getId() const { return m_id; }
+
+ void setUsage(int usage) { m_usage = usage; }
+ int getUsage() const { return m_usage; }
+
+ void setPitch(int pitch) { m_pitch = pitch; }
+ int getPitch() const { return m_pitch; }
+
+protected:
+ timeT m_rawDuration;
+ TriggerSegmentId m_id;
+ int m_usage;
+ int m_pitch;
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/TriggerSegmentManager.cpp b/src/gui/editors/segment/TriggerSegmentManager.cpp
new file mode 100644
index 0000000..3fb1732
--- /dev/null
+++ b/src/gui/editors/segment/TriggerSegmentManager.cpp
@@ -0,0 +1,576 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TriggerSegmentManager.h"
+#include "TriggerManagerItem.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include "base/BaseProperties.h"
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Clipboard.h"
+#include "base/Composition.h"
+#include "base/CompositionTimeSliceAdapter.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "base/TriggerSegment.h"
+#include "commands/segment/AddTriggerSegmentCommand.h"
+#include "commands/segment/DeleteTriggerSegmentCommand.h"
+#include "commands/segment/PasteToTriggerSegmentCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/dialogs/TimeDialog.h"
+#include "gui/general/MidiPitchLabel.h"
+#include <kaction.h>
+#include <kcommand.h>
+#include <kglobal.h>
+#include <klistview.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <kconfig.h>
+#include <qaccel.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qiconset.h>
+#include <qlistview.h>
+#include <qpushbutton.h>
+#include <qsizepolicy.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qcanvas.h>
+
+
+namespace Rosegarden
+{
+
+TriggerSegmentManager::TriggerSegmentManager(QWidget *parent,
+ RosegardenGUIDoc *doc):
+ KMainWindow(parent, "triggereditordialog"),
+ m_doc(doc),
+ m_modified(false)
+{
+ QVBox* mainFrame = new QVBox(this);
+ setCentralWidget(mainFrame);
+
+ setCaption(i18n("Manage Triggered Segments"));
+
+ m_listView = new KListView(mainFrame);
+ m_listView->addColumn("Index");
+ m_listView->addColumn(i18n("ID"));
+ m_listView->addColumn(i18n("Label"));
+ m_listView->addColumn(i18n("Duration"));
+ m_listView->addColumn(i18n("Base pitch"));
+ m_listView->addColumn(i18n("Base velocity"));
+ m_listView->addColumn(i18n("Triggers"));
+
+ // Align centrally
+ for (int i = 0; i < 2; ++i)
+ m_listView->setColumnAlignment(i, Qt::AlignHCenter);
+
+ QFrame* btnBox = new QFrame(mainFrame);
+
+ btnBox->setSizePolicy(
+ QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
+
+ QHBoxLayout* layout = new QHBoxLayout(btnBox, 4, 10);
+
+ m_addButton = new QPushButton(i18n("Add"), btnBox);
+ m_deleteButton = new QPushButton(i18n("Delete"), btnBox);
+ m_deleteAllButton = new QPushButton(i18n("Delete All"), btnBox);
+
+ m_closeButton = new QPushButton(i18n("Close"), btnBox);
+
+ QToolTip::add
+ (m_addButton,
+ i18n("Add a Triggered Segment"));
+
+ QToolTip::add
+ (m_deleteButton,
+ i18n("Delete a Triggered Segment"));
+
+ QToolTip::add
+ (m_deleteAllButton,
+ i18n("Delete All Triggered Segments"));
+
+ QToolTip::add
+ (m_closeButton,
+ i18n("Close the Triggered Segment Manager"));
+
+ layout->addStretch(10);
+ layout->addWidget(m_addButton);
+ layout->addWidget(m_deleteButton);
+ layout->addWidget(m_deleteAllButton);
+ layout->addSpacing(30);
+
+ layout->addWidget(m_closeButton);
+ layout->addSpacing(5);
+
+ connect(m_addButton, SIGNAL(released()),
+ SLOT(slotAdd()));
+
+ connect(m_deleteButton, SIGNAL(released()),
+ SLOT(slotDelete()));
+
+ connect(m_closeButton, SIGNAL(released()),
+ SLOT(slotClose()));
+
+ connect(m_deleteAllButton, SIGNAL(released()),
+ SLOT(slotDeleteAll()));
+
+ setupActions();
+
+ m_doc->getCommandHistory()->attachView(actionCollection());
+ connect(m_doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(slotUpdate()));
+
+ connect(m_listView, SIGNAL(doubleClicked(QListViewItem *)),
+ SLOT(slotEdit(QListViewItem *)));
+
+ connect(m_listView, SIGNAL(pressed(QListViewItem *)),
+ this, SLOT(slotItemClicked(QListViewItem *)));
+
+ // Highlight all columns - enable extended selection mode
+ //
+ m_listView->setAllColumnsShowFocus(true);
+ m_listView->setSelectionMode(QListView::Extended);
+ m_listView->setItemsRenameable(true);
+
+ initDialog();
+
+ setAutoSaveSettings(TriggerManagerConfigGroup, true);
+
+ m_accelerators = new QAccel(this);
+}
+
+TriggerSegmentManager::~TriggerSegmentManager()
+{
+ RG_DEBUG << "TriggerSegmentManager::~TriggerSegmentManager" << endl;
+
+ m_listView->saveLayout(kapp->config(), TriggerManagerConfigGroup);
+
+ if (m_doc)
+ m_doc->getCommandHistory()->detachView(actionCollection());
+}
+
+void
+TriggerSegmentManager::initDialog()
+{
+ RG_DEBUG << "TriggerSegmentManager::initDialog" << endl;
+ slotUpdate();
+}
+
+void
+TriggerSegmentManager::slotUpdate()
+{
+ RG_DEBUG << "TriggerSegmentManager::slotUpdate" << endl;
+
+ TriggerManagerItem *item;
+
+ m_listView->clear();
+
+ Composition &comp = m_doc->getComposition();
+
+ const Composition::triggersegmentcontainer &triggers =
+ comp.getTriggerSegments();
+
+ Composition::triggersegmentcontainerconstiterator it;
+
+ kapp->config()->setGroup(TriggerManagerConfigGroup);
+ int timeMode = kapp->config()->readNumEntry("timemode", 0);
+
+ int i = 0;
+
+ for (it = triggers.begin(); it != triggers.end(); ++it) {
+
+ // duration is as of first usage, or 0
+
+ int uses = 0;
+ timeT first = 0;
+ std::set
+ <int> tracks;
+
+ CompositionTimeSliceAdapter tsa(&m_doc->getComposition());
+ for (CompositionTimeSliceAdapter::iterator ci = tsa.begin();
+ ci != tsa.end(); ++ci) {
+ if ((*ci)->has(BaseProperties::TRIGGER_SEGMENT_ID) &&
+ (*ci)->get
+ <Int>(BaseProperties::TRIGGER_SEGMENT_ID) == (long)(*it)->getId()) {
+ ++uses;
+ if (tracks.empty()) {
+ first = (*ci)->getAbsoluteTime();
+ }
+ tracks.insert(ci.getTrack());
+ }
+ }
+
+ timeT duration =
+ (*it)->getSegment()->getEndMarkerTime() -
+ (*it)->getSegment()->getStartTime();
+
+ QString timeString = makeDurationString
+ (first, duration, timeMode);
+
+ QString label = strtoqstr((*it)->getSegment()->getLabel());
+ if (label == "")
+ label = i18n("<no label>");
+
+ QString used = i18n("%1 on 1 track",
+ "%1 on %n tracks",
+ tracks.size()).arg(uses);
+
+ QString pitch = QString("%1 (%2)")
+ .arg(MidiPitchLabel((*it)->getBasePitch()).getQString())
+ .arg((*it)->getBasePitch());
+
+ QString velocity = QString("%1").arg((*it)->getBaseVelocity());
+
+ item = new TriggerManagerItem
+ (m_listView, QString("%1").arg(i + 1), QString("%1").arg((*it)->getId()),
+ label, timeString, pitch, velocity, used);
+
+ item->setRawDuration(duration);
+ item->setId((*it)->getId());
+ item->setUsage(uses);
+ item->setPitch((*it)->getBasePitch());
+
+ m_listView->insertItem(item);
+ ++i;
+ }
+
+ if (m_listView->childCount() == 0) {
+ QListViewItem *item =
+ new TriggerManagerItem(m_listView, i18n("<none>"));
+ m_listView->insertItem(item);
+
+ m_listView->setSelectionMode(QListView::NoSelection);
+ } else {
+ m_listView->setSelectionMode(QListView::Extended);
+ }
+}
+
+void
+TriggerSegmentManager::slotDeleteAll()
+{
+ if (KMessageBox::warningContinueCancel(this, i18n("This will remove all triggered segments from the whole composition. Are you sure?")) != KMessageBox::Continue)
+ return ;
+
+ RG_DEBUG << "TriggerSegmentManager::slotDeleteAll" << endl;
+ KMacroCommand *command = new KMacroCommand(i18n("Remove all triggered segments"));
+
+ QListViewItem *it = m_listView->firstChild();
+
+ do {
+
+ TriggerManagerItem *item =
+ dynamic_cast<TriggerManagerItem*>(it);
+
+ if (!item)
+ continue;
+
+ DeleteTriggerSegmentCommand *c =
+ new DeleteTriggerSegmentCommand(m_doc,
+ item->getId());
+ command->addCommand(c);
+
+ } while ((it = it->nextSibling()));
+
+ addCommandToHistory(command);
+}
+
+void
+TriggerSegmentManager::slotAdd()
+{
+ TimeDialog dialog(this, i18n("Trigger Segment Duration"),
+ &m_doc->getComposition(),
+ 0, 3840, false);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ addCommandToHistory(new AddTriggerSegmentCommand
+ (m_doc, dialog.getTime(), 64));
+ }
+}
+
+void
+TriggerSegmentManager::slotDelete()
+{
+ RG_DEBUG << "TriggerSegmentManager::slotDelete" << endl;
+
+ TriggerManagerItem *item =
+ dynamic_cast<TriggerManagerItem*>(m_listView->currentItem());
+
+ if (!item)
+ return ;
+
+ if (item->getUsage() > 0) {
+ if (KMessageBox::warningContinueCancel(this, i18n("This triggered segment is used 1 time in the current composition. Are you sure you want to remove it?",
+ "This triggered segment is used %n times in the current composition. Are you sure you want to remove it?", item->getUsage())) != KMessageBox::Continue)
+ return ;
+ }
+
+ DeleteTriggerSegmentCommand *command =
+ new DeleteTriggerSegmentCommand(m_doc, item->getId());
+
+ addCommandToHistory(command);
+}
+
+void
+TriggerSegmentManager::slotPasteAsNew()
+{
+ Clipboard *clipboard = m_doc->getClipboard();
+
+ if (clipboard->isEmpty()) {
+ KMessageBox::information(this, i18n("Clipboard is empty"));
+ return ;
+ }
+
+ addCommandToHistory(new PasteToTriggerSegmentCommand
+ (&m_doc->getComposition(),
+ clipboard,
+ "",
+ -1));
+}
+
+void
+TriggerSegmentManager::slotClose()
+{
+ RG_DEBUG << "TriggerSegmentManager::slotClose" << endl;
+
+ if (m_doc)
+ m_doc->getCommandHistory()->detachView(actionCollection());
+ m_doc = 0;
+
+ close();
+}
+
+void
+TriggerSegmentManager::setupActions()
+{
+ KAction* close = KStdAction::close(this,
+ SLOT(slotClose()),
+ actionCollection());
+
+ m_closeButton->setText(close->text());
+ connect(m_closeButton, SIGNAL(released()), this, SLOT(slotClose()));
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+
+ // some adjustments
+ new KToolBarPopupAction(i18n("Und&o"),
+ "undo",
+ KStdAccel::key(KStdAccel::Undo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Undo));
+
+ new KToolBarPopupAction(i18n("Re&do"),
+ "redo",
+ KStdAccel::key(KStdAccel::Redo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Redo));
+
+ new KAction(i18n("Pa&ste as New Triggered Segment"), CTRL + SHIFT + Key_V, this,
+ SLOT(slotPasteAsNew()), actionCollection(),
+ "paste_to_trigger_segment");
+
+ kapp->config()->setGroup(TriggerManagerConfigGroup);
+ int timeMode = kapp->config()->readNumEntry("timemode", 0);
+
+ KRadioAction *action;
+
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/time-musical.png");
+ QIconSet icon(pixmap);
+
+ action = new KRadioAction(i18n("&Musical Times"), icon, 0, this,
+ SLOT(slotMusicalTime()),
+ actionCollection(), "time_musical");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 0)
+ action->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/time-real.png");
+ icon = QIconSet(pixmap);
+
+ action = new KRadioAction(i18n("&Real Times"), icon, 0, this,
+ SLOT(slotRealTime()),
+ actionCollection(), "time_real");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 1)
+ action->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/time-raw.png");
+ icon = QIconSet(pixmap);
+
+ action = new KRadioAction(i18n("Ra&w Times"), icon, 0, this,
+ SLOT(slotRawTime()),
+ actionCollection(), "time_raw");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 2)
+ action->setChecked(true);
+
+ createGUI("triggermanager.rc");
+}
+
+void
+TriggerSegmentManager::addCommandToHistory(KCommand *command)
+{
+ getCommandHistory()->addCommand(command);
+ setModified(false);
+}
+
+MultiViewCommandHistory*
+TriggerSegmentManager::getCommandHistory()
+{
+ return m_doc->getCommandHistory();
+}
+
+void
+TriggerSegmentManager::setModified(bool modified)
+{
+ RG_DEBUG << "TriggerSegmentManager::setModified(" << modified << ")" << endl;
+
+ m_modified = modified;
+}
+
+void
+TriggerSegmentManager::checkModified()
+{
+ RG_DEBUG << "TriggerSegmentManager::checkModified(" << m_modified << ")"
+ << endl;
+
+}
+
+void
+TriggerSegmentManager::slotEdit(QListViewItem *i)
+{
+ RG_DEBUG << "TriggerSegmentManager::slotEdit" << endl;
+
+ TriggerManagerItem *item =
+ dynamic_cast<TriggerManagerItem*>(i);
+
+ if (!item)
+ return ;
+
+ TriggerSegmentId id = item->getId();
+
+ RG_DEBUG << "id is " << id << endl;
+
+ emit editTriggerSegment(id);
+}
+
+void
+TriggerSegmentManager::closeEvent(QCloseEvent *e)
+{
+ emit closing();
+ KMainWindow::closeEvent(e);
+}
+
+void
+TriggerSegmentManager::setDocument(RosegardenGUIDoc *doc)
+{
+ // reset our pointers
+ m_doc = doc;
+ m_modified = false;
+
+ slotUpdate();
+}
+
+void
+TriggerSegmentManager::slotItemClicked(QListViewItem *item)
+{
+ RG_DEBUG << "TriggerSegmentManager::slotItemClicked" << endl;
+}
+
+QString
+TriggerSegmentManager::makeDurationString(timeT time,
+ timeT duration, int timeMode)
+{
+ //!!! duplication with EventView::makeDurationString -- merge somewhere?
+
+ switch (timeMode) {
+
+ case 0: // musical time
+ {
+ int bar, beat, fraction, remainder;
+ m_doc->getComposition().getMusicalTimeForDuration
+ (time, duration, bar, beat, fraction, remainder);
+ return QString("%1%2%3-%4%5-%6%7-%8%9 ")
+ .arg(bar / 100)
+ .arg((bar % 100) / 10)
+ .arg(bar % 10)
+ .arg(beat / 10)
+ .arg(beat % 10)
+ .arg(fraction / 10)
+ .arg(fraction % 10)
+ .arg(remainder / 10)
+ .arg(remainder % 10);
+ }
+
+ case 1: // real time
+ {
+ RealTime rt =
+ m_doc->getComposition().getRealTimeDifference
+ (time, time + duration);
+ // return QString("%1 ").arg(rt.toString().c_str());
+ return QString("%1 ").arg(rt.toText().c_str());
+ }
+
+ default:
+ return QString("%1 ").arg(duration);
+ }
+}
+
+void
+TriggerSegmentManager::slotMusicalTime()
+{
+ kapp->config()->setGroup(TriggerManagerConfigGroup);
+ kapp->config()->writeEntry("timemode", 0);
+ slotUpdate();
+}
+
+void
+TriggerSegmentManager::slotRealTime()
+{
+ kapp->config()->setGroup(TriggerManagerConfigGroup);
+ kapp->config()->writeEntry("timemode", 1);
+ slotUpdate();
+}
+
+void
+TriggerSegmentManager::slotRawTime()
+{
+ kapp->config()->setGroup(TriggerManagerConfigGroup);
+ kapp->config()->writeEntry("timemode", 2);
+ slotUpdate();
+}
+
+}
+#include "TriggerSegmentManager.moc"
diff --git a/src/gui/editors/segment/TriggerSegmentManager.h b/src/gui/editors/segment/TriggerSegmentManager.h
new file mode 100644
index 0000000..2de6488
--- /dev/null
+++ b/src/gui/editors/segment/TriggerSegmentManager.h
@@ -0,0 +1,116 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TRIGGERSEGMENTMANAGER_H_
+#define _RG_TRIGGERSEGMENTMANAGER_H_
+
+#include <kmainwindow.h>
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QPushButton;
+class QListViewItem;
+class QCloseEvent;
+class QAccel;
+class KListView;
+class KCommand;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class MultiViewCommandHistory;
+
+
+class TriggerSegmentManager : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ TriggerSegmentManager(QWidget *parent,
+ RosegardenGUIDoc *doc);
+ ~TriggerSegmentManager();
+
+ void initDialog();
+
+ void addCommandToHistory(KCommand *command);
+ MultiViewCommandHistory* getCommandHistory();
+
+ void setModified(bool value);
+ void checkModified();
+
+ // reset the document
+ void setDocument(RosegardenGUIDoc *doc);
+
+ QAccel* getAccelerators() { return m_accelerators; }
+
+public slots:
+ void slotUpdate();
+
+ void slotAdd();
+ void slotDelete();
+ void slotDeleteAll();
+ void slotClose();
+ void slotEdit(QListViewItem *);
+ void slotItemClicked(QListViewItem *);
+ void slotPasteAsNew();
+
+ void slotMusicalTime();
+ void slotRealTime();
+ void slotRawTime();
+
+signals:
+ void editTriggerSegment(int);
+ void closing();
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+
+ void setupActions();
+ QString makeDurationString(timeT startTime,
+ timeT duration, int timeMode);
+
+ //--------------- Data members ---------------------------------
+ RosegardenGUIDoc *m_doc;
+
+ QPushButton *m_closeButton;
+ QPushButton *m_addButton;
+ QPushButton *m_deleteButton;
+ QPushButton *m_deleteAllButton;
+
+ KListView *m_listView;
+
+ bool m_modified;
+
+ QAccel *m_accelerators;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.cpp b/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.cpp
new file mode 100644
index 0000000..1b982dc
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.cpp
@@ -0,0 +1,316 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "AudioPreviewPainter.h"
+
+#include "CompositionModelImpl.h"
+#include "CompositionColourCache.h"
+#include "base/Composition.h"
+#include "base/Track.h"
+#include "base/AudioLevel.h"
+#include "base/Studio.h"
+
+#include "misc/Debug.h"
+#include "document/ConfigGroups.h"
+
+#include <qimage.h>
+#include <qapplication.h>
+
+#include <kapp.h>
+#include <kconfig.h>
+
+namespace Rosegarden {
+
+AudioPreviewPainter::AudioPreviewPainter(CompositionModelImpl& model,
+ CompositionModel::AudioPreviewData* apData,
+ const Composition &composition,
+ const Segment* segment)
+ : m_model(model),
+ m_apData(apData),
+ m_composition(composition),
+ m_segment(segment),
+ m_rect(model.computeSegmentRect(*(segment))),
+ m_image(1, 1, 8, 4),
+ m_defaultCol(CompositionColourCache::getInstance()->SegmentAudioPreview),
+ m_height(model.grid().getYSnap()/2)
+{
+ int pixWidth = std::min(m_rect.getBaseWidth(), tileWidth());
+
+ m_image = QImage(pixWidth, m_rect.height(), 8, 4);
+ m_image.setAlphaBuffer(true);
+
+ m_penWidth = (std::max(1U, m_rect.getPen().width()) * 2);
+ m_halfRectHeight = m_model.grid().getYSnap()/2 - m_penWidth / 2 - 2;
+}
+
+int AudioPreviewPainter::tileWidth()
+{
+ static int tw = -1;
+ if (tw == -1) tw = QApplication::desktop()->width();
+ return tw;
+}
+
+void AudioPreviewPainter::paintPreviewImage()
+{
+ const std::vector<float>& values = m_apData->getValues();
+
+ if (values.size() == 0)
+ return;
+
+ float gain[2] = { 1.0, 1.0 };
+ int instrumentChannels = 2;
+ TrackId trackId = m_segment->getTrack();
+ Track *track = m_model.getComposition().getTrackById(trackId);
+ if (track) {
+ Instrument *instrument = m_model.getStudio().getInstrumentById(track->getInstrument());
+ if (instrument) {
+ float level = AudioLevel::dB_to_multiplier(instrument->getLevel());
+ float pan = instrument->getPan() - 100.0;
+ gain[0] = level * ((pan > 0.0) ? (1.0 - (pan / 100.0)) : 1.0);
+ gain[1] = level * ((pan < 0.0) ? ((pan + 100.0) / 100.0) : 1.0);
+ instrumentChannels = instrument->getAudioChannels();
+ }
+ }
+
+ bool showMinima = m_apData->showsMinima();
+ unsigned int channels = m_apData->getChannels();
+ if (channels == 0) {
+ RG_DEBUG << "AudioPreviewPainter::paintPreviewImage : problem with audio file for segment "
+ << m_segment->getLabel().c_str() << endl;
+ return;
+ }
+
+ int samplePoints = values.size() / (channels * (showMinima ? 2 : 1));
+ float h1, h2, l1 = 0, l2 = 0;
+ double sampleScaleFactor = samplePoints / double(m_rect.getBaseWidth());
+ m_sliceNb = 0;
+
+ m_image.fill(0);
+
+ int centre = m_image.height() / 2;
+
+ RG_DEBUG << "AudioPreviewPainter::paintPreviewImage width = " << m_rect.getBaseWidth() << ", height = " << m_rect.height() << ", halfRectHeight = " << m_halfRectHeight << endl;
+
+ RG_DEBUG << "AudioPreviewPainter::paintPreviewImage: channels = " << channels << ", gain left = " << gain[0] << ", right = " << gain[1] << endl;
+
+ double audioDuration = double(m_segment->getAudioEndTime().sec) +
+ double(m_segment->getAudioEndTime().nsec) / 1000000000.0;
+
+ // We need to take each pixel value and map it onto a point within
+ // the preview. We have samplePoints preview points in a known
+ // duration of audioDuration. Thus each point spans a real time
+ // of audioDuration / samplePoints. We need to convert the
+ // accumulated real time back into musical time, and map this
+ // proportionately across the segment width.
+
+ RealTime startRT =
+ m_model.getComposition().getElapsedRealTime(m_segment->getStartTime());
+ double startTime = double(startRT.sec) + double(startRT.nsec) / 1000000000.0;
+
+ RealTime endRT =
+ m_model.getComposition().getElapsedRealTime(m_segment->getEndMarkerTime());
+ double endTime = double(endRT.sec) + double(endRT.nsec) / 1000000000.0;
+
+ bool haveTempoChange = false;
+
+ int finalTempoChangeNumber =
+ m_model.getComposition().getTempoChangeNumberAt
+ (m_segment->getEndMarkerTime());
+
+ if ((finalTempoChangeNumber >= 0) &&
+
+ (finalTempoChangeNumber >
+ m_model.getComposition().getTempoChangeNumberAt
+ (m_segment->getStartTime()))) {
+
+ haveTempoChange = true;
+ }
+
+ KConfig* config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+
+ bool meterLevels = (config->readUnsignedNumEntry("audiopreviewstyle", 1)
+ == 1);
+
+ for (int i = 0; i < m_rect.getBaseWidth(); ++i) {
+
+ // i is the x coordinate within the rectangle. We need to
+ // calculate the position within the audio preview from which
+ // to draw the peak for this coordinate. It's possible there
+ // may be more than one, in which case we need to find the
+ // peak of all of them.
+
+ int position = 0;
+
+ if (haveTempoChange) {
+
+ // First find the time corresponding to this i.
+ timeT musicalTime =
+ m_model.grid().getRulerScale()->getTimeForX(m_rect.x() + i);
+ RealTime realTime =
+ m_model.getComposition().getElapsedRealTime(musicalTime);
+
+ double time = double(realTime.sec) +
+ double(realTime.nsec) / 1000000000.0;
+ double offset = time - startTime;
+
+ if (endTime > startTime) {
+ position = offset * m_rect.getBaseWidth() / (endTime - startTime);
+ position = int(channels * position);
+ }
+
+ } else {
+
+ position = int(channels * i * sampleScaleFactor);
+ }
+
+ if (position < 0) continue;
+
+ if (position >= values.size() - channels) {
+ finalizeCurrentSlice();
+ break;
+ }
+
+ if (channels == 1) {
+
+ h1 = values[position++];
+ h2 = h1;
+
+ if (showMinima) {
+ l1 = values[position++];
+ l2 = l1;
+ }
+ } else {
+
+ h1 = values[position++];
+ if (showMinima) l1 = values[position++];
+
+ h2 = values[position++];
+ if (showMinima) l2 = values[position++];
+
+ }
+
+ if (instrumentChannels == 1 && channels == 2) {
+ h1 = h2 = (h1 + h2) / 2;
+ l1 = l2 = (l1 + l2) / 2;
+ }
+
+ h1 *= gain[0];
+ h2 *= gain[1];
+
+ l1 *= gain[0];
+ l2 *= gain[1];
+
+ int width = 1;
+ int pixel;
+
+ // h1 left, h2 right
+ if (h1 >= 1.0) { h1 = 1.0; pixel = 2; }
+ else { pixel = 1; }
+
+ int h;
+
+ if (meterLevels) {
+ h = AudioLevel::multiplier_to_preview(h1, m_height);
+ } else {
+ h = h1 * m_height;
+ }
+ if (h <= 0) h = 1;
+ if (h > m_halfRectHeight) h = m_halfRectHeight;
+
+ int rectX = i % tileWidth();
+
+ for (int py = 0; py < h; ++py) {
+ m_image.setPixel(rectX, centre - py, pixel);
+ }
+
+ if (h2 >= 1.0) { h2 = 1.0; pixel = 2; }
+ else { pixel = 1; }
+
+ if (meterLevels) {
+ h = AudioLevel::multiplier_to_preview(h2, m_height);
+ } else {
+ h = h2 * m_height;
+ }
+ if (h < 0) h = 0;
+
+ for (int py = 0; py < h; ++py) {
+ m_image.setPixel(rectX, centre + py, pixel);
+ }
+
+ if (((i+1) % tileWidth()) == 0 || i == (m_rect.getBaseWidth() - 1)) {
+ finalizeCurrentSlice();
+ }
+ }
+
+/* Auto-fade not yet implemented.
+
+ if (m_segment->isAutoFading()) {
+
+ Composition &comp = m_model.getComposition();
+
+ int audioFadeInEnd = int(
+ m_model.grid().getRulerScale()->getXForTime(comp.
+ getElapsedTimeForRealTime(m_segment->getFadeInTime()) +
+ m_segment->getStartTime()) -
+ m_model.grid().getRulerScale()->getXForTime(m_segment->getStartTime()));
+
+ m_p.setPen(Qt::blue);
+ m_p.drawRect(0, m_apData->getSegmentRect().height() - 1, audioFadeInEnd, 1);
+ m_pb.drawRect(0, m_apData->getSegmentRect().height() - 1, audioFadeInEnd, 1);
+ }
+
+ m_p.end();
+ m_pb.end();
+*/
+}
+
+void AudioPreviewPainter::finalizeCurrentSlice()
+{
+// RG_DEBUG << "AudioPreviewPainter::finalizeCurrentSlice : copying pixmap to image at " << m_sliceNb * tileWidth() << endl;
+
+ // transparent background
+ m_image.setColor(0, qRgba(255, 255, 255, 0));
+
+ // foreground from computeSegmentPreviewColor
+ QColor c = m_model.computeSegmentPreviewColor(m_segment);
+ QRgb rgba = qRgba(c.red(), c.green(), c.blue(), 255);
+ m_image.setColor(1, rgba);
+
+ // red for clipping
+ m_image.setColor(2, qRgba(255, 0, 0, 255));
+
+ m_previewPixmaps.push_back(m_image.copy());
+
+ m_image.fill(0);
+
+ ++m_sliceNb;
+}
+
+PixmapArray AudioPreviewPainter::getPreviewImage()
+{
+ return m_previewPixmaps;
+}
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.h b/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.h
new file mode 100644
index 0000000..b3c1cac
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewPainter.h
@@ -0,0 +1,79 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_AUDIOPREVIEWPAINTER_H_
+#define _RG_AUDIOPREVIEWPAINTER_H_
+
+#include "CompositionModel.h"
+
+#include <qpainter.h>
+#include <qcolor.h>
+
+namespace Rosegarden {
+
+class CompositionModelImpl;
+class Composition;
+class Segment;
+class CompositionRect;
+
+class AudioPreviewPainter {
+public:
+ AudioPreviewPainter(CompositionModelImpl& model,
+ CompositionModel::AudioPreviewData* apData,
+ const Composition &composition,
+ const Segment* segment);
+
+ void paintPreviewImage();
+ PixmapArray getPreviewImage();
+ const CompositionRect& getSegmentRect() { return m_rect; }
+
+ static int tileWidth();
+
+protected:
+ void finalizeCurrentSlice();
+
+ //--------------- Data members ---------------------------------
+ CompositionModelImpl& m_model;
+ CompositionModel::AudioPreviewData* m_apData;
+ const Composition &m_composition;
+ const Segment* m_segment;
+ CompositionRect m_rect;
+
+ QImage m_image;
+ PixmapArray m_previewPixmaps;
+
+ QPainter m_p;
+ QPainter m_pb;
+ QColor m_defaultCol;
+ int m_penWidth;
+ int m_height;
+ int m_halfRectHeight;
+ int m_sliceNb;
+
+};
+
+}
+
+#endif
+
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.cpp b/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.cpp
new file mode 100644
index 0000000..ae64134
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.cpp
@@ -0,0 +1,267 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "AudioPreviewThread.h"
+
+#include "base/RealTime.h"
+#include "sound/AudioFileManager.h"
+#include "sound/PeakFileManager.h"
+#include <qapplication.h>
+#include <qevent.h>
+#include <qmutex.h>
+#include <qobject.h>
+#include <qthread.h>
+
+
+namespace Rosegarden
+{
+
+AudioPreviewThread::AudioPreviewThread(AudioFileManager *manager) :
+ m_manager(manager),
+ m_nextToken(0),
+ m_exiting(false),
+ m_emptyQueueListener(0)
+{}
+
+void
+AudioPreviewThread::run()
+{
+ bool emptyQueueSignalled = false;
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+
+ std::cerr << "AudioPreviewThread::run entering\n";
+#endif
+
+ while (!m_exiting) {
+
+ if (m_queue.empty()) {
+ if (m_emptyQueueListener && !emptyQueueSignalled) {
+ QApplication::postEvent(m_emptyQueueListener,
+ new QCustomEvent(AudioPreviewQueueEmpty, 0));
+ emptyQueueSignalled = true;
+ }
+
+ usleep(300000);
+ } else {
+ process();
+ }
+ }
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::run exiting\n";
+#endif
+}
+
+void
+AudioPreviewThread::finish()
+{
+ m_exiting = true;
+}
+
+bool
+AudioPreviewThread::process()
+{
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process()\n";
+#endif
+
+ if (!m_queue.empty()) {
+
+ int failed = 0;
+ int inQueue = 0;
+ //int count = 0;
+
+ m_mutex.lock();
+
+ // process 1st request and leave
+ inQueue = m_queue.size();
+ RequestQueue::iterator i = m_queue.begin();
+
+ // i->first is width, which we use only to provide an ordering to
+ // ensure we do smaller previews first. We don't use it here.
+
+ RequestRec &rec = i->second;
+ int token = rec.first;
+ Request req = rec.second;
+ m_mutex.unlock();
+
+ std::vector<float> results;
+
+ try {
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process() file id " << req.audioFileId << std::endl;
+#endif
+
+ // Requires thread-safe AudioFileManager::getPreview
+ results = m_manager->getPreview(req.audioFileId,
+ req.audioStartTime,
+ req.audioEndTime,
+ req.width,
+ req.showMinima);
+ } catch (AudioFileManager::BadAudioPathException e) {
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process: failed to update preview for audio file " << req.audioFileId << ": bad audio path: " << e.getMessage() << std::endl;
+#endif
+
+ // OK, we hope this just means we're still recording -- so
+ // leave this one in the queue
+ ++failed;
+
+ } catch (PeakFileManager::BadPeakFileException e) {
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process: failed to update preview for audio file " << req.audioFileId << ": bad peak file: " << e.getMessage() << std::endl;
+#endif
+
+ // As above
+ ++failed;
+ }
+
+ m_mutex.lock();
+
+ // We need to check that the token is still in the queue
+ // (i.e. hasn't been cancelled). Otherwise we shouldn't notify
+
+ bool found = false;
+ for (RequestQueue::iterator i = m_queue.begin(); i != m_queue.end(); ++i) {
+ if (i->second.first == token) {
+ found = true;
+ m_queue.erase(i);
+ break;
+ }
+ }
+
+ if (found) {
+ unsigned int channels =
+ m_manager->getAudioFile(req.audioFileId)->getChannels();
+ m_results[token] = ResultsPair(channels, results);
+ QObject *notify = req.notify;
+ QApplication::postEvent
+ (notify,
+ new QCustomEvent(AudioPreviewReady, (void *)token));
+ }
+
+ m_mutex.unlock();
+
+ if (failed > 0 && failed == inQueue) {
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process() - return true\n";
+#endif
+
+ return true; // delay and try again
+ }
+ }
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+ std::cerr << "AudioPreviewThread::process() - return false\n";
+#endif
+
+ return false;
+}
+
+int
+AudioPreviewThread::requestPreview(const Request &request)
+{
+ m_mutex.lock();
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+
+ std::cerr << "AudioPreviewThread::requestPreview for file id " << request.audioFileId << ", start " << request.audioStartTime << ", end " << request.audioEndTime << ", width " << request.width << ", notify " << request.notify << std::endl;
+#endif
+ /*!!!
+ for (RequestQueue::iterator i = m_queue.begin(); i != m_queue.end(); ++i) {
+ if (i->second.second.notify == request.notify) {
+ m_queue.erase(i);
+ break;
+ }
+ }
+ */
+ int token = m_nextToken;
+ m_queue.insert(RequestQueue::value_type(request.width,
+ RequestRec(token, request)));
+ ++m_nextToken;
+ m_mutex.unlock();
+
+ // if (!running()) start();
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+
+ std::cerr << "AudioPreviewThread::requestPreview : thread running : " << running()
+ << " - thread finished : " << finished() << std::endl;
+
+ std::cerr << "AudioPreviewThread::requestPreview - token = " << token << std::endl;
+#endif
+
+ return token;
+}
+
+void
+AudioPreviewThread::cancelPreview(int token)
+{
+ m_mutex.lock();
+
+#ifdef DEBUG_AUDIO_PREVIEW_THREAD
+
+ std::cerr << "AudioPreviewThread::cancelPreview for token " << token << std::endl;
+#endif
+
+ for (RequestQueue::iterator i = m_queue.begin(); i != m_queue.end(); ++i) {
+ if (i->second.first == token) {
+ m_queue.erase(i);
+ break;
+ }
+ }
+
+ m_mutex.unlock();
+}
+
+void
+AudioPreviewThread::getPreview(int token, unsigned int &channels,
+ std::vector<float> &values)
+{
+ m_mutex.lock();
+
+ values.clear();
+ if (m_results.find(token) == m_results.end()) {
+ channels = 0;
+ m_mutex.unlock();
+ return ;
+ }
+
+ channels = m_results[token].first;
+ values = m_results[token].second;
+ m_results.erase(m_results.find(token));
+
+ m_mutex.unlock();
+
+ return ;
+}
+
+const QEvent::Type AudioPreviewThread::AudioPreviewReady = QEvent::Type(QEvent::User + 1);
+const QEvent::Type AudioPreviewThread::AudioPreviewQueueEmpty = QEvent::Type(QEvent::User + 2);
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.h b/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.h
new file mode 100644
index 0000000..ae3ac81
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewThread.h
@@ -0,0 +1,99 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_AUDIOPREVIEWTHREAD_H_
+#define _RG_AUDIOPREVIEWTHREAD_H_
+
+#include "base/RealTime.h"
+#include <map>
+#include <qevent.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <utility>
+#include <vector>
+
+
+class QObject;
+
+
+namespace Rosegarden
+{
+
+class AudioFileManager;
+
+
+class AudioPreviewThread : public QThread
+{
+public:
+ AudioPreviewThread(AudioFileManager *manager);
+
+ virtual void run();
+ virtual void finish();
+
+ struct Request {
+ int audioFileId;
+ RealTime audioStartTime;
+ RealTime audioEndTime;
+ int width;
+ bool showMinima;
+ QObject *notify;
+ };
+
+ virtual int requestPreview(const Request &request);
+ virtual void cancelPreview(int token);
+ virtual void getPreview(int token, unsigned int &channels,
+ std::vector<float> &values);
+
+ void setEmptyQueueListener(QObject* o) { m_emptyQueueListener = o; }
+
+ static const QEvent::Type AudioPreviewReady;
+ static const QEvent::Type AudioPreviewQueueEmpty;
+
+
+protected:
+ virtual bool process();
+
+
+ AudioFileManager *m_manager;
+ int m_nextToken;
+ bool m_exiting;
+
+ QObject* m_emptyQueueListener;
+
+ typedef std::pair<int, Request> RequestRec;
+ typedef std::multimap<int, RequestRec> RequestQueue;
+ RequestQueue m_queue;
+
+ typedef std::pair<unsigned int, std::vector<float> > ResultsPair;
+ typedef std::map<int, ResultsPair> ResultsQueue;
+ ResultsQueue m_results;
+
+ QMutex m_mutex;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.cpp b/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.cpp
new file mode 100644
index 0000000..76497b9
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.cpp
@@ -0,0 +1,149 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "AudioPreviewUpdater.h"
+
+#include "misc/Debug.h"
+#include "AudioPreviewThread.h"
+#include "base/Composition.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "CompositionModelImpl.h"
+#include <qevent.h>
+#include <qobject.h>
+#include <qrect.h>
+
+
+namespace Rosegarden
+{
+
+static int apuExtantCount = 0;
+
+AudioPreviewUpdater::AudioPreviewUpdater(AudioPreviewThread &thread,
+ const Composition& c, const Segment* s,
+ const QRect& r,
+ CompositionModelImpl* parent)
+ : QObject(parent),
+ m_thread(thread),
+ m_composition(c),
+ m_segment(s),
+ m_rect(r),
+ m_showMinima(false),
+ m_channels(0),
+ m_previewToken( -1)
+{
+ ++apuExtantCount;
+ RG_DEBUG << "AudioPreviewUpdater::AudioPreviewUpdater " << this << " (now " << apuExtantCount << " extant)" << endl;
+}
+
+AudioPreviewUpdater::~AudioPreviewUpdater()
+{
+ --apuExtantCount;
+ RG_DEBUG << "AudioPreviewUpdater::~AudioPreviewUpdater on " << this << " ( token " << m_previewToken << ") (now " << apuExtantCount << " extant)" << endl;
+ if (m_previewToken >= 0)
+ m_thread.cancelPreview(m_previewToken);
+}
+
+void AudioPreviewUpdater::update()
+{
+ // Get sample start and end times and work out duration
+ //
+ RealTime audioStartTime = m_segment->getAudioStartTime();
+ RealTime audioEndTime = audioStartTime +
+ m_composition.getElapsedRealTime(m_segment->getEndMarkerTime()) -
+ m_composition.getElapsedRealTime(m_segment->getStartTime()) ;
+
+ RG_DEBUG << "AudioPreviewUpdater(" << this << ")::update() - for file id "
+ << m_segment->getAudioFileId() << " requesting values - thread running : "
+ << m_thread.running() << " - thread finished : " << m_thread.finished() << endl;
+
+ AudioPreviewThread::Request request;
+ request.audioFileId = m_segment->getAudioFileId();
+ request.audioStartTime = audioStartTime;
+ request.audioEndTime = audioEndTime;
+ request.width = m_rect.width();
+ request.showMinima = m_showMinima;
+ request.notify = this;
+ if (m_previewToken >= 0)
+ m_thread.cancelPreview(m_previewToken);
+ m_previewToken = m_thread.requestPreview(request);
+ if (!m_thread.running())
+ m_thread.start();
+}
+
+void AudioPreviewUpdater::cancel()
+{
+ if (m_previewToken >= 0)
+ m_thread.cancelPreview(m_previewToken);
+ m_previewToken = -1;
+}
+
+bool AudioPreviewUpdater::event(QEvent *e)
+{
+ RG_DEBUG << "AudioPreviewUpdater(" << this << ")::event (" << e << ")" << endl;
+
+ if (e->type() == AudioPreviewThread::AudioPreviewReady) {
+ QCustomEvent *ev = dynamic_cast<QCustomEvent *>(e);
+ if (ev) {
+ intptr_t token = (intptr_t)ev->data();
+ m_channels = 0; // to be filled as getPreview return value
+
+ RG_DEBUG << "AudioPreviewUpdater::token " << token << ", my token " << m_previewToken << endl;
+
+ if (m_previewToken >= 0 && token >= m_previewToken) {
+
+ m_previewToken = -1;
+ m_thread.getPreview(token, m_channels, m_values);
+
+ if (m_channels == 0) {
+ RG_DEBUG << "AudioPreviewUpdater: failed to find preview!\n";
+ } else {
+
+ RG_DEBUG << "AudioPreviewUpdater: got correct preview (" << m_channels
+ << " channels, " << m_values.size() << " samples)\n";
+ }
+
+ emit audioPreviewComplete(this);
+
+ } else {
+
+ // this one is out of date already
+ std::vector<float> tmp;
+ unsigned int tmpChannels;
+ m_thread.getPreview(token, tmpChannels, tmp);
+
+ RG_DEBUG << "AudioPreviewUpdater: got obsolete preview (" << tmpChannels
+ << " channels, " << tmp.size() << " samples)\n";
+ }
+
+ return true;
+ }
+ }
+
+ return QObject::event(e);
+}
+
+}
+#include "AudioPreviewUpdater.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.h b/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.h
new file mode 100644
index 0000000..ffc97c9
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/AudioPreviewUpdater.h
@@ -0,0 +1,90 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_AUDIOPREVIEWUPDATER_H_
+#define _RG_AUDIOPREVIEWUPDATER_H_
+
+#include <qobject.h>
+#include <qrect.h>
+#include <vector>
+
+
+class QEvent;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class CompositionModelImpl;
+class Composition;
+class AudioPreviewThread;
+
+
+class AudioPreviewUpdater : public QObject
+{
+ Q_OBJECT
+
+public:
+ AudioPreviewUpdater(AudioPreviewThread &thread,
+ const Composition &composition,
+ const Segment *segment,
+ const QRect &displayExtent,
+ CompositionModelImpl *parent);
+ ~AudioPreviewUpdater();
+
+ void update();
+ void cancel();
+
+ QRect getDisplayExtent() const { return m_rect; }
+ void setDisplayExtent(const QRect &rect) { m_rect = rect; }
+
+ const Segment *getSegment() const { return m_segment; }
+
+ const std::vector<float> &getComputedValues(unsigned int &channels) const
+ { channels = m_channels; return m_values; }
+
+signals:
+ void audioPreviewComplete(AudioPreviewUpdater*);
+
+protected:
+ virtual bool event(QEvent*);
+
+ AudioPreviewThread& m_thread;
+
+ const Composition& m_composition;
+ const Segment* m_segment;
+ QRect m_rect;
+ bool m_showMinima;
+ unsigned int m_channels;
+ std::vector<float> m_values;
+
+ intptr_t m_previewToken;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionColourCache.cpp b/src/gui/editors/segment/segmentcanvas/CompositionColourCache.cpp
new file mode 100644
index 0000000..b36d6e0
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionColourCache.cpp
@@ -0,0 +1,62 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "CompositionColourCache.h"
+
+#include "gui/general/GUIPalette.h"
+#include <qcolor.h>
+
+
+namespace Rosegarden
+{
+
+void CompositionColourCache::init()
+{
+ SegmentCanvas = GUIPalette::getColour(GUIPalette::SegmentCanvas);
+ SegmentAudioPreview = GUIPalette::getColour(GUIPalette::SegmentAudioPreview);
+ SegmentInternalPreview = GUIPalette::getColour(GUIPalette::SegmentInternalPreview);
+ SegmentLabel = GUIPalette::getColour(GUIPalette::SegmentLabel);
+ SegmentBorder = GUIPalette::getColour(GUIPalette::SegmentBorder);
+ RepeatSegmentBorder = GUIPalette::getColour(GUIPalette::RepeatSegmentBorder);
+ RecordingSegmentBorder = GUIPalette::getColour(GUIPalette::RecordingSegmentBorder);
+ RecordingAudioSegmentBlock = GUIPalette::getColour(GUIPalette::RecordingAudioSegmentBlock);
+ RecordingInternalSegmentBlock = GUIPalette::getColour(GUIPalette::RecordingInternalSegmentBlock);
+ RotaryFloatBackground = GUIPalette::getColour(GUIPalette::RotaryFloatBackground);
+ RotaryFloatForeground = GUIPalette::getColour(GUIPalette::RotaryFloatForeground);
+
+}
+
+CompositionColourCache* CompositionColourCache::getInstance()
+{
+ if (!m_instance) {
+ m_instance = new CompositionColourCache();
+ }
+
+ return m_instance;
+}
+
+CompositionColourCache* CompositionColourCache::m_instance = 0;
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionColourCache.h b/src/gui/editors/segment/segmentcanvas/CompositionColourCache.h
new file mode 100644
index 0000000..32d4719
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionColourCache.h
@@ -0,0 +1,69 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONCOLOURCACHE_H_
+#define _RG_COMPOSITIONCOLOURCACHE_H_
+
+#include <qcolor.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class CompositionColourCache
+{
+public:
+ static CompositionColourCache* getInstance();
+
+ void init();
+
+ QColor SegmentCanvas;
+ QColor SegmentAudioPreview;
+ QColor SegmentInternalPreview;
+ QColor SegmentLabel;
+ QColor SegmentBorder;
+ QColor RepeatSegmentBorder;
+ QColor RecordingSegmentBorder;
+ QColor RecordingAudioSegmentBlock;
+ QColor RecordingInternalSegmentBlock;
+ QColor Pointer;
+ QColor MovementGuide;
+ QColor RotaryFloatBackground;
+ QColor RotaryFloatForeground;
+
+protected:
+ CompositionColourCache() { init(); }
+ static CompositionColourCache* m_instance;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItem.cpp b/src/gui/editors/segment/segmentcanvas/CompositionItem.cpp
new file mode 100644
index 0000000..798178a
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItem.cpp
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "CompositionItem.h"
+
+#include <qobject.h>
+#include <qrect.h>
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItem.h b/src/gui/editors/segment/segmentcanvas/CompositionItem.h
new file mode 100644
index 0000000..b5e749b
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItem.h
@@ -0,0 +1,67 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONITEM_H_
+#define _RG_COMPOSITIONITEM_H_
+
+#include <qguardedptr.h>
+#include <qobject.h>
+#include <qrect.h>
+
+
+namespace Rosegarden
+{
+
+class _CompositionItem : public QObject {
+public:
+ virtual bool isRepeating() const = 0;
+ virtual QRect rect() const = 0;
+ virtual void moveBy(int x, int y) = 0;
+ virtual void moveTo(int x, int y) = 0;
+ virtual void setX(int x) = 0;
+ virtual void setY(int y) = 0;
+ virtual void setZ(unsigned int z) = 0;
+ virtual int x() = 0;
+ virtual int y() = 0;
+ virtual unsigned int z() = 0;
+ virtual void setWidth(int w) = 0;
+
+ // used by itemcontainer
+ virtual long hashKey() = 0;
+
+ QRect savedRect() const { return m_savedRect; }
+ void saveRect() const { m_savedRect = rect(); }
+
+protected:
+ mutable QRect m_savedRect;
+};
+
+typedef QGuardedPtr<_CompositionItem> CompositionItem;
+bool operator<(const CompositionItem&, const CompositionItem&);
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.cpp b/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.cpp
new file mode 100644
index 0000000..e1705cd
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.cpp
@@ -0,0 +1,150 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include <cmath>
+
+#include "CompositionItemHelper.h"
+
+#include "base/Segment.h"
+#include "base/SnapGrid.h"
+#include "misc/Debug.h"
+#include "CompositionModel.h"
+#include "CompositionItemImpl.h"
+#include <qcolor.h>
+#include <qpoint.h>
+#include <qrect.h>
+
+namespace Rosegarden
+{
+
+timeT CompositionItemHelper::getStartTime(const CompositionItem& item, const Rosegarden::SnapGrid& grid)
+{
+ timeT t = 0;
+
+ if (item) {
+ // t = std::max(grid.snapX(item->rect().x()), 0L); - wrong, we can have negative start times,
+ // and if we do this we 'crop' segments when they are moved before the start of the composition
+ t = grid.snapX(item->rect().x());
+
+// RG_DEBUG << "CompositionItemHelper::getStartTime(): item is repeating : " << item->isRepeating()
+// << " - startTime = " << t
+// << endl;
+ }
+
+ return t;
+}
+
+timeT CompositionItemHelper::getEndTime(const CompositionItem& item, const Rosegarden::SnapGrid& grid)
+{
+ timeT t = 0;
+
+ if (item) {
+ QRect itemRect = item->rect();
+
+ t = std::max(grid.snapX(itemRect.x() + itemRect.width()), 0L);
+
+// RG_DEBUG << "CompositionItemHelper::getEndTime() : rect width = "
+// << itemRect.width()
+// << " - item is repeating : " << item->isRepeating()
+// << " - endTime = " << t
+// << endl;
+
+ }
+
+ return t;
+}
+
+void CompositionItemHelper::setStartTime(CompositionItem& item, timeT time,
+ const Rosegarden::SnapGrid& grid)
+{
+ if (item) {
+ int x = int(nearbyint(grid.getRulerScale()->getXForTime(time)));
+
+ RG_DEBUG << "CompositionItemHelper::setStartTime() time = " << time
+ << " -> x = " << x << endl;
+
+ int curX = item->rect().x();
+ item->setX(x);
+ if (item->isRepeating()) {
+ int deltaX = curX - x;
+ CompositionRect& sr = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)item)->getCompRect();
+ int curW = sr.getBaseWidth();
+ sr.setBaseWidth(curW + deltaX);
+ }
+
+ }
+
+}
+
+void CompositionItemHelper::setEndTime(CompositionItem& item, timeT time,
+ const Rosegarden::SnapGrid& grid)
+{
+ if (item) {
+ int x = int(nearbyint(grid.getRulerScale()->getXForTime(time)));
+ QRect r = item->rect();
+ QPoint topRight = r.topRight();
+ topRight.setX(x);
+ r.setTopRight(topRight);
+ item->setWidth(r.width());
+
+ if (item->isRepeating()) {
+ CompositionRect& sr = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)item)->getCompRect();
+ sr.setBaseWidth(r.width());
+ }
+ }
+}
+
+int CompositionItemHelper::getTrackPos(const CompositionItem& item, const Rosegarden::SnapGrid& grid)
+{
+ return grid.getYBin(item->rect().y());
+}
+
+Rosegarden::Segment* CompositionItemHelper::getSegment(CompositionItem item)
+{
+ return (dynamic_cast<CompositionItemImpl*>((_CompositionItem*)item))->getSegment();
+}
+
+CompositionItem CompositionItemHelper::makeCompositionItem(Rosegarden::Segment* segment)
+{
+ return CompositionItem(new CompositionItemImpl(*segment, QRect()));
+}
+
+CompositionItem CompositionItemHelper::findSiblingCompositionItem(const CompositionModel::itemcontainer& items,
+ const CompositionItem& referenceItem)
+{
+ CompositionModel::itemcontainer::const_iterator it;
+ Rosegarden::Segment* currentSegment = CompositionItemHelper::getSegment(referenceItem);
+
+ for (it = items.begin(); it != items.end(); it++) {
+ CompositionItem item = *it;
+ Rosegarden::Segment* segment = CompositionItemHelper::getSegment(item);
+ if (segment == currentSegment) {
+ return item;
+ }
+ }
+
+ return referenceItem;
+}
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.h b/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.h
new file mode 100644
index 0000000..1b3ad95
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItemHelper.h
@@ -0,0 +1,61 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONITEMHELPER_H_
+#define _RG_COMPOSITIONITEMHELPER_H_
+
+#include "CompositionModel.h"
+#include "base/Event.h"
+
+
+
+
+namespace Rosegarden
+{
+
+class SnapGrid;
+class Segment;
+
+
+class CompositionItemHelper {
+public:
+ static timeT getStartTime(const CompositionItem&, const SnapGrid&);
+ static timeT getEndTime(const CompositionItem&, const SnapGrid&);
+ static int getTrackPos(const CompositionItem&, const SnapGrid&);
+ static void setStartTime(CompositionItem&, timeT, const SnapGrid&);
+ static void setEndTime(CompositionItem&, timeT, const SnapGrid&);
+ static Segment* getSegment(CompositionItem);
+ static CompositionItem makeCompositionItem(Segment*);
+ /**
+ * return the CompositionItem in the model which references the same segment as referenceItem
+ */
+ static CompositionItem findSiblingCompositionItem(const CompositionModel::itemcontainer& items, const CompositionItem& referenceItem);
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.cpp b/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.cpp
new file mode 100644
index 0000000..5508ad2
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.cpp
@@ -0,0 +1,67 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "CompositionItemImpl.h"
+
+#include "misc/Debug.h"
+#include "base/Segment.h"
+#include "CompositionRect.h"
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+CompositionItemImpl::CompositionItemImpl(Segment& s, const CompositionRect& rect)
+ : m_segment(s),
+ m_rect(rect),
+ m_z(0)
+{}
+
+QRect CompositionItemImpl::rect() const
+{
+ QRect res = m_rect;
+ if (m_rect.isRepeating()) {
+ CompositionRect::repeatmarks repeatMarks = m_rect.getRepeatMarks();
+ int neww = m_rect.getBaseWidth();
+
+ // RG_DEBUG << "CompositionItemImpl::rect() - width = "
+ // << m_rect.width() << " - base w = " << neww << endl;
+ res.setWidth(neww);
+ } else {
+ // RG_DEBUG << "CompositionItemImpl::rect() m_rect not repeating\n";
+ }
+
+
+ return res;
+}
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.h b/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.h
new file mode 100644
index 0000000..b5b3ef7
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionItemImpl.h
@@ -0,0 +1,74 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONITEMIMPL_H_
+#define _RG_COMPOSITIONITEMIMPL_H_
+
+#include "CompositionRect.h"
+#include "CompositionItem.h"
+#include <qrect.h>
+
+
+
+
+namespace Rosegarden
+{
+
+class Segment;
+
+
+class CompositionItemImpl : public _CompositionItem {
+public:
+ CompositionItemImpl(Segment& s, const CompositionRect&);
+ virtual bool isRepeating() const { return m_rect.isRepeating(); }
+ virtual QRect rect() const;
+ virtual void moveBy(int x, int y) { m_rect.moveBy(x, y); }
+ virtual void moveTo(int x, int y) { m_rect.setRect(x, y, m_rect.width(), m_rect.height()); }
+ virtual void setX(int x) { m_rect.setX(x); }
+ virtual void setY(int y) { m_rect.setY(y); }
+ virtual void setZ(unsigned int z) { m_z = z; }
+ virtual int x() { return m_rect.x(); }
+ virtual int y() { return m_rect.y(); }
+ virtual unsigned int z() { return m_z; }
+ virtual void setWidth(int w) { m_rect.setWidth(w); }
+ // use segment address as hash key
+ virtual long hashKey() { return (long)getSegment(); }
+
+ Segment* getSegment() { return &m_segment; }
+ const Segment* getSegment() const { return &m_segment; }
+ CompositionRect& getCompRect() { return m_rect; }
+
+protected:
+
+ //--------------- Data members ---------------------------------
+ Segment& m_segment;
+ CompositionRect m_rect;
+ unsigned int m_z;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionModel.cpp b/src/gui/editors/segment/segmentcanvas/CompositionModel.cpp
new file mode 100644
index 0000000..9701c8a
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionModel.cpp
@@ -0,0 +1,43 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "CompositionModel.h"
+
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/Segment.h"
+#include "CompositionItemHelper.h"
+
+
+namespace Rosegarden
+{
+
+bool CompositionModel::CompositionItemCompare::operator()(const CompositionItem &c1, const CompositionItem &c2) const
+{
+ return CompositionItemHelper::getSegment(c1) < CompositionItemHelper::getSegment(c2);
+}
+
+}
+#include "CompositionModel.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionModel.h b/src/gui/editors/segment/segmentcanvas/CompositionModel.h
new file mode 100644
index 0000000..beafc2e
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionModel.h
@@ -0,0 +1,179 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONMODEL_H_
+#define _RG_COMPOSITIONMODEL_H_
+
+#include "base/Composition.h"
+#include "base/Segment.h"
+#include <set>
+#include <qcolor.h>
+#include <qobject.h>
+#include <qimage.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <utility>
+#include <vector>
+#include "base/Event.h"
+#include "CompositionRect.h"
+#include "CompositionItem.h"
+
+
+class RectRanges;
+class CompositionItem;
+class AudioPreviewDrawData;
+
+
+namespace Rosegarden
+{
+
+class SnapGrid;
+typedef std::vector<QImage> PixmapArray;
+
+
+class CompositionModel : public QObject, public CompositionObserver, public SegmentObserver
+{
+ Q_OBJECT
+public:
+
+ struct CompositionItemCompare {
+ bool operator()(const CompositionItem &c1, const CompositionItem &c2) const;
+ };
+
+ typedef std::vector<QRect> rectlist;
+ typedef std::vector<int> heightlist;
+ typedef std::vector<CompositionRect> rectcontainer;
+ typedef std::set<CompositionItem, CompositionItemCompare> itemcontainer;
+
+ struct AudioPreviewDrawDataItem {
+ AudioPreviewDrawDataItem(PixmapArray p, QPoint bp, QRect r) :
+ pixmap(p), basePoint(bp), rect(r), resizeOffset(0) {};
+ PixmapArray pixmap;
+ QPoint basePoint;
+ QRect rect;
+
+ // when showing a segment that is being resized from the
+ // beginning, this contains the offset between the current
+ // rect of the segment and the resized one
+ int resizeOffset;
+ };
+
+ typedef std::vector<AudioPreviewDrawDataItem> AudioPreviewDrawData;
+
+ struct RectRange {
+ std::pair<rectlist::iterator, rectlist::iterator> range;
+ QPoint basePoint;
+ QColor color;
+ };
+
+ typedef std::vector<RectRange> RectRanges;
+
+ class AudioPreviewData {
+ public:
+ AudioPreviewData(bool showMinima, unsigned int channels) : m_showMinima(showMinima), m_channels(channels) {};
+ // ~AudioPreviewData();
+
+ bool showsMinima() { return m_showMinima; }
+ void setShowMinima(bool s) { m_showMinima = s; }
+
+ unsigned int getChannels() { return m_channels; }
+ void setChannels(unsigned int c) { m_channels = c; }
+
+ const std::vector<float> &getValues() const { return m_values; }
+ void setValues(const std::vector<float>&v) { m_values = v; }
+
+ QRect getSegmentRect() { return m_segmentRect; }
+ void setSegmentRect(const QRect& r) { m_segmentRect = r; }
+
+ protected:
+ std::vector<float> m_values;
+ bool m_showMinima;
+ unsigned int m_channels;
+ QRect m_segmentRect;
+
+ private:
+ // no copy ctor
+ AudioPreviewData(const AudioPreviewData&);
+ };
+
+
+ virtual ~CompositionModel() {};
+
+ virtual unsigned int getNbRows() = 0;
+ virtual const rectcontainer& getRectanglesIn(const QRect& rect,
+ RectRanges* notationRects, AudioPreviewDrawData* audioRects) = 0;
+
+ virtual heightlist getTrackDividersIn(const QRect& rect) = 0;
+
+ virtual itemcontainer getItemsAt (const QPoint&) = 0;
+ virtual timeT getRepeatTimeAt (const QPoint&, const CompositionItem&) = 0;
+
+ virtual SnapGrid& grid() = 0;
+
+ virtual void setPointerPos(int xPos) = 0;
+ virtual void setSelected(const CompositionItem&, bool selected = true) = 0;
+ virtual bool isSelected(const CompositionItem&) const = 0;
+ virtual void setSelected(const itemcontainer&) = 0;
+ virtual void clearSelected() = 0;
+ virtual bool haveSelection() const = 0;
+ virtual bool haveMultipleSelection() const = 0;
+ virtual void signalSelection() = 0;
+ virtual void setSelectionRect(const QRect&) = 0;
+ virtual void finalizeSelectionRect() = 0;
+ virtual QRect getSelectionContentsRect() = 0;
+ virtual void signalContentChange() = 0;
+
+ virtual void addRecordingItem(const CompositionItem&) = 0;
+ virtual void removeRecordingItem(const CompositionItem&) = 0;
+ virtual void clearRecordingItems() = 0;
+ virtual bool haveRecordingItems() = 0;
+
+ enum ChangeType { ChangeMove, ChangeResizeFromStart, ChangeResizeFromEnd };
+
+ virtual void startChange(const CompositionItem&, ChangeType change) = 0;
+ virtual void startChangeSelection(ChangeType change) = 0;
+ virtual itemcontainer& getChangingItems() = 0;
+ virtual void endChange() = 0;
+ virtual ChangeType getChangeType() = 0;
+
+ virtual void setLength(int width) = 0;
+ virtual int getLength() = 0;
+
+signals:
+ void needContentUpdate();
+ void needContentUpdate(const QRect&);
+ void needArtifactsUpdate();
+
+protected:
+ CompositionItem* m_currentCompositionItem;
+};
+
+class AudioPreviewThread;
+class AudioPreviewUpdater;
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.cpp b/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.cpp
new file mode 100644
index 0000000..39deb2e
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.cpp
@@ -0,0 +1,1328 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include <cmath>
+#include <algorithm>
+#include "CompositionModelImpl.h"
+
+#include "base/BaseProperties.h"
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "AudioPreviewThread.h"
+#include "AudioPreviewUpdater.h"
+#include "base/Composition.h"
+#include "base/Event.h"
+#include "base/MidiProgram.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/SnapGrid.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "CompositionItemHelper.h"
+#include "CompositionItemImpl.h"
+#include "CompositionModel.h"
+#include "CompositionRect.h"
+#include "CompositionColourCache.h"
+#include "AudioPreviewPainter.h"
+#include "gui/general/GUIPalette.h"
+#include "SegmentOrderer.h"
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qregexp.h>
+#include <qsize.h>
+#include <qstring.h>
+
+
+
+namespace Rosegarden
+{
+
+CompositionModelImpl::CompositionModelImpl(Composition& compo,
+ Studio& studio,
+ RulerScale *rulerScale,
+ int vStep)
+ : m_composition(compo),
+ m_studio(studio),
+ m_grid(rulerScale, vStep),
+ m_pointerTimePos(0),
+ m_audioPreviewThread(0)
+{
+ m_notationPreviewDataCache.setAutoDelete(true);
+ m_audioPreviewDataCache.setAutoDelete(true);
+ m_composition.addObserver(this);
+
+ setTrackHeights();
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ (*i)->addObserver(this);
+ }
+}
+
+CompositionModelImpl::~CompositionModelImpl()
+{
+ RG_DEBUG << "CompositionModelImpl::~CompositionModelImpl()" << endl;
+
+ if (!isCompositionDeleted()) {
+
+ m_composition.removeObserver(this);
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ (*i)->removeObserver(this);
+ }
+ }
+
+ RG_DEBUG << "CompositionModelImpl::~CompositionModelImpl(): removal from Segment & Composition observers OK" << endl;
+
+ if (m_audioPreviewThread) {
+ while (!m_audioPreviewUpdaterMap.empty()) {
+ // Cause any running previews to be cancelled
+ delete m_audioPreviewUpdaterMap.begin()->second;
+ m_audioPreviewUpdaterMap.erase(m_audioPreviewUpdaterMap.begin());
+ }
+ }
+}
+
+struct RectCompare {
+ bool operator()(const QRect &r1, const QRect &r2) const {
+ return r1.x() < r2.x();
+ }
+};
+
+void CompositionModelImpl::makeNotationPreviewRects(RectRanges* npRects, QPoint basePoint,
+ const Segment* segment, const QRect& clipRect)
+{
+
+ rectlist* cachedNPData = getNotationPreviewData(segment);
+
+ if (cachedNPData->empty())
+ return ;
+
+ rectlist::iterator npEnd = cachedNPData->end();
+
+ rectlist::iterator npi = std::lower_bound(cachedNPData->begin(), npEnd, clipRect, RectCompare());
+
+ if (npi == npEnd)
+ return ;
+
+ if (npi != cachedNPData->begin())
+ --npi;
+
+ RectRange interval;
+
+ interval.range.first = npi;
+
+ int segEndX = int(nearbyint(m_grid.getRulerScale()->getXForTime(segment->getEndMarkerTime())));
+ int xLim = std::min(clipRect.topRight().x(), segEndX);
+
+ // RG_DEBUG << "CompositionModelImpl::makeNotationPreviewRects : basePoint.x : "
+ // << basePoint.x() << endl;
+
+ // move iterator forward
+ //
+ while (npi->x() < xLim && npi != npEnd)
+ ++npi;
+
+ interval.range.second = npi;
+ interval.basePoint.setX(0);
+ interval.basePoint.setY(basePoint.y());
+ interval.color = computeSegmentPreviewColor(segment);
+
+ npRects->push_back(interval);
+}
+
+void CompositionModelImpl::makeNotationPreviewRectsMovingSegment(RectRanges* npRects, QPoint basePoint,
+ const Segment* segment, const QRect& currentSR)
+{
+ CompositionRect unmovedSR = computeSegmentRect(*segment);
+
+ rectlist* cachedNPData = getNotationPreviewData(segment);
+
+ if (cachedNPData->empty())
+ return ;
+
+ rectlist::iterator npEnd = cachedNPData->end(),
+ npBegin = cachedNPData->begin();
+
+ rectlist::iterator npi;
+
+ if (getChangeType() == ChangeResizeFromStart)
+ npi = std::lower_bound(npBegin, npEnd, currentSR, RectCompare());
+ else
+ npi = std::lower_bound(npBegin, npEnd, unmovedSR, RectCompare());
+
+ if (npi == npEnd)
+ return ;
+
+ if (npi != npBegin && getChangeType() != ChangeResizeFromStart) {
+ --npi;
+ }
+
+ RectRange interval;
+
+ interval.range.first = npi;
+
+ int xLim = getChangeType() == ChangeMove ? unmovedSR.topRight().x() : currentSR.topRight().x();
+
+ // RG_DEBUG << "CompositionModelImpl::makeNotationPreviewRectsMovingSegment : basePoint.x : "
+ // << basePoint.x() << endl;
+
+ // move iterator forward
+ //
+ while (npi->x() < xLim && npi != npEnd)
+ ++npi;
+
+ interval.range.second = npi;
+ interval.basePoint.setY(basePoint.y());
+
+ if (getChangeType() == ChangeMove)
+ interval.basePoint.setX(basePoint.x() - unmovedSR.x());
+ else
+ interval.basePoint.setX(0);
+
+ interval.color = computeSegmentPreviewColor(segment);
+
+ npRects->push_back(interval);
+}
+
+void CompositionModelImpl::makeAudioPreviewRects(AudioPreviewDrawData* apRects, const Segment* segment,
+ const CompositionRect& segRect, const QRect& clipRect)
+{
+ Profiler profiler("CompositionModelImpl::makeAudioPreviewRects", true);
+ RG_DEBUG << "CompositionModelImpl::makeAudioPreviewRects - segRect = " << segRect << endl;
+
+ PixmapArray previewImage = getAudioPreviewPixmap(segment);
+
+ QPoint basePoint = segRect.topLeft();
+
+ AudioPreviewDrawDataItem previewItem(previewImage, basePoint, segRect);
+
+ if (getChangeType() == ChangeResizeFromStart) {
+ CompositionRect originalRect = computeSegmentRect(*segment);
+ previewItem.resizeOffset = segRect.x() - originalRect.x();
+ }
+
+ apRects->push_back(previewItem);
+}
+
+void CompositionModelImpl::computeRepeatMarks(CompositionItem& item)
+{
+ Segment* s = CompositionItemHelper::getSegment(item);
+ CompositionRect& sr = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)item)->getCompRect();
+ computeRepeatMarks(sr, s);
+}
+
+void CompositionModelImpl::computeRepeatMarks(CompositionRect& sr, const Segment* s)
+{
+ if (s->isRepeating()) {
+
+ timeT startTime = s->getStartTime();
+ timeT endTime = s->getEndMarkerTime();
+ timeT repeatInterval = endTime - startTime;
+
+ if (repeatInterval <= 0) {
+ // std::cerr << "WARNING: CompositionModelImpl::computeRepeatMarks: Segment at " << startTime << " has repeatInterval " << repeatInterval << std::endl;
+ // std::cerr << kdBacktrace() << std::endl;
+ return ;
+ }
+
+ timeT repeatStart = endTime;
+ timeT repeatEnd = s->getRepeatEndTime();
+ sr.setWidth(int(nearbyint(m_grid.getRulerScale()->getWidthForDuration(startTime,
+ repeatEnd - startTime))));
+
+ CompositionRect::repeatmarks repeatMarks;
+
+ // RG_DEBUG << "CompositionModelImpl::computeRepeatMarks : repeatStart = "
+ // << repeatStart << " - repeatEnd = " << repeatEnd << endl;
+
+ for (timeT repeatMark = repeatStart; repeatMark < repeatEnd; repeatMark += repeatInterval) {
+ int mark = int(nearbyint(m_grid.getRulerScale()->getXForTime(repeatMark)));
+ // RG_DEBUG << "CompositionModelImpl::computeRepeatMarks : mark at " << mark << endl;
+ repeatMarks.push_back(mark);
+ }
+ sr.setRepeatMarks(repeatMarks);
+ if (repeatMarks.size() > 0)
+ sr.setBaseWidth(repeatMarks[0] - sr.x());
+ else {
+ // RG_DEBUG << "CompositionModelImpl::computeRepeatMarks : no repeat marks\n";
+ sr.setBaseWidth(sr.width());
+ }
+
+ // RG_DEBUG << "CompositionModelImpl::computeRepeatMarks : s = "
+ // << s << " base width = " << sr.getBaseWidth()
+ // << " - nb repeat marks = " << repeatMarks.size() << endl;
+
+ }
+}
+
+void CompositionModelImpl::setAudioPreviewThread(AudioPreviewThread *thread)
+{
+ // std::cerr << "\nCompositionModelImpl::setAudioPreviewThread()\n" << std::endl;
+
+ while (!m_audioPreviewUpdaterMap.empty()) {
+ // Cause any running previews to be cancelled
+ delete m_audioPreviewUpdaterMap.begin()->second;
+ m_audioPreviewUpdaterMap.erase(m_audioPreviewUpdaterMap.begin());
+ }
+
+ m_audioPreviewThread = thread;
+}
+
+void CompositionModelImpl::clearPreviewCache()
+{
+ RG_DEBUG << "CompositionModelImpl::clearPreviewCache\n";
+
+ m_notationPreviewDataCache.clear();
+ m_audioPreviewDataCache.clear();
+ m_audioSegmentPreviewMap.clear();
+
+ for (AudioPreviewUpdaterMap::iterator i = m_audioPreviewUpdaterMap.begin();
+ i != m_audioPreviewUpdaterMap.end(); ++i) {
+ i->second->cancel();
+ }
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ if ((*i)->getType() == Segment::Audio) {
+ // This will create the audio preview updater. The
+ // preview won't be calculated and cached until the
+ // updater completes and calls back.
+ updatePreviewCacheForAudioSegment((*i), 0);
+ }
+ }
+}
+
+void CompositionModelImpl::updatePreviewCacheForNotationSegment(const Segment* segment, rectlist* npData)
+{
+ npData->clear();
+
+ int segStartX = int(nearbyint(m_grid.getRulerScale()->getXForTime(segment->getStartTime())));
+
+ bool isPercussion = false;
+ Track *track = m_composition.getTrackById(segment->getTrack());
+ if (track) {
+ InstrumentId iid = track->getInstrument();
+ Instrument *instrument = m_studio.getInstrumentById(iid);
+ if (instrument && instrument->isPercussion()) isPercussion = true;
+ }
+
+ for (Segment::iterator i = segment->begin();
+ i != segment->end(); ++i) {
+
+ long pitch = 0;
+ if (!(*i)->isa(Note::EventType) ||
+ !(*i)->get<Int>(BaseProperties::PITCH, pitch)) {
+ continue;
+ }
+
+ timeT eventStart = (*i)->getAbsoluteTime();
+ timeT eventEnd = eventStart + (*i)->getDuration();
+ // if (eventEnd > segment->getEndMarkerTime()) {
+ // eventEnd = segment->getEndMarkerTime();
+ // }
+
+ int x = int(nearbyint(m_grid.getRulerScale()->getXForTime(eventStart)));
+ int width = int(nearbyint(m_grid.getRulerScale()->getWidthForDuration(eventStart,
+ eventEnd - eventStart)));
+
+ if (x <= segStartX) {
+ ++x;
+ if (width > 1)
+ --width;
+ }
+ if (width > 1)
+ --width;
+ if (width < 1)
+ ++width;
+
+ double y0 = 0;
+ double y1 = m_grid.getYSnap();
+ double y = y1 + ((y0 - y1) * (pitch - 16)) / 96;
+
+ int height = 2;
+
+ if (isPercussion) {
+ height = 3;
+ if (width > 2) width = 2;
+ }
+
+ if (y < y0)
+ y = y0;
+ if (y > y1 - height + 1)
+ y = y1 - height + 1;
+
+ QRect r(x, (int)y, width, height);
+
+ // RG_DEBUG << "CompositionModelImpl::updatePreviewCacheForNotationSegment() : npData = "
+ // << npData << ", preview rect = "
+ // << r << endl;
+ npData->push_back(r);
+ }
+
+}
+
+QColor CompositionModelImpl::computeSegmentPreviewColor(const Segment* segment)
+{
+ // compute the preview color so it's as visible as possible over the segment's color
+ QColor segColor = GUIPalette::convertColour(m_composition.getSegmentColourMap().getColourByIndex(segment->getColourIndex()));
+ int h, s, v;
+ segColor.hsv(&h, &s, &v);
+
+ // colors with saturation lower than value should be pastel tints, and
+ // they get a value of 0; yellow and green hues close to the dead center
+ // point for that hue were taking a value of 255 with the (s < v)
+ // formula, so I added an extra hack to force hues in those two narrow
+ // ranges toward black. Black always looks good, while white washes out
+ // badly against intense yellow, and doesn't look very good against
+ // intense green either... hacky, but this produces pleasant results against
+ // every bizarre extreme of color I could cook up to throw at it, plus
+ // (the real reason for all this convoluted fiddling, it does all that while keeping
+ // white against bright reds and blues, which looks better than black)
+ if ( ((((h > 57) && (h < 66)) || ((h > 93) && (h < 131))) && (s > 127) && (v > 127) ) ||
+ (s < v) ) {
+ v = 0;
+ } else {
+ v = 255;
+ }
+ s = 31;
+ h += 180;
+
+ segColor.setHsv(h, s, v);
+
+ return segColor;
+}
+
+void CompositionModelImpl::updatePreviewCacheForAudioSegment(const Segment* segment, AudioPreviewData* apData)
+{
+ if (m_audioPreviewThread) {
+ // std::cerr << "CompositionModelImpl::updatePreviewCacheForAudioSegment() - new audio preview started" << std::endl;
+
+ CompositionRect segRect = computeSegmentRect(*segment);
+ segRect.setWidth(segRect.getBaseWidth()); // don't use repeating area
+ segRect.moveTopLeft(QPoint(0, 0));
+
+ if (apData)
+ apData->setSegmentRect(segRect);
+
+ if (m_audioPreviewUpdaterMap.find(segment) ==
+ m_audioPreviewUpdaterMap.end()) {
+
+ AudioPreviewUpdater *updater = new AudioPreviewUpdater
+ (*m_audioPreviewThread, m_composition, segment, segRect, this);
+
+ connect(updater, SIGNAL(audioPreviewComplete(AudioPreviewUpdater*)),
+ this, SLOT(slotAudioPreviewComplete(AudioPreviewUpdater*)));
+
+ m_audioPreviewUpdaterMap[segment] = updater;
+
+ } else {
+
+ m_audioPreviewUpdaterMap[segment]->setDisplayExtent(segRect);
+ }
+
+ m_audioPreviewUpdaterMap[segment]->update();
+
+ } else {
+ RG_DEBUG << "CompositionModelImpl::updatePreviewCacheForAudioSegment() - no audio preview thread set\n";
+ }
+}
+
+void CompositionModelImpl::slotAudioPreviewComplete(AudioPreviewUpdater* apu)
+{
+ RG_DEBUG << "CompositionModelImpl::slotAudioPreviewComplete()\n";
+
+ AudioPreviewData *apData = getAudioPreviewData(apu->getSegment());
+ QRect updateRect;
+
+ if (apData) {
+ RG_DEBUG << "CompositionModelImpl::slotAudioPreviewComplete(" << apu << "): apData contains " << apData->getValues().size() << " values already" << endl;
+ unsigned int channels = 0;
+ const std::vector<float> &values = apu->getComputedValues(channels);
+ if (channels > 0) {
+ RG_DEBUG << "CompositionModelImpl::slotAudioPreviewComplete: set "
+ << values.size() << " samples on " << channels << " channels\n";
+ apData->setChannels(channels);
+ apData->setValues(values);
+ updateRect = postProcessAudioPreview(apData, apu->getSegment());
+ }
+ }
+
+ if (!updateRect.isEmpty())
+ emit needContentUpdate(updateRect);
+}
+
+QRect CompositionModelImpl::postProcessAudioPreview(AudioPreviewData* apData, const Segment* segment)
+{
+ // RG_DEBUG << "CompositionModelImpl::postProcessAudioPreview()\n";
+
+ AudioPreviewPainter previewPainter(*this, apData, m_composition, segment);
+ previewPainter.paintPreviewImage();
+
+ m_audioSegmentPreviewMap[segment] = previewPainter.getPreviewImage();
+
+ return previewPainter.getSegmentRect();
+}
+
+void CompositionModelImpl::slotInstrumentParametersChanged(InstrumentId id)
+{
+ std::cerr << "CompositionModelImpl::slotInstrumentParametersChanged()\n";
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ Segment* s = *i;
+ TrackId trackId = s->getTrack();
+ Track *track = getComposition().getTrackById(trackId);
+
+ // We need to update the cache for audio segments, because the
+ // instrument playback level is reflected in the audio
+ // preview. And we need to update it for midi segments,
+ // because the preview style differs depending on whether the
+ // segment is on a percussion instrument or not
+
+ if (track && track->getInstrument() == id) {
+ removePreviewCache(s);
+ emit needContentUpdate(computeSegmentRect(*s));
+ }
+ }
+}
+
+void CompositionModelImpl::slotAudioFileFinalized(Segment* s)
+{
+ // RG_DEBUG << "CompositionModelImpl::slotAudioFileFinalized()\n";
+ removePreviewCache(s);
+}
+
+PixmapArray CompositionModelImpl::getAudioPreviewPixmap(const Segment* s)
+{
+ getAudioPreviewData(s);
+ return m_audioSegmentPreviewMap[s];
+}
+
+void CompositionModelImpl::eventAdded(const Segment *s, Event *)
+{
+ // RG_DEBUG << "CompositionModelImpl::eventAdded()\n";
+ removePreviewCache(s);
+ emit needContentUpdate(computeSegmentRect(*s));
+}
+
+void CompositionModelImpl::eventRemoved(const Segment *s, Event *)
+{
+ // RG_DEBUG << "CompositionModelImpl::eventRemoved" << endl;
+ removePreviewCache(s);
+ emit needContentUpdate(computeSegmentRect(*s));
+}
+
+void CompositionModelImpl::appearanceChanged(const Segment *s)
+{
+ // RG_DEBUG << "CompositionModelImpl::appearanceChanged" << endl;
+ clearInCache(s, true);
+ emit needContentUpdate(computeSegmentRect(*s));
+}
+
+void CompositionModelImpl::endMarkerTimeChanged(const Segment *s, bool shorten)
+{
+ // RG_DEBUG << "CompositionModelImpl::endMarkerTimeChanged(" << shorten << ")" << endl;
+ clearInCache(s, true);
+ if (shorten) {
+ emit needContentUpdate(); // no longer know former segment dimension
+ } else {
+ emit needContentUpdate(computeSegmentRect(*s));
+ }
+}
+
+void CompositionModelImpl::makePreviewCache(const Segment *s)
+{
+ if (s->getType() == Segment::Internal) {
+ makeNotationPreviewDataCache(s);
+ } else {
+ makeAudioPreviewDataCache(s);
+ }
+}
+
+void CompositionModelImpl::removePreviewCache(const Segment *s)
+{
+ if (s->getType() == Segment::Internal) {
+ m_notationPreviewDataCache.remove(const_cast<Segment*>(s));
+ } else {
+ m_audioPreviewDataCache.remove(const_cast<Segment*>(s));
+ m_audioSegmentPreviewMap.erase(s);
+ }
+
+}
+
+void CompositionModelImpl::segmentAdded(const Composition *, Segment *s)
+{
+ std::cerr << "CompositionModelImpl::segmentAdded: segment " << s << " on track " << s->getTrack() << ": calling setTrackHeights" << std::endl;
+ setTrackHeights(s);
+
+ makePreviewCache(s);
+ s->addObserver(this);
+ emit needContentUpdate();
+}
+
+void CompositionModelImpl::segmentRemoved(const Composition *, Segment *s)
+{
+ setTrackHeights();
+
+ QRect r = computeSegmentRect(*s);
+
+ m_selectedSegments.erase(s);
+
+ clearInCache(s, true);
+ s->removeObserver(this);
+ m_recordingSegments.erase(s); // this could be a recording segment
+ emit needContentUpdate(r);
+}
+
+void CompositionModelImpl::segmentTrackChanged(const Composition *, Segment *s, TrackId tid)
+{
+ std::cerr << "CompositionModelImpl::segmentTrackChanged: segment " << s << " on track " << tid << ", calling setTrackHeights" << std::endl;
+
+ // we don't call setTrackHeights(s), because some of the tracks
+ // above s may have changed height as well (if s was moved off one
+ // of them)
+ if (setTrackHeights()) {
+ std::cerr << "... changed, updating" << std::endl;
+ emit needContentUpdate();
+ }
+}
+
+void CompositionModelImpl::segmentStartChanged(const Composition *, Segment *s, timeT)
+{
+// std::cerr << "CompositionModelImpl::segmentStartChanged: segment " << s << " on track " << s->getTrack() << ": calling setTrackHeights" << std::endl;
+ if (setTrackHeights(s)) emit needContentUpdate();
+}
+
+void CompositionModelImpl::segmentEndMarkerChanged(const Composition *, Segment *s, bool)
+{
+// std::cerr << "CompositionModelImpl::segmentEndMarkerChanged: segment " << s << " on track " << s->getTrack() << ": calling setTrackHeights" << std::endl;
+ if (setTrackHeights(s)) {
+// std::cerr << "... changed, updating" << std::endl;
+ emit needContentUpdate();
+ }
+}
+
+void CompositionModelImpl::segmentRepeatChanged(const Composition *, Segment *s, bool)
+{
+ clearInCache(s);
+ setTrackHeights(s);
+ emit needContentUpdate();
+}
+
+void CompositionModelImpl::endMarkerTimeChanged(const Composition *, bool)
+{
+ emit needSizeUpdate();
+}
+
+void CompositionModelImpl::setSelectionRect(const QRect& r)
+{
+ m_selectionRect = r.normalize();
+
+ m_previousTmpSelectedSegments = m_tmpSelectedSegments;
+ m_tmpSelectedSegments.clear();
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ QRect updateRect = m_selectionRect;
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ Segment* s = *i;
+ CompositionRect sr = computeSegmentRect(*s);
+ if (sr.intersects(m_selectionRect)) {
+ m_tmpSelectedSegments.insert(s);
+ updateRect |= sr;
+ }
+ }
+
+ updateRect = updateRect.normalize();
+
+ if (!updateRect.isNull() && !m_previousSelectionUpdateRect.isNull()) {
+
+ if (m_tmpSelectedSegments != m_previousTmpSelectedSegments)
+ emit needContentUpdate(updateRect | m_previousSelectionUpdateRect);
+
+ emit needArtifactsUpdate();
+ }
+
+
+ m_previousSelectionUpdateRect = updateRect;
+
+}
+
+void CompositionModelImpl::finalizeSelectionRect()
+{
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ Segment* s = *i;
+ CompositionRect sr = computeSegmentRect(*s);
+ if (sr.intersects(m_selectionRect)) {
+ setSelected(s);
+ }
+ }
+
+ m_previousSelectionUpdateRect = m_selectionRect = QRect();
+ m_tmpSelectedSegments.clear();
+}
+
+QRect CompositionModelImpl::getSelectionContentsRect()
+{
+ QRect selectionRect;
+
+ SegmentSelection sel = getSelectedSegments();
+ for (SegmentSelection::iterator i = sel.begin();
+ i != sel.end(); ++i) {
+
+ Segment* s = *i;
+ CompositionRect sr = computeSegmentRect(*s);
+ selectionRect |= sr;
+ }
+
+ return selectionRect;
+}
+
+void CompositionModelImpl::addRecordingItem(const CompositionItem& item)
+{
+ m_recordingSegments.insert(CompositionItemHelper::getSegment(item));
+ emit needContentUpdate();
+
+ RG_DEBUG << "CompositionModelImpl::addRecordingItem: now have "
+ << m_recordingSegments.size() << " recording items\n";
+}
+
+void CompositionModelImpl::removeRecordingItem(const CompositionItem &item)
+{
+ Segment* s = CompositionItemHelper::getSegment(item);
+
+ m_recordingSegments.erase(s);
+ clearInCache(s, true);
+
+ emit needContentUpdate();
+
+ RG_DEBUG << "CompositionModelImpl::removeRecordingItem: now have "
+ << m_recordingSegments.size() << " recording items\n";
+}
+
+void CompositionModelImpl::clearRecordingItems()
+{
+ for (recordingsegmentset::iterator i = m_recordingSegments.begin();
+ i != m_recordingSegments.end(); ++i)
+ clearInCache(*i, true);
+
+ m_recordingSegments.clear();
+
+ emit needContentUpdate();
+ RG_DEBUG << "CompositionModelImpl::clearRecordingItem\n";
+}
+
+bool CompositionModelImpl::isMoving(const Segment* sm) const
+{
+ itemcontainer::const_iterator movEnd = m_changingItems.end();
+
+ for (itemcontainer::const_iterator i = m_changingItems.begin(); i != movEnd; ++i) {
+ const CompositionItemImpl* ci = dynamic_cast<const CompositionItemImpl*>((_CompositionItem*)(*i));
+ const Segment* s = ci->getSegment();
+ if (sm == s)
+ return true;
+ }
+
+ return false;
+}
+
+bool CompositionModelImpl::isRecording(const Segment* s) const
+{
+ return m_recordingSegments.find(const_cast<Segment*>(s)) != m_recordingSegments.end();
+}
+
+CompositionModel::itemcontainer CompositionModelImpl::getItemsAt(const QPoint& point)
+{
+ itemcontainer res;
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segments.end(); ++i) {
+
+ Segment* s = *i;
+
+ CompositionRect sr = computeSegmentRect(*s);
+ if (sr.contains(point)) {
+ // RG_DEBUG << "CompositionModelImpl::getItemsAt() adding " << sr << " for segment " << s << endl;
+ CompositionItem item(new CompositionItemImpl(*s, sr));
+ unsigned int z = computeZForSegment(s);
+ // RG_DEBUG << "CompositionModelImpl::getItemsAt() z = " << z << endl;
+ item->setZ(z);
+ res.insert(item);
+ } else {
+ // RG_DEBUG << "CompositionModelImpl::getItemsAt() skiping " << sr << endl;
+ }
+
+ }
+
+ if (res.size() == 1) { // only one segment under click point
+ Segment* s = CompositionItemHelper::getSegment(*(res.begin()));
+ m_segmentOrderer.segmentClicked(s);
+ }
+
+ return res;
+}
+
+void CompositionModelImpl::setPointerPos(int xPos)
+{
+ m_pointerTimePos = grid().getRulerScale()->getTimeForX(xPos);
+
+ for (recordingsegmentset::iterator i = m_recordingSegments.begin();
+ i != m_recordingSegments.end(); ++i) {
+ emit needContentUpdate(computeSegmentRect(**i));
+ }
+}
+
+void CompositionModelImpl::setSelected(const CompositionItem& item, bool selected)
+{
+ const CompositionItemImpl* itemImpl = dynamic_cast<const CompositionItemImpl*>((_CompositionItem*)item);
+ if (itemImpl) {
+ Segment* segment = const_cast<Segment*>(itemImpl->getSegment());
+ setSelected(segment, selected);
+ }
+}
+
+void CompositionModelImpl::setSelected(const itemcontainer& items)
+{
+ for (itemcontainer::const_iterator i = items.begin(); i != items.end(); ++i) {
+ setSelected(*i);
+ }
+}
+
+void CompositionModelImpl::setSelected(const Segment* segment, bool selected)
+{
+ RG_DEBUG << "CompositionModelImpl::setSelected " << segment << " - " << selected << endl;
+ if (selected) {
+ if (!isSelected(segment))
+ m_selectedSegments.insert(const_cast<Segment*>(segment));
+ } else {
+ SegmentSelection::iterator i = m_selectedSegments.find(const_cast<Segment*>(segment));
+ if (i != m_selectedSegments.end())
+ m_selectedSegments.erase(i);
+ }
+ emit needContentUpdate();
+}
+
+void CompositionModelImpl::signalSelection()
+{
+ // RG_DEBUG << "CompositionModelImpl::signalSelection()\n";
+ emit selectedSegments(getSelectedSegments());
+}
+
+void CompositionModelImpl::signalContentChange()
+{
+ // RG_DEBUG << "CompositionModelImpl::signalContentChange" << endl;
+ emit needContentUpdate();
+}
+
+void CompositionModelImpl::clearSelected()
+{
+ RG_DEBUG << "CompositionModelImpl::clearSelected" << endl;
+ m_selectedSegments.clear();
+ emit needContentUpdate();
+}
+
+bool CompositionModelImpl::isSelected(const CompositionItem& ci) const
+{
+ const CompositionItemImpl* itemImpl = dynamic_cast<const CompositionItemImpl*>((_CompositionItem*)ci);
+ return itemImpl ? isSelected(itemImpl->getSegment()) : 0;
+}
+
+bool CompositionModelImpl::isSelected(const Segment* s) const
+{
+ return m_selectedSegments.find(const_cast<Segment*>(s)) != m_selectedSegments.end();
+}
+
+bool CompositionModelImpl::isTmpSelected(const Segment* s) const
+{
+ return m_tmpSelectedSegments.find(const_cast<Segment*>(s)) != m_tmpSelectedSegments.end();
+}
+
+bool CompositionModelImpl::wasTmpSelected(const Segment* s) const
+{
+ return m_previousTmpSelectedSegments.find(const_cast<Segment*>(s)) != m_previousTmpSelectedSegments.end();
+}
+
+void CompositionModelImpl::startChange(const CompositionItem& item, CompositionModel::ChangeType change)
+{
+ m_changeType = change;
+
+ itemcontainer::iterator i = m_changingItems.find(item);
+
+ // if an "identical" composition item has already been inserted, drop this one
+ if (i != m_changingItems.end()) {
+ RG_DEBUG << "CompositionModelImpl::startChange : item already in\n";
+ m_itemGC.push_back(item);
+ } else {
+ item->saveRect();
+ m_changingItems.insert(item);
+ }
+}
+
+void CompositionModelImpl::startChangeSelection(CompositionModel::ChangeType change)
+{
+ SegmentSelection::iterator i = m_selectedSegments.begin();
+ for (; i != m_selectedSegments.end(); ++i) {
+ Segment* s = *i;
+ CompositionRect sr = computeSegmentRect(*s);
+ startChange(CompositionItem(new CompositionItemImpl(*s, sr)), change);
+ }
+
+}
+
+void CompositionModelImpl::endChange()
+{
+ for (itemcontainer::const_iterator i = m_changingItems.begin(); i != m_changingItems.end(); ++i) {
+ delete *i;
+ }
+
+ m_changingItems.clear();
+
+ for (itemgc::iterator i = m_itemGC.begin(); i != m_itemGC.end(); ++i) {
+ delete *i;
+ }
+ m_itemGC.clear();
+ RG_DEBUG << "CompositionModelImpl::endChange\n";
+ emit needContentUpdate();
+}
+
+void CompositionModelImpl::setLength(int width)
+{
+ timeT endMarker = m_grid.snapX(width);
+ m_composition.setEndMarker(endMarker);
+}
+
+int CompositionModelImpl::getLength()
+{
+ timeT endMarker = m_composition.getEndMarker();
+ int w = int(nearbyint(m_grid.getRulerScale()->getWidthForDuration(0, endMarker)));
+ return w;
+}
+
+timeT CompositionModelImpl::getRepeatTimeAt(const QPoint& p, const CompositionItem& cItem)
+{
+ // timeT timeAtClick = m_grid.getRulerScale()->getTimeForX(p.x());
+
+ CompositionItemImpl* itemImpl = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)cItem);
+
+ const Segment* s = itemImpl->getSegment();
+
+ timeT startTime = s->getStartTime();
+ timeT endTime = s->getEndMarkerTime();
+ timeT repeatInterval = endTime - startTime;
+
+ int rWidth = int(nearbyint(m_grid.getRulerScale()->getXForTime(repeatInterval)));
+
+ int count = (p.x() - int(itemImpl->rect().x())) / rWidth;
+ RG_DEBUG << "CompositionModelImpl::getRepeatTimeAt() : count = " << count << endl;
+
+ return count != 0 ? startTime + (count * (s->getEndMarkerTime() - s->getStartTime())) : 0;
+}
+
+bool CompositionModelImpl::setTrackHeights(Segment *s)
+{
+ bool heightsChanged = false;
+
+// std::cerr << "CompositionModelImpl::setTrackHeights" << std::endl;
+
+ for (Composition::trackcontainer::const_iterator i =
+ m_composition.getTracks().begin();
+ i != m_composition.getTracks().end(); ++i) {
+
+ if (s && i->first != s->getTrack()) continue;
+
+ int max = m_composition.getMaxContemporaneousSegmentsOnTrack(i->first);
+ if (max == 0) max = 1;
+
+// std::cerr << "for track " << i->first << ": height = " << max << ", old height = " << m_trackHeights[i->first] << std::endl;
+
+ if (max != m_trackHeights[i->first]) {
+ heightsChanged = true;
+ m_trackHeights[i->first] = max;
+ }
+
+ m_grid.setBinHeightMultiple(i->second->getPosition(), max);
+ }
+
+ if (heightsChanged) {
+// std::cerr << "CompositionModelImpl::setTrackHeights: heights have changed" << std::endl;
+ for (Composition::segmentcontainer::iterator i = m_composition.begin();
+ i != m_composition.end(); ++i) {
+ computeSegmentRect(**i);
+ }
+ }
+
+ return heightsChanged;
+}
+
+QPoint CompositionModelImpl::computeSegmentOrigin(const Segment& s)
+{
+ // Profiler profiler("CompositionModelImpl::computeSegmentOrigin", true);
+
+ int trackPosition = m_composition.getTrackPositionById(s.getTrack());
+ timeT startTime = s.getStartTime();
+
+ QPoint res;
+
+ res.setX(int(nearbyint(m_grid.getRulerScale()->getXForTime(startTime))));
+
+ res.setY(m_grid.getYBinCoordinate(trackPosition) +
+ m_composition.getSegmentVoiceIndex(&s) *
+ m_grid.getYSnap() + 1);
+
+ return res;
+}
+
+bool CompositionModelImpl::isCachedRectCurrent(const Segment& s, const CompositionRect& r, QPoint cachedSegmentOrigin, timeT cachedSegmentEndTime)
+{
+ return s.isRepeating() == r.isRepeating() &&
+ ((cachedSegmentOrigin.x() != r.x() && s.getEndMarkerTime() != cachedSegmentEndTime) ||
+ (cachedSegmentOrigin.x() == r.x() && s.getEndMarkerTime() == cachedSegmentEndTime));
+}
+
+void CompositionModelImpl::clearInCache(const Segment* s, bool clearPreview)
+{
+ if (s) {
+ m_segmentRectMap.erase(s);
+ m_segmentEndTimeMap.erase(s);
+ if (clearPreview)
+ removePreviewCache(s);
+ } else { // clear the whole cache
+ m_segmentRectMap.clear();
+ m_segmentEndTimeMap.clear();
+ if (clearPreview)
+ clearPreviewCache();
+ }
+}
+
+void CompositionModelImpl::putInCache(const Segment*s, const CompositionRect& cr)
+{
+ m_segmentRectMap[s] = cr;
+ m_segmentEndTimeMap[s] = s->getEndMarkerTime();
+}
+
+CompositionRect CompositionModelImpl::computeSegmentRect(const Segment& s, bool computeZ)
+{
+ // Profiler profiler("CompositionModelImpl::computeSegmentRect", true);
+
+ QPoint origin = computeSegmentOrigin(s);
+
+ bool isRecordingSegment = isRecording(&s);
+
+ if (!isRecordingSegment) {
+ timeT endTime = 0;
+
+ CompositionRect cachedCR = getFromCache(&s, endTime);
+ // don't cache repeating segments - it's just hopeless, because the segment's rect may have to be recomputed
+ // in other cases than just when the segment itself is moved,
+ // for instance if another segment is moved over it
+ if (!s.isRepeating() && cachedCR.isValid() && isCachedRectCurrent(s, cachedCR, origin, endTime)) {
+ // RG_DEBUG << "CompositionModelImpl::computeSegmentRect() : using cache for seg "
+ // << &s << " - cached rect repeating = " << cachedCR.isRepeating() << " - base width = "
+ // << cachedCR.getBaseWidth() << endl;
+
+ bool xChanged = origin.x() != cachedCR.x();
+ bool yChanged = origin.y() != cachedCR.y();
+
+ cachedCR.moveTopLeft(origin);
+
+ if (s.isRepeating() && (xChanged || yChanged)) { // update repeat marks
+
+ // this doesn't work in the general case (if there's another segment on the same track for instance),
+ // it's better to simply recompute all the marks
+ // CompositionRect::repeatmarks repeatMarks = cachedCR.getRepeatMarks();
+ // for(unsigned int i = 0; i < repeatMarks.size(); ++i) {
+ // repeatMarks[i] += deltaX;
+ // }
+ // cachedCR.setRepeatMarks(repeatMarks);
+ computeRepeatMarks(cachedCR, &s);
+ }
+ putInCache(&s, cachedCR);
+ return cachedCR;
+ }
+ }
+
+ timeT startTime = s.getStartTime();
+ timeT endTime = isRecordingSegment ? m_pointerTimePos /*s.getEndTime()*/ : s.getEndMarkerTime();
+
+
+ int h = m_grid.getYSnap() - 2;
+ int w;
+
+ RG_DEBUG << "CompositionModelImpl::computeSegmentRect: x " << origin.x() << ", y " << origin.y() << " startTime " << startTime << ", endTime " << endTime << endl;
+
+ if (s.isRepeating()) {
+ timeT repeatStart = endTime;
+ timeT repeatEnd = s.getRepeatEndTime();
+ w = int(nearbyint(m_grid.getRulerScale()->getWidthForDuration(startTime,
+ repeatEnd - startTime)));
+ // RG_DEBUG << "CompositionModelImpl::computeSegmentRect : s is repeating - repeatStart = "
+ // << repeatStart << " - repeatEnd : " << repeatEnd
+ // << " w = " << w << endl;
+ } else {
+ w = int(nearbyint(m_grid.getRulerScale()->getWidthForDuration(startTime, endTime - startTime)));
+ // RG_DEBUG << "CompositionModelImpl::computeSegmentRect : s is NOT repeating"
+ // << " w = " << w << " (x for time at start is " << m_grid.getRulerScale()->getXForTime(startTime) << ", end is " << m_grid.getRulerScale()->getXForTime(endTime) << ")" << endl;
+ }
+
+ CompositionRect cr(origin, QSize(w, h));
+ QString label = strtoqstr(s.getLabel());
+ if (s.getType() == Segment::Audio) {
+ static QRegExp re1("( *\\([^)]*\\))*$"); // (inserted) (copied) (etc)
+ static QRegExp re2("\\.[^.]+$"); // filename suffix
+ label.replace(re1, "").replace(re2, "");
+ }
+ cr.setLabel(label);
+
+ if (s.isRepeating()) {
+ computeRepeatMarks(cr, &s);
+ } else {
+ cr.setBaseWidth(cr.width());
+ }
+
+ putInCache(&s, cr);
+
+ return cr;
+}
+
+unsigned int CompositionModelImpl::computeZForSegment(const Rosegarden::Segment* s)
+{
+ return m_segmentOrderer.getZForSegment(s);
+}
+
+const CompositionRect& CompositionModelImpl::getFromCache(const Rosegarden::Segment* s, timeT& endTime)
+{
+ endTime = m_segmentEndTimeMap[s];
+ return m_segmentRectMap[s];
+}
+
+unsigned int CompositionModelImpl::getNbRows()
+{
+ return m_composition.getNbTracks();
+}
+
+const CompositionModel::rectcontainer& CompositionModelImpl::getRectanglesIn(const QRect& rect,
+ RectRanges* npData,
+ AudioPreviewDrawData* apData)
+{
+ // Profiler profiler("CompositionModelImpl::getRectanglesIn", true);
+
+ m_res.clear();
+
+ // RG_DEBUG << "CompositionModelImpl::getRectanglesIn: ruler scale is "
+ // << (dynamic_cast<SimpleRulerScale *>(m_grid.getRulerScale()))->getUnitsPerPixel() << endl;
+
+ const Composition::segmentcontainer& segments = m_composition.getSegments();
+ Composition::segmentcontainer::iterator segEnd = segments.end();
+
+ for (Composition::segmentcontainer::iterator i = segments.begin();
+ i != segEnd; ++i) {
+
+ // RG_DEBUG << "CompositionModelImpl::getRectanglesIn: Composition contains segment " << *i << " (" << (*i)->getStartTime() << "->" << (*i)->getEndTime() << ")"<< endl;
+
+ Segment* s = *i;
+
+ if (isMoving(s))
+ continue;
+
+ CompositionRect sr = computeSegmentRect(*s);
+ // RG_DEBUG << "CompositionModelImpl::getRectanglesIn: seg rect = " << sr << endl;
+
+ if (sr.intersects(rect)) {
+ bool tmpSelected = isTmpSelected(s),
+ pTmpSelected = wasTmpSelected(s);
+
+// RG_DEBUG << "CompositionModelImpl::getRectanglesIn: segment " << s
+// << " selected : " << isSelected(s) << " - tmpSelected : " << isTmpSelected(s) << endl;
+
+ if (isSelected(s) || isTmpSelected(s) || sr.intersects(m_selectionRect)) {
+ sr.setSelected(true);
+ }
+
+ if (pTmpSelected != tmpSelected)
+ sr.setNeedsFullUpdate(true);
+
+ bool isAudio = (s && s->getType() == Segment::Audio);
+
+ if (!isRecording(s)) {
+ QColor brushColor = GUIPalette::convertColour(m_composition.
+ getSegmentColourMap().getColourByIndex(s->getColourIndex()));
+ sr.setBrush(brushColor);
+ sr.setPen(CompositionColourCache::getInstance()->SegmentBorder);
+ } else {
+ // border is the same for both audio and MIDI
+ sr.setPen(CompositionColourCache::getInstance()->RecordingSegmentBorder);
+ // audio color
+ if (isAudio) {
+ sr.setBrush(CompositionColourCache::getInstance()->RecordingAudioSegmentBlock);
+ // MIDI/default color
+ } else {
+ sr.setBrush(CompositionColourCache::getInstance()->RecordingInternalSegmentBlock);
+ }
+ }
+
+ // Notation preview data
+ if (npData && s->getType() == Segment::Internal) {
+ makeNotationPreviewRects(npData, QPoint(0, sr.y()), s, rect);
+ // Audio preview data
+ } else if (apData && s->getType() == Segment::Audio) {
+ makeAudioPreviewRects(apData, s, sr, rect);
+ }
+
+ m_res.push_back(sr);
+ } else {
+ // RG_DEBUG << "CompositionModelImpl::getRectanglesIn: - segment out of rect\n";
+ }
+
+ }
+
+ // changing items
+
+ itemcontainer::iterator movEnd = m_changingItems.end();
+ for (itemcontainer::iterator i = m_changingItems.begin(); i != movEnd; ++i) {
+ CompositionRect sr((*i)->rect());
+ if (sr.intersects(rect)) {
+ Segment* s = CompositionItemHelper::getSegment(*i);
+ sr.setSelected(true);
+ QColor brushColor = GUIPalette::convertColour(m_composition.getSegmentColourMap().getColourByIndex(s->getColourIndex()));
+ sr.setBrush(brushColor);
+
+ sr.setPen(CompositionColourCache::getInstance()->SegmentBorder);
+
+ // Notation preview data
+ if (npData && s->getType() == Segment::Internal) {
+ makeNotationPreviewRectsMovingSegment(npData, sr.topLeft(), s, sr);
+ // Audio preview data
+ } else if (apData && s->getType() == Segment::Audio) {
+ makeAudioPreviewRects(apData, s, sr, rect);
+ }
+
+ m_res.push_back(sr);
+ }
+ }
+
+ return m_res;
+}
+
+CompositionModel::heightlist
+CompositionModelImpl::getTrackDividersIn(const QRect& rect)
+{
+ int top = m_grid.getYBin(rect.y());
+ int bottom = m_grid.getYBin(rect.y() + rect.height());
+
+// std::cerr << "CompositionModelImpl::getTrackDividersIn: rect "
+// << rect.x() << ", " << rect.y() << ", "
+// << rect.width() << "x" << rect.height() << ", top = " << top
+// << ", bottom = " << bottom << std::endl;
+
+ CompositionModel::heightlist list;
+
+ for (int pos = top; pos <= bottom; ++pos) {
+ int divider = m_grid.getYBinCoordinate(pos);
+ list.push_back(divider);
+// std::cerr << "divider at " << divider << std::endl;
+ }
+
+ return list;
+}
+
+CompositionModel::rectlist* CompositionModelImpl::getNotationPreviewData(const Segment* s)
+{
+ rectlist* npData = m_notationPreviewDataCache[const_cast<Segment*>(s)];
+
+ if (!npData) {
+ npData = makeNotationPreviewDataCache(s);
+ }
+
+ return npData;
+}
+
+CompositionModel::AudioPreviewData* CompositionModelImpl::getAudioPreviewData(const Segment* s)
+{
+ // Profiler profiler("CompositionModelImpl::getAudioPreviewData", true);
+ RG_DEBUG << "CompositionModelImpl::getAudioPreviewData\n";
+
+ AudioPreviewData* apData = m_audioPreviewDataCache[const_cast<Segment*>(s)];
+
+ if (!apData) {
+ apData = makeAudioPreviewDataCache(s);
+ }
+
+ RG_DEBUG << "CompositionModelImpl::getAudioPreviewData returning\n";
+ return apData;
+}
+
+CompositionModel::rectlist* CompositionModelImpl::makeNotationPreviewDataCache(const Segment *s)
+{
+ rectlist* npData = new rectlist();
+ updatePreviewCacheForNotationSegment(s, npData);
+ m_notationPreviewDataCache.insert(const_cast<Segment*>(s), npData);
+ return npData;
+}
+
+CompositionModel::AudioPreviewData* CompositionModelImpl::makeAudioPreviewDataCache(const Segment *s)
+{
+ RG_DEBUG << "CompositionModelImpl::makeAudioPreviewDataCache(" << s << ")" << endl;
+
+ AudioPreviewData* apData = new AudioPreviewData(false, 0); // 0 channels -> empty
+ updatePreviewCacheForAudioSegment(s, apData);
+ m_audioPreviewDataCache.insert(const_cast<Segment*>(s), apData);
+ return apData;
+}
+
+}
+#include "CompositionModelImpl.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.h b/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.h
new file mode 100644
index 0000000..6e1c9d6
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionModelImpl.h
@@ -0,0 +1,239 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONMODELIMPL_H_
+#define _RG_COMPOSITIONMODELIMPL_H_
+
+#include "base/Selection.h"
+#include "base/SnapGrid.h"
+#include "CompositionModel.h"
+#include "CompositionRect.h"
+#include <map>
+#include "SegmentOrderer.h"
+#include <set>
+#include <qcolor.h>
+#include <qpoint.h>
+#include <qptrdict.h>
+#include <qrect.h>
+#include <vector>
+#include "base/Event.h"
+
+
+class RectRanges;
+class CompositionItem;
+class AudioPreviewDrawData;
+class AudioPreviewData;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class Segment;
+class RulerScale;
+class Event;
+class Composition;
+class AudioPreviewUpdater;
+class AudioPreviewThread;
+
+
+class CompositionModelImpl : public CompositionModel
+{
+ Q_OBJECT
+public:
+
+ CompositionModelImpl(Composition& compo,
+ Studio& studio,
+ RulerScale *rulerScale,
+ int vStep);
+
+ virtual ~CompositionModelImpl();
+
+ virtual unsigned int getNbRows();
+ virtual const rectcontainer& getRectanglesIn(const QRect& rect,
+ RectRanges* notationRects, AudioPreviewDrawData* audioRects);
+ virtual heightlist getTrackDividersIn(const QRect& rect);
+ virtual itemcontainer getItemsAt (const QPoint&);
+ virtual timeT getRepeatTimeAt (const QPoint&, const CompositionItem&);
+
+ virtual SnapGrid& grid() { return m_grid; }
+
+ virtual void setPointerPos(int xPos);
+ virtual void setSelected(const CompositionItem&, bool selected = true);
+ virtual bool isSelected(const CompositionItem&) const;
+ virtual void setSelected(const itemcontainer&);
+ virtual void clearSelected();
+ virtual bool haveSelection() const { return !m_selectedSegments.empty(); }
+ virtual bool haveMultipleSelection() const { return m_selectedSegments.size() > 1; }
+ virtual void signalSelection();
+ virtual void setSelectionRect(const QRect&);
+ virtual void finalizeSelectionRect();
+ virtual QRect getSelectionContentsRect();
+ virtual void signalContentChange();
+
+ virtual void addRecordingItem(const CompositionItem&);
+ virtual void removeRecordingItem(const CompositionItem &);
+ virtual void clearRecordingItems();
+ virtual bool haveRecordingItems() { return m_recordingSegments.size() > 0; }
+
+ virtual void startChange(const CompositionItem&, ChangeType change);
+ virtual void startChangeSelection(ChangeType change);
+ virtual itemcontainer& getChangingItems() { return m_changingItems; }
+ virtual void endChange();
+ virtual ChangeType getChangeType() { return m_changeType; }
+
+ virtual void setLength(int width);
+ virtual int getLength();
+
+ void setAudioPreviewThread(AudioPreviewThread *thread);
+ AudioPreviewThread* getAudioPreviewThread() { return m_audioPreviewThread; }
+
+ void clearPreviewCache();
+ void clearSegmentRectsCache(bool clearPreviews = false) { clearInCache(0, clearPreviews); }
+
+ rectlist* makeNotationPreviewDataCache(const Segment *s);
+ AudioPreviewData* makeAudioPreviewDataCache(const Segment *s);
+
+ CompositionRect computeSegmentRect(const Segment&, bool computeZ = false);
+ QColor computeSegmentPreviewColor(const Segment*);
+ QPoint computeSegmentOrigin(const Segment&);
+ void computeRepeatMarks(CompositionItem&);
+
+ SegmentSelection getSelectedSegments() { return m_selectedSegments; }
+ Composition& getComposition() { return m_composition; }
+ Studio& getStudio() { return m_studio; }
+
+
+ // CompositionObserver
+ virtual void segmentAdded(const Composition *, Segment *);
+ virtual void segmentRemoved(const Composition *, Segment *);
+ virtual void segmentRepeatChanged(const Composition *, Segment *, bool);
+ virtual void segmentStartChanged(const Composition *, Segment *, timeT);
+ virtual void segmentEndMarkerChanged(const Composition *, Segment *, bool);
+ virtual void segmentTrackChanged(const Composition *, Segment *, TrackId);
+ virtual void endMarkerTimeChanged(const Composition *, bool /*shorten*/);
+
+ // SegmentObserver
+ virtual void eventAdded(const Segment *, Event *);
+ virtual void eventRemoved(const Segment *, Event *);
+ virtual void appearanceChanged(const Segment *);
+ virtual void endMarkerTimeChanged(const Segment *, bool /*shorten*/);
+ virtual void segmentDeleted(const Segment*) { /* nothing to do - handled by CompositionObserver::segmentRemoved() */ };
+
+signals:
+ void selectedSegments(const SegmentSelection &);
+ void needSizeUpdate();
+
+public slots:
+ void slotAudioFileFinalized(Segment*);
+ void slotInstrumentParametersChanged(InstrumentId);
+
+protected slots:
+ void slotAudioPreviewComplete(AudioPreviewUpdater*);
+
+protected:
+ bool setTrackHeights(Segment *changed = 0); // true if something changed
+
+ void setSelected(const Segment*, bool selected = true);
+ bool isSelected(const Segment*) const;
+ bool isTmpSelected(const Segment*) const;
+ bool wasTmpSelected(const Segment*) const;
+ bool isMoving(const Segment*) const;
+ bool isRecording(const Segment*) const;
+
+ void computeRepeatMarks(CompositionRect& sr, const Segment* s);
+ unsigned int computeZForSegment(const Segment* s);
+
+ // segment preview stuff
+
+ void updatePreviewCacheForNotationSegment(const Segment* s, rectlist*);
+ void updatePreviewCacheForAudioSegment(const Segment* s, AudioPreviewData*);
+ rectlist* getNotationPreviewData(const Segment* s);
+ AudioPreviewData* getAudioPreviewData(const Segment* s);
+ PixmapArray getAudioPreviewPixmap(const Segment* s);
+ QRect postProcessAudioPreview(AudioPreviewData*, const Segment*);
+
+ void makePreviewCache(const Segment* s);
+ void removePreviewCache(const Segment* s);
+ void makeNotationPreviewRects(RectRanges* npData, QPoint basePoint, const Segment*, const QRect&);
+ void makeNotationPreviewRectsMovingSegment(RectRanges* npData, QPoint basePoint, const Segment*,
+ const QRect&);
+ void makeAudioPreviewRects(AudioPreviewDrawData* apRects, const Segment*,
+ const CompositionRect& segRect, const QRect& clipRect);
+
+ void clearInCache(const Segment*, bool clearPreviewCache = false);
+ void putInCache(const Segment*, const CompositionRect&);
+ const CompositionRect& getFromCache(const Segment*, timeT& endTime);
+ bool isCachedRectCurrent(const Segment& s, const CompositionRect& r,
+ QPoint segmentOrigin, timeT segmentEndTime);
+
+ //--------------- Data members ---------------------------------
+ Composition& m_composition;
+ Studio& m_studio;
+ SnapGrid m_grid;
+ SegmentSelection m_selectedSegments;
+ SegmentSelection m_tmpSelectedSegments;
+ SegmentSelection m_previousTmpSelectedSegments;
+
+ timeT m_pointerTimePos;
+
+ typedef std::set<Segment *> recordingsegmentset;
+ recordingsegmentset m_recordingSegments;
+
+ typedef std::vector<CompositionItem> itemgc;
+
+ AudioPreviewThread* m_audioPreviewThread;
+
+ typedef QPtrDict<rectlist> NotationPreviewDataCache;
+ typedef QPtrDict<AudioPreviewData> AudioPreviewDataCache;
+
+ NotationPreviewDataCache m_notationPreviewDataCache;
+ AudioPreviewDataCache m_audioPreviewDataCache;
+
+ rectcontainer m_res;
+ itemcontainer m_changingItems;
+ ChangeType m_changeType;
+ itemgc m_itemGC;
+
+ QRect m_selectionRect;
+ QRect m_previousSelectionUpdateRect;
+
+ std::map<const Segment*, CompositionRect> m_segmentRectMap;
+ std::map<const Segment*, timeT> m_segmentEndTimeMap;
+ std::map<const Segment*, PixmapArray> m_audioSegmentPreviewMap;
+ std::map<TrackId, int> m_trackHeights;
+
+ typedef std::map<const Segment*, AudioPreviewUpdater *>
+ AudioPreviewUpdaterMap;
+ AudioPreviewUpdaterMap m_audioPreviewUpdaterMap;
+
+ SegmentOrderer m_segmentOrderer;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionRect.cpp b/src/gui/editors/segment/segmentcanvas/CompositionRect.cpp
new file mode 100644
index 0000000..9e34d71
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionRect.cpp
@@ -0,0 +1,42 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "CompositionRect.h"
+#include "base/ColourMap.h"
+
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+ const QColor CompositionRect::DefaultPenColor = Qt::black;
+ const QColor CompositionRect::DefaultBrushColor = QColor(COLOUR_DEF_R, COLOUR_DEF_G, COLOUR_DEF_B);
+}
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionRect.h b/src/gui/editors/segment/segmentcanvas/CompositionRect.h
new file mode 100644
index 0000000..3c3d2b6
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionRect.h
@@ -0,0 +1,108 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONRECT_H_
+#define _RG_COMPOSITIONRECT_H_
+
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <qvaluevector.h>
+
+
+class QSize;
+class QPoint;
+
+
+namespace Rosegarden
+{
+
+class CompositionRect : public QRect
+{
+public:
+ typedef QValueVector<int> repeatmarks;
+
+ friend bool operator<(const CompositionRect&, const CompositionRect&);
+
+ CompositionRect() : QRect(), m_selected(false),
+ m_needUpdate(false), m_brush(DefaultBrushColor), m_pen(DefaultPenColor) {};
+ CompositionRect(const QRect& r) : QRect(r), m_resized(false), m_selected(false),
+ m_needUpdate(false), m_brush(DefaultBrushColor), m_pen(DefaultPenColor), m_z(0) {};
+ CompositionRect(const QPoint & topLeft, const QPoint & bottomRight)
+ : QRect(topLeft, bottomRight), m_resized(false), m_selected(false),
+ m_needUpdate(false), m_brush(DefaultBrushColor), m_pen(DefaultPenColor), m_z(0) {};
+ CompositionRect(const QPoint & topLeft, const QSize & size)
+ : QRect(topLeft, size), m_resized(false), m_selected(false),
+ m_needUpdate(false), m_brush(DefaultBrushColor), m_pen(DefaultPenColor), m_z(0) {};
+ CompositionRect(int left, int top, int width, int height)
+ : QRect(left, top, width, height), m_resized(false), m_selected(false),
+ m_needUpdate(false), m_brush(DefaultBrushColor), m_pen(DefaultPenColor), m_z(0) {};
+
+ void setResized(bool s) { m_resized = s; }
+ bool isResized() const { return m_resized; }
+ void setSelected(bool s) { m_selected = s; }
+ bool isSelected() const { return m_selected; }
+ bool needsFullUpdate() const { return m_needUpdate; }
+ void setNeedsFullUpdate(bool s) { m_needUpdate = s; }
+
+ void setZ(int z) { m_z = z; }
+ int z() const { return m_z; }
+
+ // brush, pen draw info
+ void setBrush(QBrush b) { m_brush = b; }
+ QBrush getBrush() const { return m_brush; }
+ void setPen(QPen b) { m_pen = b; }
+ QPen getPen() const { return m_pen; }
+
+ // repeating segments
+ void setRepeatMarks(const repeatmarks& rm) { m_repeatMarks = rm; }
+ const repeatmarks& getRepeatMarks() const { return m_repeatMarks; }
+ bool isRepeating() const { return m_repeatMarks.size() > 0; }
+ int getBaseWidth() const { return m_baseWidth; }
+ void setBaseWidth(int bw) { m_baseWidth = bw; }
+ QString getLabel() const { return m_label; }
+ void setLabel(QString l) { m_label = l; }
+
+ static const QColor DefaultPenColor;
+ static const QColor DefaultBrushColor;
+
+protected:
+ bool m_resized;
+ bool m_selected;
+ bool m_needUpdate;
+ QBrush m_brush;
+ QPen m_pen;
+ repeatmarks m_repeatMarks;
+ int m_baseWidth;
+ QString m_label;
+ int m_z;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionView.cpp b/src/gui/editors/segment/segmentcanvas/CompositionView.cpp
new file mode 100644
index 0000000..8e83a6b
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionView.cpp
@@ -0,0 +1,1591 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "CompositionView.h"
+
+#include "misc/Debug.h"
+#include "AudioPreviewThread.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/SnapGrid.h"
+#include "CompositionColourCache.h"
+#include "CompositionItemHelper.h"
+#include "CompositionItemImpl.h"
+#include "CompositionModel.h"
+#include "CompositionModelImpl.h"
+#include "CompositionRect.h"
+#include "AudioPreviewPainter.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "gui/general/RosegardenScrollView.h"
+#include "SegmentSelector.h"
+#include "SegmentToolBox.h"
+#include "SegmentTool.h"
+#include <kmessagebox.h>
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qevent.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qmemarray.h>
+#include <qpainter.h>
+#include <qpen.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qscrollbar.h>
+#include <qscrollview.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qwidget.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+class PreviewRect : public QRect {
+public:
+ PreviewRect(int left, int top, int width, int height) :
+ QRect(left, top, width, height) {};
+
+ PreviewRect(const QRect& r) :
+ QRect(r) {};
+
+ const QColor& getColor() const { return m_color; }
+ void setColor(QColor c) { m_color = c; }
+
+protected:
+ QColor m_color;
+};
+
+CompositionView::CompositionView(RosegardenGUIDoc* doc,
+ CompositionModel* model,
+ QWidget * parent, const char * name, WFlags f)
+#if KDE_VERSION >= KDE_MAKE_VERSION(3,2,0)
+ : RosegardenScrollView(parent, name, f | WNoAutoErase | WStaticContents),
+#else
+ :
+ RosegardenScrollView(parent, name, f | WRepaintNoErase | WResizeNoErase | WStaticContents),
+#endif
+ m_model(model),
+ m_currentItem(0),
+ m_tool(0),
+ m_toolBox(0),
+ m_showPreviews(false),
+ m_showSegmentLabels(true),
+ m_fineGrain(false),
+ m_pencilOverExisting(false),
+ m_minWidth(m_model->getLength()),
+ m_stepSize(0),
+ m_rectFill(0xF0, 0xF0, 0xF0),
+ m_selectedRectFill(0x00, 0x00, 0xF0),
+ m_pointerPos(0),
+ m_pointerColor(GUIPalette::getColour(GUIPalette::Pointer)),
+ m_pointerWidth(4),
+ m_pointerPen(QPen(m_pointerColor, m_pointerWidth)),
+ m_tmpRect(QRect(QPoint(0, 0), QPoint( -1, -1))),
+ m_tmpRectFill(CompositionRect::DefaultBrushColor),
+ m_trackDividerColor(GUIPalette::getColour(GUIPalette::TrackDivider)),
+ m_drawGuides(false),
+ m_guideColor(GUIPalette::getColour(GUIPalette::MovementGuide)),
+ m_topGuidePos(0),
+ m_foreGuidePos(0),
+ m_drawSelectionRect(false),
+ m_drawTextFloat(false),
+ m_segmentsDrawBuffer(visibleWidth(), visibleHeight()),
+ m_artifactsDrawBuffer(visibleWidth(), visibleHeight()),
+ m_segmentsDrawBufferRefresh(0, 0, visibleWidth(), visibleHeight()),
+ m_artifactsDrawBufferRefresh(0, 0, visibleWidth(), visibleHeight()),
+ m_lastBufferRefreshX(0),
+ m_lastBufferRefreshY(0),
+ m_lastPointerRefreshX(0),
+ m_contextHelpShown(false)
+{
+ if (doc) {
+ m_toolBox = new SegmentToolBox(this, doc);
+
+ connect(m_toolBox, SIGNAL(showContextHelp(const QString &)),
+ this, SLOT(slotToolHelpChanged(const QString &)));
+ }
+
+ setDragAutoScroll(true);
+ setBackgroundMode(NoBackground);
+ viewport()->setBackgroundMode(NoBackground);
+ viewport()->setPaletteBackgroundColor(GUIPalette::getColour(GUIPalette::SegmentCanvas));
+
+ slotUpdateSize();
+
+ QScrollBar* hsb = horizontalScrollBar();
+
+ // dynamically adjust content size when scrolling past current composition's end
+ connect(hsb, SIGNAL(nextLine()),
+ this, SLOT(scrollRight()));
+ connect(hsb, SIGNAL(prevLine()),
+ this, SLOT(scrollLeft()));
+
+ // connect(this, SIGNAL(contentsMoving(int, int)),
+ // this, SLOT(slotAllDrawBuffersNeedRefresh()));
+
+ // connect(this, SIGNAL(contentsMoving(int, int)),
+ // this, SLOT(slotContentsMoving(int, int)));
+
+ connect(model, SIGNAL(needContentUpdate()),
+ this, SLOT(slotUpdateSegmentsDrawBuffer()));
+ connect(model, SIGNAL(needContentUpdate(const QRect&)),
+ this, SLOT(slotUpdateSegmentsDrawBuffer(const QRect&)));
+ connect(model, SIGNAL(needArtifactsUpdate()),
+ this, SLOT(slotArtifactsDrawBufferNeedsRefresh()));
+ connect(model, SIGNAL(needSizeUpdate()),
+ this, SLOT(slotUpdateSize()));
+
+ if (doc) {
+ connect(doc, SIGNAL(docColoursChanged()),
+ this, SLOT(slotRefreshColourCache()));
+
+ // recording-related signals
+ connect(doc, SIGNAL(newMIDIRecordingSegment(Segment*)),
+ this, SLOT(slotNewMIDIRecordingSegment(Segment*)));
+ connect(doc, SIGNAL(newAudioRecordingSegment(Segment*)),
+ this, SLOT(slotNewAudioRecordingSegment(Segment*)));
+ // connect(doc, SIGNAL(recordMIDISegmentUpdated(Segment*, timeT)),
+ // this, SLOT(slotRecordMIDISegmentUpdated(Segment*, timeT)));
+ connect(doc, SIGNAL(stoppedAudioRecording()),
+ this, SLOT(slotStoppedRecording()));
+ connect(doc, SIGNAL(stoppedMIDIRecording()),
+ this, SLOT(slotStoppedRecording()));
+ connect(doc, SIGNAL(audioFileFinalized(Segment*)),
+ getModel(), SLOT(slotAudioFileFinalized(Segment*)));
+ }
+
+ CompositionModelImpl* cmi = dynamic_cast<CompositionModelImpl*>(model);
+ if (cmi) {
+ cmi->setAudioPreviewThread(&doc->getAudioPreviewThread());
+ }
+
+ if (doc) {
+ doc->getAudioPreviewThread().setEmptyQueueListener(this);
+ }
+
+ m_segmentsDrawBuffer.setOptimization(QPixmap::BestOptim);
+ m_artifactsDrawBuffer.setOptimization(QPixmap::BestOptim);
+
+ viewport()->setMouseTracking(true);
+}
+
+void CompositionView::endAudioPreviewGeneration()
+{
+ CompositionModelImpl* cmi = dynamic_cast<CompositionModelImpl*>(m_model);
+ if (cmi) {
+ cmi->setAudioPreviewThread(0);
+ }
+}
+
+void CompositionView::setBackgroundPixmap(const QPixmap &m)
+{
+ m_backgroundPixmap = m;
+ // viewport()->setErasePixmap(m_backgroundPixmap);
+}
+
+void CompositionView::initStepSize()
+{
+ QScrollBar* hsb = horizontalScrollBar();
+ m_stepSize = hsb->lineStep();
+}
+
+void CompositionView::slotUpdateSize()
+{
+ int vStep = getModel()->grid().getYSnap();
+ int height = std::max(getModel()->getNbRows() * vStep, (unsigned)visibleHeight());
+
+ RulerScale *ruler = grid().getRulerScale();
+
+ int minWidth = sizeHint().width();
+ int computedWidth = int(nearbyint(ruler->getTotalWidth()));
+
+ int width = std::max(computedWidth, minWidth);
+
+ resizeContents(width, height);
+}
+
+void CompositionView::scrollRight()
+{
+ RG_DEBUG << "CompositionView::scrollRight()\n";
+ if (m_stepSize == 0)
+ initStepSize();
+
+ if (horizontalScrollBar()->value() == horizontalScrollBar()->maxValue()) {
+
+ resizeContents(contentsWidth() + m_stepSize, contentsHeight());
+ setContentsPos(contentsX() + m_stepSize, contentsY());
+ getModel()->setLength(contentsWidth());
+ }
+
+}
+
+void CompositionView::scrollLeft()
+{
+ RG_DEBUG << "CompositionView::scrollLeft()\n";
+ if (m_stepSize == 0)
+ initStepSize();
+
+ int cWidth = contentsWidth();
+
+ if (horizontalScrollBar()->value() < cWidth && cWidth > m_minWidth) {
+ resizeContents(cWidth - m_stepSize, contentsHeight());
+ getModel()->setLength(contentsWidth());
+ }
+
+}
+
+void CompositionView::setSelectionRectPos(const QPoint& pos)
+{
+ m_selectionRect.setRect(pos.x(), pos.y(), 0, 0);
+ getModel()->setSelectionRect(m_selectionRect);
+}
+
+void CompositionView::setSelectionRectSize(int w, int h)
+{
+ m_selectionRect.setSize(QSize(w, h));
+ getModel()->setSelectionRect(m_selectionRect);
+}
+
+void CompositionView::setDrawSelectionRect(bool d)
+{
+ if (m_drawSelectionRect != d) {
+ m_drawSelectionRect = d;
+ slotArtifactsDrawBufferNeedsRefresh();
+ slotUpdateSegmentsDrawBuffer(m_selectionRect);
+ }
+}
+
+void CompositionView::clearSegmentRectsCache(bool clearPreviews)
+{
+ dynamic_cast<CompositionModelImpl*>(getModel())->clearSegmentRectsCache(clearPreviews);
+}
+
+SegmentSelection
+CompositionView::getSelectedSegments()
+{
+ return (dynamic_cast<CompositionModelImpl*>(m_model))->getSelectedSegments();
+}
+
+void CompositionView::updateSelectionContents()
+{
+ if (!haveSelection())
+ return ;
+
+
+ QRect selectionRect = getModel()->getSelectionContentsRect();
+ updateContents(selectionRect);
+}
+
+void CompositionView::slotContentsMoving(int x, int y)
+{
+ // qDebug("contents moving : x=%d", x);
+}
+
+void CompositionView::slotSetTool(const QString& toolName)
+{
+ RG_DEBUG << "CompositionView::slotSetTool(" << toolName << ")"
+ << this << "\n";
+
+ if (m_tool)
+ m_tool->stow();
+
+ m_toolContextHelp = "";
+
+ m_tool = m_toolBox->getTool(toolName);
+
+ if (m_tool)
+ m_tool->ready();
+ else {
+ KMessageBox::error(0, QString("CompositionView::slotSetTool() : unknown tool name %1").arg(toolName));
+ }
+}
+
+void CompositionView::slotSelectSegments(const SegmentSelection &segments)
+{
+ RG_DEBUG << "CompositionView::slotSelectSegments\n";
+
+ static QRect dummy;
+
+ getModel()->clearSelected();
+
+ for (SegmentSelection::iterator i = segments.begin(); i != segments.end(); ++i) {
+ getModel()->setSelected(CompositionItem(new CompositionItemImpl(**i, dummy)));
+ }
+ slotUpdateSegmentsDrawBuffer();
+}
+
+SegmentSelector*
+CompositionView::getSegmentSelectorTool()
+{
+ return dynamic_cast<SegmentSelector*>(getToolBox()->getTool(SegmentSelector::ToolName));
+}
+
+void CompositionView::slotSetSelectAdd(bool value)
+{
+ SegmentSelector* selTool = getSegmentSelectorTool();
+
+ if (!selTool)
+ return ;
+
+ selTool->setSegmentAdd(value);
+}
+
+void CompositionView::slotSetSelectCopy(bool value)
+{
+ SegmentSelector* selTool = getSegmentSelectorTool();
+
+ if (!selTool)
+ return ;
+
+ selTool->setSegmentCopy(value);
+}
+
+void CompositionView::slotShowSplitLine(int x, int y)
+{
+ m_splitLinePos.setX(x);
+ m_splitLinePos.setY(y);
+}
+
+void CompositionView::slotHideSplitLine()
+{
+ m_splitLinePos.setX( -1);
+ m_splitLinePos.setY( -1);
+}
+
+void CompositionView::slotExternalWheelEvent(QWheelEvent* e)
+{
+ e->accept();
+ wheelEvent(e);
+}
+
+CompositionItem CompositionView::getFirstItemAt(QPoint pos)
+{
+ CompositionModel::itemcontainer items = getModel()->getItemsAt(pos);
+
+ if (items.size()) {
+ // find topmost item
+ CompositionItem res = *(items.begin());
+
+ unsigned int maxZ = res->z();
+
+ CompositionModel::itemcontainer::iterator maxZItemPos = items.begin();
+
+ for (CompositionModel::itemcontainer::iterator i = items.begin();
+ i != items.end(); ++i) {
+ CompositionItem ic = *i;
+ if (ic->z() > maxZ) {
+ RG_DEBUG << k_funcinfo << "found new topmost at z=" << ic->z() << endl;
+ res = ic;
+ maxZ = ic->z();
+ maxZItemPos = i;
+ }
+ }
+
+ // get rid of the rest;
+ items.erase(maxZItemPos);
+ for (CompositionModel::itemcontainer::iterator i = items.begin();
+ i != items.end(); ++i)
+ delete *i;
+
+ return res;
+ } else {
+ RG_DEBUG << k_funcinfo << "no item under cursor\n";
+ }
+
+
+ return CompositionItem();
+}
+
+void CompositionView::setSnapGrain(bool fine)
+{
+ if (m_fineGrain) {
+ grid().setSnapTime(SnapGrid::NoSnap);
+ } else {
+ grid().setSnapTime(fine ? SnapGrid::SnapToBeat : SnapGrid::SnapToBar);
+ }
+}
+
+void CompositionView::slotUpdateSegmentsDrawBuffer()
+{
+ // RG_DEBUG << "CompositionView::slotUpdateSegmentsDrawBuffer()\n";
+ slotAllDrawBuffersNeedRefresh();
+ updateContents();
+}
+
+void CompositionView::slotUpdateSegmentsDrawBuffer(const QRect& rect)
+{
+ // RG_DEBUG << "CompositionView::slotUpdateSegmentsDrawBuffer() rect "
+ // << rect << " - valid : " << rect.isValid() << endl;
+
+ slotAllDrawBuffersNeedRefresh(rect);
+
+ if (rect.isValid()) {
+ updateContents(rect);
+ } else {
+ updateContents();
+ }
+}
+
+void CompositionView::slotRefreshColourCache()
+{
+ CompositionColourCache::getInstance()->init();
+ clearSegmentRectsCache();
+ slotUpdateSegmentsDrawBuffer();
+}
+
+void CompositionView::slotNewMIDIRecordingSegment(Segment* s)
+{
+ getModel()->addRecordingItem(CompositionItemHelper::makeCompositionItem(s));
+}
+
+void CompositionView::slotNewAudioRecordingSegment(Segment* s)
+{
+ getModel()->addRecordingItem(CompositionItemHelper::makeCompositionItem(s));
+}
+
+void CompositionView::slotStoppedRecording()
+{
+ getModel()->clearRecordingItems();
+}
+
+void CompositionView::resizeEvent(QResizeEvent* e)
+{
+ QScrollView::resizeEvent(e);
+ slotUpdateSize();
+
+ int w = std::max(m_segmentsDrawBuffer.width(), visibleWidth());
+ int h = std::max(m_segmentsDrawBuffer.height(), visibleHeight());
+
+ m_segmentsDrawBuffer.resize(w, h);
+ m_artifactsDrawBuffer.resize(w, h);
+ slotAllDrawBuffersNeedRefresh();
+ // RG_DEBUG << "CompositionView::resizeEvent() : drawBuffer size = " << m_segmentsDrawBuffer.size() << endl;
+}
+
+void CompositionView::viewportPaintEvent(QPaintEvent* e)
+{
+ QMemArray<QRect> rects = e->region().rects();
+
+ for (unsigned int i = 0; i < rects.size(); ++i) {
+ viewportPaintRect(rects[i]);
+ }
+}
+
+void CompositionView::viewportPaintRect(QRect r)
+{
+ QRect updateRect = r;
+
+ r &= viewport()->rect();
+ r.moveBy(contentsX(), contentsY());
+
+ // RG_DEBUG << "CompositionView::viewportPaintRect() r = " << r
+ // << " - moveBy " << contentsX() << "," << contentsY() << " - updateRect = " << updateRect
+ // << " - refresh " << m_segmentsDrawBufferRefresh << " artrefresh " << m_artifactsDrawBufferRefresh << endl;
+
+
+ bool scroll = false;
+ bool changed = checkScrollAndRefreshDrawBuffer(r, scroll);
+
+ if (changed || m_artifactsDrawBufferRefresh.isValid()) {
+
+ // r was modified by checkScrollAndRefreshDrawBuffer
+ QRect copyRect(r | m_artifactsDrawBufferRefresh);
+ copyRect.moveBy( -contentsX(), -contentsY());
+
+ // RG_DEBUG << "copying from segment to artifacts buffer: " << copyRect << endl;
+
+ bitBlt(&m_artifactsDrawBuffer,
+ copyRect.x(), copyRect.y(),
+ &m_segmentsDrawBuffer,
+ copyRect.x(), copyRect.y(), copyRect.width(), copyRect.height());
+ m_artifactsDrawBufferRefresh |= r;
+ }
+
+ if (m_artifactsDrawBufferRefresh.isValid()) {
+ refreshArtifactsDrawBuffer(m_artifactsDrawBufferRefresh);
+ m_artifactsDrawBufferRefresh = QRect();
+ }
+
+ if (scroll) {
+ bitBlt(viewport(), 0, 0,
+ &m_artifactsDrawBuffer, 0, 0,
+ m_artifactsDrawBuffer.width(), m_artifactsDrawBuffer.height());
+ } else {
+ bitBlt(viewport(), updateRect.x(), updateRect.y(),
+ &m_artifactsDrawBuffer, updateRect.x(), updateRect.y(),
+ updateRect.width(), updateRect.height());
+ }
+
+ // DEBUG
+
+ // QPainter pdebug(viewport());
+ // static QPen framePen(Qt::red, 1);
+ // pdebug.setPen(framePen);
+ // pdebug.drawRect(updateRect);
+
+}
+
+bool CompositionView::checkScrollAndRefreshDrawBuffer(QRect &rect, bool& scroll)
+{
+ bool all = false;
+ QRect refreshRect = m_segmentsDrawBufferRefresh;
+
+ int w = visibleWidth(), h = visibleHeight();
+ int cx = contentsX(), cy = contentsY();
+
+ scroll = (cx != m_lastBufferRefreshX || cy != m_lastBufferRefreshY);
+
+ if (scroll) {
+
+ // RG_DEBUG << "checkScrollAndRefreshDrawBuffer: scrolling by ("
+ // << cx - m_lastBufferRefreshX << "," << cy - m_lastBufferRefreshY << ")" << endl;
+
+ if (refreshRect.isValid()) {
+
+ // If we've scrolled and there was an existing refresh
+ // rect, we can't be sure whether the refresh rect
+ // predated or postdated the internal update of scroll
+ // location. Cut our losses and refresh everything.
+
+ refreshRect.setRect(cx, cy, w, h);
+
+ } else {
+
+ // No existing refresh rect: we only need to handle the
+ // scroll
+
+ if (cx != m_lastBufferRefreshX) {
+
+ int dx = m_lastBufferRefreshX - cx;
+
+ if (dx > -w && dx < w) {
+
+ QPainter cp(&m_segmentsDrawBuffer);
+ cp.drawPixmap(dx, 0, m_segmentsDrawBuffer);
+
+ if (dx < 0) {
+ refreshRect |= QRect(cx + w + dx, cy, -dx, h);
+ } else {
+ refreshRect |= QRect(cx, cy, dx, h);
+ }
+
+ } else {
+
+ refreshRect.setRect(cx, cy, w, h);
+ all = true;
+ }
+ }
+
+ if (cy != m_lastBufferRefreshY && !all) {
+
+ int dy = m_lastBufferRefreshY - cy;
+
+ if (dy > -h && dy < h) {
+
+ QPainter cp(&m_segmentsDrawBuffer);
+ cp.drawPixmap(0, dy, m_segmentsDrawBuffer);
+
+ if (dy < 0) {
+ refreshRect |= QRect(cx, cy + h + dy, w, -dy);
+ } else {
+ refreshRect |= QRect(cx, cy, w, dy);
+ }
+
+ } else {
+
+ refreshRect.setRect(cx, cy, w, h);
+ all = true;
+ }
+ }
+ }
+ }
+
+ bool needRefresh = false;
+
+ if (refreshRect.isValid()) {
+ needRefresh = true;
+ }
+
+ if (needRefresh)
+ refreshSegmentsDrawBuffer(refreshRect);
+
+ m_segmentsDrawBufferRefresh = QRect();
+ m_lastBufferRefreshX = cx;
+ m_lastBufferRefreshY = cy;
+
+ rect |= refreshRect;
+ if (scroll)
+ rect.setRect(cx, cy, w, h);
+ return needRefresh;
+}
+
+void CompositionView::refreshSegmentsDrawBuffer(const QRect& rect)
+{
+ // Profiler profiler("CompositionView::refreshDrawBuffer", true);
+ // RG_DEBUG << "CompositionView::refreshSegmentsDrawBuffer() r = "
+ // << rect << endl;
+
+ QPainter p(&m_segmentsDrawBuffer, viewport());
+ p.translate( -contentsX(), -contentsY());
+
+ if (!m_backgroundPixmap.isNull()) {
+ QPoint pp(rect.x() % m_backgroundPixmap.height(), rect.y() % m_backgroundPixmap.width());
+ p.drawTiledPixmap(rect, m_backgroundPixmap, pp);
+ } else {
+ p.eraseRect(rect);
+ }
+
+ drawArea(&p, rect);
+
+ // DEBUG - show what's updated
+ // QPen framePen(Qt::red, 1);
+ // p.setPen(framePen);
+ // p.drawRect(rect);
+
+ // m_segmentsDrawBufferNeedsRefresh = false;
+}
+
+void CompositionView::refreshArtifactsDrawBuffer(const QRect& rect)
+{
+ // RG_DEBUG << "CompositionView::refreshArtifactsDrawBuffer() r = "
+ // << rect << endl;
+
+ QPainter p;
+ p.begin(&m_artifactsDrawBuffer, viewport());
+ p.translate( -contentsX(), -contentsY());
+ // QRect r(contentsX(), contentsY(), m_artifactsDrawBuffer.width(), m_artifactsDrawBuffer.height());
+ drawAreaArtifacts(&p, rect);
+ p.end();
+
+ // m_artifactsDrawBufferNeedsRefresh = false;
+}
+
+void CompositionView::drawArea(QPainter *p, const QRect& clipRect)
+{
+ // Profiler profiler("CompositionView::drawArea", true);
+
+ // RG_DEBUG << "CompositionView::drawArea() clipRect = " << clipRect << endl;
+
+ //
+ // Fetch track dividing lines
+ //
+ CompositionModel::heightlist lineHeights = getModel()->getTrackDividersIn(clipRect);
+
+ if (!lineHeights.empty()) {
+
+ p->save();
+ QColor light = m_trackDividerColor.light();
+ p->setPen(light);
+
+ for (CompositionModel::heightlist::const_iterator hi = lineHeights.begin();
+ hi != lineHeights.end(); ++hi) {
+ int y = *hi;
+ if (y-1 >= clipRect.y()) {
+ p->drawLine(clipRect.x(), y-1,
+ clipRect.x() + clipRect.width() - 1, y-1);
+ }
+ if (y >= clipRect.y()) {
+ p->drawLine(clipRect.x(), y,
+ clipRect.x() + clipRect.width() - 1, y);
+ }
+ }
+
+ p->setPen(m_trackDividerColor);
+
+ for (CompositionModel::heightlist::const_iterator hi = lineHeights.begin();
+ hi != lineHeights.end(); ++hi) {
+ int y = *hi;
+ if (y-2 >= clipRect.y()) {
+ p->drawLine(clipRect.x(), y-2,
+ clipRect.x() + clipRect.width() - 1, y-2);
+ }
+ if (y+1 >= clipRect.y()) {
+ p->drawLine(clipRect.x(), y+1,
+ clipRect.x() + clipRect.width() - 1, y+1);
+ }
+ }
+
+ p->restore();
+ }
+
+ CompositionModel::AudioPreviewDrawData* audioPreviewData = 0;
+ CompositionModel::RectRanges* notationPreviewData = 0;
+
+ //
+ // Fetch previews
+ //
+ if (m_showPreviews) {
+ notationPreviewData = &m_notationPreviewRects;
+ m_notationPreviewRects.clear();
+ audioPreviewData = &m_audioPreviewRects;
+ m_audioPreviewRects.clear();
+ }
+
+ //
+ // Fetch segment rectangles to draw
+ //
+ const CompositionModel::rectcontainer& rects = getModel()->getRectanglesIn(clipRect,
+ notationPreviewData, audioPreviewData);
+ CompositionModel::rectcontainer::const_iterator i = rects.begin();
+ CompositionModel::rectcontainer::const_iterator end = rects.end();
+
+ //
+ // Draw Segment Rectangles
+ //
+ p->save();
+ for (; i != end; ++i) {
+ p->setBrush(i->getBrush());
+ p->setPen(i->getPen());
+
+ // RG_DEBUG << "CompositionView::drawArea : draw comp rect " << *i << endl;
+ drawCompRect(*i, p, clipRect);
+ }
+
+ p->restore();
+
+ if (rects.size() > 1) {
+ // RG_DEBUG << "CompositionView::drawArea : drawing intersections\n";
+ drawIntersections(rects, p, clipRect);
+ }
+
+ //
+ // Previews
+ //
+ if (m_showPreviews) {
+ p->save();
+
+ // draw audio previews
+ //
+ drawAreaAudioPreviews(p, clipRect);
+
+ // draw notation previews
+ //
+ CompositionModel::RectRanges::const_iterator npi = m_notationPreviewRects.begin();
+ CompositionModel::RectRanges::const_iterator npEnd = m_notationPreviewRects.end();
+
+ for (; npi != npEnd; ++npi) {
+ CompositionModel::RectRange interval = *npi;
+ p->save();
+ p->translate(interval.basePoint.x(), interval.basePoint.y());
+ // RG_DEBUG << "CompositionView::drawArea : translating to x = " << interval.basePoint.x() << endl;
+ for (; interval.range.first != interval.range.second; ++interval.range.first) {
+
+ const PreviewRect& pr = *(interval.range.first);
+ QColor defaultCol = CompositionColourCache::getInstance()->SegmentInternalPreview;
+ QColor col = interval.color.isValid() ? interval.color : defaultCol;
+ p->setBrush(col);
+ p->setPen(col);
+ // RG_DEBUG << "CompositionView::drawArea : drawing preview rect at x = " << pr.x() << endl;
+ p->drawRect(pr);
+ }
+ p->restore();
+ }
+
+ p->restore();
+ }
+
+ //
+ // Draw segment labels (they must be drawn over the preview rects)
+ //
+ if (m_showSegmentLabels) {
+ for (i = rects.begin(); i != end; ++i) {
+ drawCompRectLabel(*i, p, clipRect);
+ }
+ }
+
+ // drawAreaArtifacts(p, clipRect);
+
+}
+
+void CompositionView::drawAreaAudioPreviews(QPainter * p, const QRect& clipRect)
+{
+ CompositionModel::AudioPreviewDrawData::const_iterator api = m_audioPreviewRects.begin();
+ CompositionModel::AudioPreviewDrawData::const_iterator apEnd = m_audioPreviewRects.end();
+ QRect rectToFill, // rect to fill on canvas
+ localRect; // the rect of the tile to draw on the canvas
+ QPoint basePoint, // origin of segment rect
+ drawBasePoint; // origin of rect to fill on canvas
+ QRect r;
+ for (; api != apEnd; ++api) {
+ rectToFill = api->rect;
+ basePoint = api->basePoint;
+ rectToFill.moveTopLeft(basePoint);
+ rectToFill &= clipRect;
+ r = rectToFill;
+ drawBasePoint = rectToFill.topLeft();
+ rectToFill.moveBy( -basePoint.x(), -basePoint.y());
+ int firstPixmapIdx = (r.x() - basePoint.x()) / AudioPreviewPainter::tileWidth();
+ if (firstPixmapIdx >= api->pixmap.size()) {
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : WARNING - miscomputed pixmap array : r.x = "
+ // << r.x() << " - basePoint.x = " << basePoint.x() << " - firstPixmapIdx = " << firstPixmapIdx
+ // << endl;
+ continue;
+ }
+ int x = 0, idx = firstPixmapIdx;
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : clipRect = " << clipRect
+ // << " - firstPixmapIdx = " << firstPixmapIdx << endl;
+ while (x < clipRect.width()) {
+ int pixmapRectXOffset = idx * AudioPreviewPainter::tileWidth();
+ localRect.setRect(basePoint.x() + pixmapRectXOffset, basePoint.y(),
+ AudioPreviewPainter::tileWidth(), api->rect.height());
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : initial localRect = "
+ // << localRect << endl;
+ localRect &= r;
+ if (idx == firstPixmapIdx && api->resizeOffset != 0) {
+ // this segment is being resized from start, clip beginning of preview
+ localRect.moveBy(api->resizeOffset, 0);
+ }
+
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : localRect & clipRect = "
+ // << localRect << endl;
+ if (localRect.isEmpty()) {
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : localRect & clipRect is empty\n";
+ break;
+ }
+ localRect.moveBy( -(basePoint.x() + pixmapRectXOffset), -basePoint.y());
+
+ // RG_DEBUG << "CompositionView::drawAreaAudioPreviews : drawing pixmap "
+ // << idx << " at " << drawBasePoint << " - localRect = " << localRect
+ // << " - preResizeOrigin : " << api->preResizeOrigin << endl;
+
+ p->drawImage(drawBasePoint, api->pixmap[idx], localRect,
+ Qt::ColorOnly | Qt::ThresholdDither | Qt::AvoidDither);
+
+ ++idx;
+ if (idx >= api->pixmap.size())
+ break;
+ drawBasePoint.setX(drawBasePoint.x() + localRect.width());
+ x += localRect.width();
+ }
+ }
+}
+
+void CompositionView::drawAreaArtifacts(QPainter * p, const QRect& clipRect)
+{
+ //
+ // Playback Pointer
+ //
+ drawPointer(p, clipRect);
+
+ //
+ // Tmp rect (rect displayed while drawing a new segment)
+ //
+ if (m_tmpRect.isValid() && m_tmpRect.intersects(clipRect)) {
+ p->setBrush(m_tmpRectFill);
+ p->setPen(CompositionColourCache::getInstance()->SegmentBorder);
+ drawRect(m_tmpRect, p, clipRect);
+ }
+
+ //
+ // Tool guides (crosshairs)
+ //
+ if (m_drawGuides)
+ drawGuides(p, clipRect);
+
+ //
+ // Selection Rect
+ //
+ if (m_drawSelectionRect) {
+ drawRect(m_selectionRect, p, clipRect, false, 0, false);
+ }
+
+ //
+ // Floating Text
+ //
+ if (m_drawTextFloat)
+ drawTextFloat(p, clipRect);
+
+ //
+ // Split line
+ //
+ if (m_splitLinePos.x() > 0 && clipRect.contains(m_splitLinePos)) {
+ p->save();
+ p->setPen(m_guideColor);
+ p->drawLine(m_splitLinePos.x(), m_splitLinePos.y(),
+ m_splitLinePos.x(), m_splitLinePos.y() + getModel()->grid().getYSnap());
+ p->restore();
+ }
+}
+
+void CompositionView::drawGuides(QPainter * p, const QRect& /*clipRect*/)
+{
+ // no need to check for clipping, these guides are meant to follow the mouse cursor
+ QPoint guideOrig(m_topGuidePos, m_foreGuidePos);
+
+ p->save();
+ p->setPen(m_guideColor);
+ p->drawLine(guideOrig.x(), 0, guideOrig.x(), contentsHeight());
+ p->drawLine(0, guideOrig.y(), contentsWidth(), guideOrig.y());
+ p->restore();
+}
+
+void CompositionView::drawCompRect(const CompositionRect& r, QPainter *p, const QRect& clipRect,
+ int intersectLvl, bool fill)
+{
+ p->save();
+
+ QBrush brush = r.getBrush();
+
+ if (r.isRepeating()) {
+ QColor brushColor = brush.color();
+ brush.setColor(brushColor.light(150));
+ }
+
+ p->setBrush(brush);
+ p->setPen(r.getPen());
+ drawRect(r, p, clipRect, r.isSelected(), intersectLvl, fill);
+
+ if (r.isRepeating()) {
+
+ CompositionRect::repeatmarks repeatMarks = r.getRepeatMarks();
+
+ // RG_DEBUG << "CompositionView::drawCompRect() : drawing repeating rect " << r
+ // << " nb repeat marks = " << repeatMarks.size() << endl;
+
+ // draw 'start' rectangle with original brush
+ //
+ QRect startRect = r;
+ startRect.setWidth(repeatMarks[0] - r.x());
+ p->setBrush(r.getBrush());
+ drawRect(startRect, p, clipRect, r.isSelected(), intersectLvl, fill);
+
+
+ // now draw the 'repeat' marks
+ //
+ p->setPen(CompositionColourCache::getInstance()->RepeatSegmentBorder);
+ int penWidth = std::max(r.getPen().width(), 1u);
+
+ for (unsigned int i = 0; i < repeatMarks.size(); ++i) {
+ int pos = repeatMarks[i];
+ if (pos > clipRect.right())
+ break;
+
+ if (pos >= clipRect.left()) {
+ QPoint p1(pos, r.y() + penWidth),
+ p2(pos, r.y() + r.height() - penWidth - 1);
+
+ // RG_DEBUG << "CompositionView::drawCompRect() : drawing repeat mark at "
+ // << p1 << "-" << p2 << endl;
+ p->drawLine(p1, p2);
+ }
+
+ }
+
+ }
+
+ p->restore();
+}
+
+void CompositionView::drawCompRectLabel(const CompositionRect& r, QPainter *p, const QRect& clipRect)
+{
+ // draw segment label
+ //
+#ifdef NOT_DEFINED
+ if (!r.getLabel().isEmpty() /* && !r.isSelected() */)
+ {
+ p->save();
+ p->setPen(GUIPalette::getColour(GUIPalette::SegmentLabel));
+ p->setBrush(white);
+ QRect textRect(r);
+ textRect.setX(textRect.x() + 3);
+ QString label = " " + r.getLabel() + " ";
+ QRect textBoundingRect = p->boundingRect(textRect, Qt::AlignLeft | Qt::AlignVCenter, label);
+ p->drawRect(textBoundingRect & r);
+ p->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, label);
+ p->restore();
+ }
+#else
+ if (!r.getLabel().isEmpty()) {
+
+ p->save();
+
+ QFont font;
+ font.setPixelSize(r.height() / 2.2);
+ font.setWeight(QFont::Bold);
+ font.setItalic(false);
+ p->setFont(font);
+
+ QRect labelRect = QRect
+ (r.x(),
+ r.y() + ((r.height() - p->fontMetrics().height()) / 2) + 1,
+ r.width(),
+ p->fontMetrics().height());
+
+ int x = labelRect.x() + p->fontMetrics().width('x');
+ int y = labelRect.y();
+
+ QBrush brush = r.getBrush();
+ QColor surroundColour = brush.color().light(110);
+
+ int h, s, v;
+ surroundColour.hsv(&h, &s, &v);
+ if (v < 150)
+ surroundColour.setHsv(h, s, 225);
+ p->setPen(surroundColour);
+
+ for (int i = 0; i < 9; ++i) {
+
+ if (i == 4)
+ continue;
+
+ int wx = x, wy = y;
+
+ if (i < 3)
+ --wx;
+ if (i > 5)
+ ++wx;
+ if (i % 3 == 0)
+ --wy;
+ if (i % 3 == 2)
+ ++wy;
+
+ labelRect.setX(wx);
+ labelRect.setY(wy);
+
+ p->drawText(labelRect,
+ Qt::AlignLeft | Qt::AlignTop,
+ r.getLabel());
+ }
+
+ labelRect.setX(x);
+ labelRect.setY(y);
+
+ p->setPen(GUIPalette::getColour
+ (GUIPalette::SegmentLabel));
+ p->drawText(labelRect,
+ Qt::AlignLeft | Qt::AlignVCenter, r.getLabel());
+ p->restore();
+ }
+#endif
+}
+
+void CompositionView::drawRect(const QRect& r, QPainter *p, const QRect& clipRect,
+ bool isSelected, int intersectLvl, bool fill)
+{
+ // RG_DEBUG << "CompositionView::drawRect : intersectLvl = " << intersectLvl
+ // << " - brush col = " << p->brush().color() << endl;
+
+ // RG_DEBUG << "CompositionView::drawRect " << r << " - xformed : " << p->xForm(r)
+ // << " - contents x = " << contentsX() << ", contents y = " << contentsY() << endl;
+
+ p->save();
+
+ QRect rect = r;
+
+ if (fill) {
+ if (isSelected) {
+ QColor fillColor = p->brush().color();
+ fillColor = fillColor.dark(200);
+ QBrush b = p->brush();
+ b.setColor(fillColor);
+ p->setBrush(b);
+ // RG_DEBUG << "CompositionView::drawRect : selected color : " << fillColor << endl;
+ }
+
+ if (intersectLvl > 0) {
+ QColor fillColor = p->brush().color();
+ fillColor = fillColor.dark((intersectLvl) * 105);
+ QBrush b = p->brush();
+ b.setColor(fillColor);
+ p->setBrush(b);
+ // RG_DEBUG << "CompositionView::drawRect : intersected color : " << fillColor << " isSelected : " << isSelected << endl;
+ }
+ } else {
+ p->setBrush(Qt::NoBrush);
+ }
+
+ // Paint using the small coordinates...
+ QRect intersection = rect.intersect(clipRect);
+
+ if (clipRect.contains(rect)) {
+ p->drawRect(rect);
+ } else {
+ // draw only what's necessary
+ if (!intersection.isEmpty() && fill)
+ p->fillRect(intersection, p->brush());
+
+ int rectTopY = rect.y();
+
+ if (rectTopY >= clipRect.y() &&
+ rectTopY <= (clipRect.y() + clipRect.height())) {
+ // to prevent overflow, in case the original rect is too wide
+ // the line would be drawn "backwards"
+ p->drawLine(intersection.topLeft(), intersection.topRight());
+ }
+
+ int rectBottomY = rect.y() + rect.height();
+ if (rectBottomY >= clipRect.y() &&
+ rectBottomY <= (clipRect.y() + clipRect.height()))
+ // to prevent overflow, in case the original rect is too wide
+ // the line would be drawn "backwards"
+ p->drawLine(intersection.bottomLeft(), intersection.bottomRight());
+
+ int rectLeftX = rect.x();
+ if (rectLeftX >= clipRect.x() &&
+ rectLeftX <= (clipRect.x() + clipRect.width()))
+ p->drawLine(rect.topLeft(), rect.bottomLeft());
+
+ unsigned int rectRightX = rect.x() + rect.width(); // make sure we don't overflow
+ if (rectRightX >= unsigned(clipRect.x()) &&
+ rectRightX <= unsigned(clipRect.x() + clipRect.width()))
+ p->drawLine(rect.topRight(), rect.bottomRight());
+
+ }
+
+ p->restore();
+}
+
+QColor CompositionView::mixBrushes(QBrush a, QBrush b)
+{
+ QColor ac = a.color(), bc = b.color();
+
+ int aR = ac.red(), aG = ac.green(), aB = ac.blue(),
+ bR = bc.red(), bG = bc.green(), bB = ac.blue();
+
+ ac.setRgb((aR + bR) / 2, (aG + bG) / 2, (aB + bB) / 2);
+
+ return ac;
+}
+
+void CompositionView::drawIntersections(const CompositionModel::rectcontainer& rects,
+ QPainter * p, const QRect& clipRect)
+{
+ if (! (rects.size() > 1))
+ return ;
+
+ CompositionModel::rectcontainer intersections;
+
+ CompositionModel::rectcontainer::const_iterator i = rects.begin(),
+ j = rects.begin();
+
+ for (; j != rects.end(); ++j) {
+
+ CompositionRect testRect = *j;
+ i = j;
+ ++i; // set i to pos after j
+
+ if (i == rects.end())
+ break;
+
+ for (; i != rects.end(); ++i) {
+ CompositionRect ri = testRect.intersect(*i);
+ if (!ri.isEmpty()) {
+ CompositionModel::rectcontainer::iterator t = std::find(intersections.begin(),
+ intersections.end(), ri);
+ if (t == intersections.end()) {
+ ri.setBrush(mixBrushes(testRect.getBrush(), i->getBrush()));
+ ri.setSelected(testRect.isSelected() || i->isSelected());
+ intersections.push_back(ri);
+ }
+
+ }
+ }
+ }
+
+ //
+ // draw this level of intersections then compute and draw further ones
+ //
+ int intersectionLvl = 1;
+
+ while (!intersections.empty()) {
+
+ for (CompositionModel::rectcontainer::iterator intIter = intersections.begin();
+ intIter != intersections.end(); ++intIter) {
+ CompositionRect r = *intIter;
+ drawCompRect(r, p, clipRect, intersectionLvl);
+ }
+
+ if (intersections.size() > 10)
+ break; // put a limit on how many intersections we can compute and draw - this grows exponentially
+
+ ++intersectionLvl;
+
+ CompositionModel::rectcontainer intersections2;
+
+ CompositionModel::rectcontainer::iterator i = intersections.begin(),
+ j = intersections.begin();
+
+ for (; j != intersections.end(); ++j) {
+
+ CompositionRect testRect = *j;
+ i = j;
+ ++i; // set i to pos after j
+
+ if (i == intersections.end())
+ break;
+
+ for (; i != intersections.end(); ++i) {
+ CompositionRect ri = testRect.intersect(*i);
+ if (!ri.isEmpty() && ri != *i) {
+ CompositionModel::rectcontainer::iterator t = std::find(intersections2.begin(),
+ intersections2.end(), ri);
+ if (t == intersections2.end())
+ ri.setBrush(mixBrushes(testRect.getBrush(), i->getBrush()));
+ intersections2.push_back(ri);
+ }
+ }
+ }
+
+ intersections = intersections2;
+ }
+
+}
+
+void CompositionView::drawPointer(QPainter *p, const QRect& clipRect)
+{
+ // RG_DEBUG << "CompositionView::drawPointer: clipRect "
+ // << clipRect.x() << "," << clipRect.y() << " " << clipRect.width()
+ // << "x" << clipRect.height() << " pointer pos is " << m_pointerPos << endl;
+
+ if (m_pointerPos >= clipRect.x() && m_pointerPos <= (clipRect.x() + clipRect.width())) {
+ p->save();
+ p->setPen(m_pointerPen);
+ p->drawLine(m_pointerPos, clipRect.y(), m_pointerPos, clipRect.y() + clipRect.height());
+ p->restore();
+ }
+
+}
+
+void CompositionView::drawTextFloat(QPainter *p, const QRect& clipRect)
+{
+ QFontMetrics metrics(p->fontMetrics());
+
+ QRect bound = p->boundingRect(0, 0, 300, metrics.height() + 6, AlignAuto, m_textFloatText);
+
+ p->save();
+
+ bound.setLeft(bound.left() - 2);
+ bound.setRight(bound.right() + 2);
+ bound.setTop(bound.top() - 2);
+ bound.setBottom(bound.bottom() + 2);
+
+ QPoint pos(m_textFloatPos);
+ if (pos.y() < 0 && getModel()) {
+ if (pos.y() + bound.height() < 0) {
+ pos.setY(pos.y() + getModel()->grid().getYSnap() * 3);
+ } else {
+ pos.setY(pos.y() + getModel()->grid().getYSnap() * 2);
+ }
+ }
+
+ bound.moveTopLeft(pos);
+
+ if (bound.intersects(clipRect)) {
+
+ p->setBrush(CompositionColourCache::getInstance()->RotaryFloatBackground);
+
+ drawRect(bound, p, clipRect, false, 0, true);
+
+ p->setPen(CompositionColourCache::getInstance()->RotaryFloatForeground);
+
+ p->drawText(pos.x() + 2, pos.y() + 3 + metrics.ascent(), m_textFloatText);
+
+ }
+
+ p->restore();
+}
+
+bool CompositionView::event(QEvent* e)
+{
+ if (e->type() == AudioPreviewThread::AudioPreviewQueueEmpty) {
+ RG_DEBUG << "CompositionView::event - AudioPreviewQueueEmpty\n";
+ slotSegmentsDrawBufferNeedsRefresh();
+ viewport()->update();
+ return true;
+ }
+
+ return RosegardenScrollView::event(e);
+}
+
+void CompositionView::enterEvent(QEvent *e)
+{
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ if (!kapp->config()->readBoolEntry("toolcontexthelp", true)) return;
+
+ emit showContextHelp(m_toolContextHelp);
+ m_contextHelpShown = true;
+}
+
+void CompositionView::leaveEvent(QEvent *e)
+{
+ emit showContextHelp("");
+ m_contextHelpShown = false;
+}
+
+void CompositionView::slotToolHelpChanged(const QString &text)
+{
+ if (m_toolContextHelp == text) return;
+ m_toolContextHelp = text;
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ if (!kapp->config()->readBoolEntry("toolcontexthelp", true)) return;
+
+ if (m_contextHelpShown) emit showContextHelp(text);
+}
+
+void CompositionView::contentsMousePressEvent(QMouseEvent* e)
+{
+ Qt::ButtonState bs = e->state();
+ slotSetSelectCopy((bs & Qt::ControlButton) != 0);
+ slotSetSelectAdd((bs & Qt::ShiftButton) != 0);
+ slotSetFineGrain((bs & Qt::ShiftButton) != 0);
+ slotSetPencilOverExisting((bs & Qt::AltButton + Qt::ControlButton) != 0);
+
+ switch (e->button()) {
+ case LeftButton:
+ case MidButton:
+ startAutoScroll();
+
+ if (m_tool)
+ m_tool->handleMouseButtonPress(e);
+ else
+ RG_DEBUG << "CompositionView::contentsMousePressEvent() :"
+ << this << " no tool\n";
+ break;
+ case RightButton:
+ if (m_tool)
+ m_tool->handleRightButtonPress(e);
+ else
+ RG_DEBUG << "CompositionView::contentsMousePressEvent() :"
+ << this << " no tool\n";
+ break;
+ default:
+ break;
+ }
+}
+
+void CompositionView::contentsMouseReleaseEvent(QMouseEvent* e)
+{
+ RG_DEBUG << "CompositionView::contentsMouseReleaseEvent()\n";
+
+ stopAutoScroll();
+
+ if (!m_tool)
+ return ;
+
+ if (e->button() == LeftButton ||
+ e->button() == MidButton )
+ m_tool->handleMouseButtonRelease(e);
+}
+
+void CompositionView::contentsMouseDoubleClickEvent(QMouseEvent* e)
+{
+ m_currentItem = getFirstItemAt(e->pos());
+
+ if (!m_currentItem) {
+ RG_DEBUG << "CompositionView::contentsMouseDoubleClickEvent - no currentItem\n";
+ RulerScale *ruler = grid().getRulerScale();
+ if (ruler) emit setPointerPosition(ruler->getTimeForX(e->pos().x()));
+ return ;
+ }
+
+ RG_DEBUG << "CompositionView::contentsMouseDoubleClickEvent - have currentItem\n";
+
+ CompositionItemImpl* itemImpl = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)m_currentItem);
+
+ if (m_currentItem->isRepeating()) {
+ timeT time = getModel()->getRepeatTimeAt(e->pos(), m_currentItem);
+
+ RG_DEBUG << "editRepeat at time " << time << endl;
+ if (time > 0)
+ emit editRepeat(itemImpl->getSegment(), time);
+ else
+ emit editSegment(itemImpl->getSegment());
+
+ } else {
+
+ emit editSegment(itemImpl->getSegment());
+ }
+}
+
+void CompositionView::contentsMouseMoveEvent(QMouseEvent* e)
+{
+ if (!m_tool)
+ return ;
+
+ Qt::ButtonState bs = e->state();
+ slotSetFineGrain((bs & Qt::ShiftButton) != 0);
+ slotSetPencilOverExisting((bs & Qt::AltButton) != 0);
+
+ int follow = m_tool->handleMouseMove(e);
+ setScrollDirectionConstraint(follow);
+
+ if (follow != RosegardenCanvasView::NoFollow) {
+ doAutoScroll();
+
+ if (follow & RosegardenCanvasView::FollowHorizontal) {
+ slotScrollHorizSmallSteps(e->pos().x());
+
+ // enlarge composition if needed
+ if (horizontalScrollBar()->value() == horizontalScrollBar()->maxValue()) {
+ resizeContents(contentsWidth() + m_stepSize, contentsHeight());
+ setContentsPos(contentsX() + m_stepSize, contentsY());
+ getModel()->setLength(contentsWidth());
+ slotUpdateSize();
+ }
+ }
+
+ if (follow & RosegardenCanvasView::FollowVertical)
+ slotScrollVertSmallSteps(e->pos().y());
+ }
+}
+
+void CompositionView::releaseCurrentItem()
+{
+ m_currentItem = CompositionItem();
+}
+
+void CompositionView::setPointerPos(int pos)
+{
+ // RG_DEBUG << "CompositionView::setPointerPos(" << pos << ")\n";
+ int oldPos = m_pointerPos;
+ if (oldPos == pos)
+ return ;
+
+ m_pointerPos = pos;
+ getModel()->setPointerPos(pos);
+
+ // automagically grow contents width if pointer position goes beyond right end
+ //
+ if (pos >= (contentsWidth() - m_stepSize)) {
+ resizeContents(pos + m_stepSize, contentsHeight());
+ // grow composition too, if needed (it may not be the case if
+ if (getModel()->getLength() < contentsWidth())
+ getModel()->setLength(contentsWidth());
+ }
+
+
+ // interesting -- isAutoScrolling() never seems to return true?
+ // RG_DEBUG << "CompositionView::setPointerPos(" << pos << "), isAutoScrolling " << isAutoScrolling() << ", contentsX " << contentsX() << ", m_lastPointerRefreshX " << m_lastPointerRefreshX << ", contentsHeight " << contentsHeight() << endl;
+
+ if (contentsX() != m_lastPointerRefreshX) {
+ m_lastPointerRefreshX = contentsX();
+ // We'll need to shift the whole canvas anyway, so
+ slotArtifactsDrawBufferNeedsRefresh();
+ return ;
+ }
+
+ int deltaW = abs(m_pointerPos - oldPos);
+
+ if (deltaW <= m_pointerPen.width() * 2) { // use one rect instead of two separate ones
+
+ QRect updateRect
+ (std::min(m_pointerPos, oldPos) - m_pointerPen.width(), 0,
+ deltaW + m_pointerPen.width() * 2, contentsHeight());
+
+ slotArtifactsDrawBufferNeedsRefresh(updateRect);
+
+ } else {
+
+ slotArtifactsDrawBufferNeedsRefresh
+ (QRect(m_pointerPos - m_pointerPen.width(), 0,
+ m_pointerPen.width() * 2, contentsHeight()));
+
+ slotArtifactsDrawBufferNeedsRefresh
+ (QRect(oldPos - m_pointerPen.width(), 0,
+ m_pointerPen.width() * 2, contentsHeight()));
+ }
+}
+
+void CompositionView::setGuidesPos(int x, int y)
+{
+ m_topGuidePos = x;
+ m_foreGuidePos = y;
+ slotArtifactsDrawBufferNeedsRefresh();
+}
+
+void CompositionView::setGuidesPos(const QPoint& p)
+{
+ m_topGuidePos = p.x();
+ m_foreGuidePos = p.y();
+ slotArtifactsDrawBufferNeedsRefresh();
+}
+
+void CompositionView::setDrawGuides(bool d)
+{
+ m_drawGuides = d;
+ slotArtifactsDrawBufferNeedsRefresh();
+}
+
+void CompositionView::setTmpRect(const QRect& r)
+{
+ setTmpRect(r, m_tmpRectFill);
+}
+
+void CompositionView::setTmpRect(const QRect& r, const QColor &c)
+{
+ QRect pRect = m_tmpRect;
+ m_tmpRect = r;
+ m_tmpRectFill = c;
+ slotUpdateSegmentsDrawBuffer(m_tmpRect | pRect);
+}
+
+void CompositionView::setTextFloat(int x, int y, const QString &text)
+{
+ m_textFloatPos.setX(x);
+ m_textFloatPos.setY(y);
+ m_textFloatText = text;
+ m_drawTextFloat = true;
+ slotArtifactsDrawBufferNeedsRefresh();
+
+ // most of the time when the floating text is drawn
+ // we want to update a larger part of the view
+ // so don't update here
+ // QRect r = fontMetrics().boundingRect(x, y, 300, 40, AlignAuto, m_textFloatText);
+ // slotUpdateSegmentsDrawBuffer(r);
+
+
+ // rgapp->slotSetStatusMessage(text);
+}
+
+void CompositionView::slotSetFineGrain(bool value)
+{
+ m_fineGrain = value;
+}
+
+void CompositionView::slotSetPencilOverExisting(bool value)
+{
+ m_pencilOverExisting = value;
+}
+
+void
+CompositionView::slotTextFloatTimeout()
+{
+ hideTextFloat();
+ slotArtifactsDrawBufferNeedsRefresh();
+ // rgapp->slotSetStatusMessage(QString::null);
+}
+
+}
+#include "CompositionView.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/CompositionView.h b/src/gui/editors/segment/segmentcanvas/CompositionView.h
new file mode 100644
index 0000000..ff0d440
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/CompositionView.h
@@ -0,0 +1,366 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_COMPOSITIONVIEW_H_
+#define _RG_COMPOSITIONVIEW_H_
+
+#include "base/Selection.h"
+#include "CompositionModel.h"
+#include "CompositionItem.h"
+#include "gui/general/RosegardenScrollView.h"
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QWheelEvent;
+class QResizeEvent;
+class QPaintEvent;
+class QPainter;
+class QMouseEvent;
+class QEvent;
+
+
+namespace Rosegarden
+{
+
+class SnapGrid;
+class SegmentToolBox;
+class SegmentTool;
+class SegmentSelector;
+class Segment;
+class RosegardenGUIDoc;
+class CompositionRect;
+
+
+class CompositionView : public RosegardenScrollView
+{
+ Q_OBJECT
+public:
+ CompositionView(RosegardenGUIDoc*, CompositionModel*,
+ QWidget * parent=0, const char* name=0, WFlags f=0);
+
+ void setPointerPos(int pos);
+ int getPointerPos() { return m_pointerPos; }
+
+ void setGuidesPos(int x, int y);
+ void setGuidesPos(const QPoint& p);
+ void setDrawGuides(bool d);
+
+ QRect getSelectionRect() const { return m_selectionRect; }
+ void setSelectionRectPos(const QPoint& pos);
+ void setSelectionRectSize(int w, int h);
+ void setDrawSelectionRect(bool d);
+
+ SnapGrid& grid() { return m_model->grid(); }
+
+ CompositionItem getFirstItemAt(QPoint pos);
+
+ SegmentToolBox* getToolBox() { return m_toolBox; }
+
+ CompositionModel* getModel() { return m_model; }
+
+ void setTmpRect(const QRect& r);
+ void setTmpRect(const QRect& r, const QColor &c);
+ const QRect& getTmpRect() const { return m_tmpRect; }
+
+ /**
+ * Set the snap resolution of the grid to something suitable.
+ *
+ * fineTool indicates whether the current tool is a fine-grain sort
+ * (such as the resize or move tools) or a coarse one (such as the
+ * segment creation pencil). If the user is requesting extra-fine
+ * resolution (through the setFineGrain method) that will also be
+ * taken into account.
+ */
+ void setSnapGrain(bool fine);
+
+ /**
+ * Find out whether the user is requesting extra-fine resolution
+ * (e.g. by holding Shift key). This is seldom necessary -- most
+ * client code will only need to query the snap grid that is
+ * adjusted appropriately by the view when interactions take
+ * place.
+ */
+ bool isFineGrain() const { return m_fineGrain; }
+
+ /**
+ * Find out whether the user is requesting to draw over an existing segment
+ * with the pencil, by holding the Ctrl key. This is used by the segment
+ * pencil to decide whether to abort or not if a user attempts to draw over
+ * an existing segment, and this is all necessary in order to avoid breaking
+ * the double-click-to-open behavior.
+ */
+ bool pencilOverExisting() const { return m_pencilOverExisting; }
+
+ /**
+ * Set whether the segment items contain previews or not
+ */
+ void setShowPreviews(bool previews) { m_showPreviews = previews; }
+
+ /**
+ * Return whether the segment items contain previews or not
+ */
+ bool isShowingPreviews() { return m_showPreviews; }
+
+ /**
+ * clear all seg rect cache
+ */
+ void clearSegmentRectsCache(bool clearPreviews = false);
+
+ /// Return the selected Segments if we're currently using a "Selector"
+ SegmentSelection getSelectedSegments();
+
+ bool haveSelection() const { return m_model->haveSelection(); }
+
+ void updateSelectionContents();
+
+ /**
+ * Set and hide a text float on this canvas - it can contain
+ * anything and can be left to timeout or you can hide it
+ * explicitly.
+ *
+ */
+ void setTextFloat(int x, int y, const QString &text);
+ void hideTextFloat() { m_drawTextFloat = false; }
+
+ void setShowSegmentLabels(bool b) { m_showSegmentLabels = b; }
+
+ void setBackgroundPixmap(const QPixmap &m);
+
+ void endAudioPreviewGeneration();
+
+public slots:
+ void scrollRight();
+ void scrollLeft();
+ void slotContentsMoving(int x, int y);
+
+ /// Set the current segment editing tool
+ void slotSetTool(const QString& toolName);
+
+ // This method only operates if we're of the "Selector"
+ // tool type - it's called from the View to enable it
+ // to automatically set the selection of Segments (say
+ // by Track).
+ //
+ void slotSelectSegments(const SegmentSelection &segment);
+
+ // These are sent from the top level app when it gets key
+ // depresses relating to selection add (usually SHIFT) and
+ // selection copy (usually CONTROL)
+ //
+ void slotSetSelectAdd(bool value);
+ void slotSetSelectCopy(bool value);
+
+ void slotSetFineGrain(bool value);
+ void slotSetPencilOverExisting(bool value);
+
+ // Show and hige the splitting line on a Segment
+ //
+ void slotShowSplitLine(int x, int y);
+ void slotHideSplitLine();
+
+ void slotExternalWheelEvent(QWheelEvent*);
+
+ // TextFloat timer
+ void slotTextFloatTimeout();
+
+ void slotUpdateSegmentsDrawBuffer();
+ void slotUpdateSegmentsDrawBuffer(const QRect&);
+
+ void slotRefreshColourCache();
+
+ void slotNewMIDIRecordingSegment(Segment*);
+ void slotNewAudioRecordingSegment(Segment*);
+ // no longer used, see RosegardenGUIDoc::insertRecordedMidi
+// void slotRecordMIDISegmentUpdated(Segment*, timeT updatedFrom);
+ void slotStoppedRecording();
+
+ void slotUpdateSize();
+
+signals:
+ void editSegment(Segment*); // use default editor
+ void editSegmentNotation(Segment*);
+ void editSegmentMatrix(Segment*);
+ void editSegmentAudio(Segment*);
+ void editSegmentEventList(Segment*);
+ void audioSegmentAutoSplit(Segment*);
+ void editRepeat(Segment*, timeT);
+
+ void setPointerPosition(timeT);
+
+ void showContextHelp(const QString &);
+
+protected:
+ virtual bool event(QEvent *);
+
+ virtual void contentsMousePressEvent(QMouseEvent*);
+ virtual void contentsMouseReleaseEvent(QMouseEvent*);
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent*);
+ virtual void contentsMouseMoveEvent(QMouseEvent*);
+
+ virtual void viewportPaintEvent(QPaintEvent*);
+ virtual void resizeEvent(QResizeEvent*);
+
+ virtual void enterEvent(QEvent *);
+ virtual void leaveEvent(QEvent *);
+
+ virtual void viewportPaintRect(QRect);
+
+ /**
+ * if something changed, returns true and sets rect accordingly
+ * sets 'scroll' if some scrolling occurred
+ */
+ bool checkScrollAndRefreshDrawBuffer(QRect &, bool& scroll);
+ void refreshSegmentsDrawBuffer(const QRect&);
+ void refreshArtifactsDrawBuffer(const QRect&);
+ void drawArea(QPainter * p, const QRect& rect);
+ void drawAreaAudioPreviews(QPainter * p, const QRect& rect);
+ void drawAreaArtifacts(QPainter * p, const QRect& rect);
+ void drawRect(const QRect& rect, QPainter * p, const QRect& clipRect,
+ bool isSelected = false, int intersectLvl = 0, bool fill = true);
+ void drawCompRect(const CompositionRect& r, QPainter *p, const QRect& clipRect,
+ int intersectLvl = 0, bool fill = true);
+ void drawCompRectLabel(const CompositionRect& r, QPainter *p, const QRect& clipRect);
+ void drawIntersections(const CompositionModel::rectcontainer&, QPainter * p, const QRect& clipRect);
+
+ void drawPointer(QPainter * p, const QRect& clipRect);
+ void drawGuides(QPainter * p, const QRect& clipRect);
+ void drawTextFloat(QPainter * p, const QRect& clipRect);
+
+ void initStepSize();
+ void releaseCurrentItem();
+
+ static QColor mixBrushes(QBrush a, QBrush b);
+
+ SegmentSelector* getSegmentSelectorTool();
+
+protected slots:
+ void slotSegmentsDrawBufferNeedsRefresh() {
+ m_segmentsDrawBufferRefresh =
+ QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
+ }
+
+ void slotSegmentsDrawBufferNeedsRefresh(QRect r) {
+ m_segmentsDrawBufferRefresh |=
+ (QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight())
+ & r);
+ }
+
+ void slotArtifactsDrawBufferNeedsRefresh() {
+ m_artifactsDrawBufferRefresh =
+ QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
+ updateContents();
+ }
+
+ void slotArtifactsDrawBufferNeedsRefresh(QRect r) {
+ m_artifactsDrawBufferRefresh |=
+ (QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight())
+ & r);
+ updateContents(r);
+ }
+
+ void slotAllDrawBuffersNeedRefresh() {
+ slotSegmentsDrawBufferNeedsRefresh();
+ slotArtifactsDrawBufferNeedsRefresh();
+ }
+
+ void slotAllDrawBuffersNeedRefresh(QRect r) {
+ slotSegmentsDrawBufferNeedsRefresh(r);
+ slotArtifactsDrawBufferNeedsRefresh(r);
+ }
+
+ void slotToolHelpChanged(const QString &);
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ CompositionModel* m_model;
+ CompositionItem m_currentItem;
+
+ SegmentTool* m_tool;
+ SegmentToolBox* m_toolBox;
+
+ bool m_showPreviews;
+ bool m_showSegmentLabels;
+ bool m_fineGrain;
+ bool m_pencilOverExisting;
+
+ int m_minWidth;
+
+ int m_stepSize;
+ QColor m_rectFill;
+ QColor m_selectedRectFill;
+
+ int m_pointerPos;
+ QColor m_pointerColor;
+ int m_pointerWidth;
+ QPen m_pointerPen;
+
+ QRect m_tmpRect;
+ QColor m_tmpRectFill;
+ QPoint m_splitLinePos;
+
+ QColor m_trackDividerColor;
+
+ bool m_drawGuides;
+ QColor m_guideColor;
+ int m_topGuidePos;
+ int m_foreGuidePos;
+
+ bool m_drawSelectionRect;
+ QRect m_selectionRect;
+
+ bool m_drawTextFloat;
+ QString m_textFloatText;
+ QPoint m_textFloatPos;
+
+ QPixmap m_segmentsDrawBuffer;
+ QPixmap m_artifactsDrawBuffer;
+ QRect m_segmentsDrawBufferRefresh;
+ QRect m_artifactsDrawBufferRefresh;
+ int m_lastBufferRefreshX;
+ int m_lastBufferRefreshY;
+ int m_lastPointerRefreshX;
+ QPixmap m_backgroundPixmap;
+
+ QString m_toolContextHelp;
+ bool m_contextHelpShown;
+
+ mutable CompositionModel::AudioPreviewDrawData m_audioPreviewRects;
+ mutable CompositionModel::RectRanges m_notationPreviewRects;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/PreviewRect.cpp b/src/gui/editors/segment/segmentcanvas/PreviewRect.cpp
new file mode 100644
index 0000000..fa09644
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/PreviewRect.cpp
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "PreviewRect.h"
+
+#include <qcolor.h>
+#include <qrect.h>
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/editors/segment/segmentcanvas/PreviewRect.h b/src/gui/editors/segment/segmentcanvas/PreviewRect.h
new file mode 100644
index 0000000..59f3113
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/PreviewRect.h
@@ -0,0 +1,62 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PREVIEWRECT_H_
+#define _RG_PREVIEWRECT_H_
+
+#include <qcolor.h>
+#include <qrect.h>
+#include <vector>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class PreviewRect : public QRect {
+public:
+ PreviewRect(int left, int top, int width, int height) :
+ QRect(left, top, width, height) {};
+
+ PreviewRect(const QRect& r) :
+ QRect(r) {};
+
+ const QColor& getColor() const { return m_color; }
+ void setColor(QColor c) { m_color = c; }
+
+protected:
+ QColor m_color;
+};
+
+typedef std::vector<QImage> PixmapArray;
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentEraser.cpp b/src/gui/editors/segment/segmentcanvas/SegmentEraser.cpp
new file mode 100644
index 0000000..3d1e26f
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentEraser.cpp
@@ -0,0 +1,88 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentEraser.h"
+
+#include "misc/Debug.h"
+#include "commands/segment/SegmentEraseCommand.h"
+#include "CompositionView.h"
+#include "CompositionItemImpl.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentTool.h"
+#include <kcommand.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <klocale.h>
+
+
+namespace Rosegarden
+{
+
+SegmentEraser::SegmentEraser(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d)
+{
+ RG_DEBUG << "SegmentEraser()\n";
+}
+
+void SegmentEraser::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::pointingHandCursor);
+ setBasicContextHelp();
+}
+
+void SegmentEraser::handleMouseButtonPress(QMouseEvent *e)
+{
+ setCurrentItem(m_canvas->getFirstItemAt(e->pos()));
+}
+
+void SegmentEraser::handleMouseButtonRelease(QMouseEvent*)
+{
+ if (m_currentItem) {
+ // no need to test the result, we know it's good (see handleMouseButtonPress)
+ CompositionItemImpl* item = dynamic_cast<CompositionItemImpl*>((_CompositionItem*)m_currentItem);
+
+ addCommandToHistory(new SegmentEraseCommand(item->getSegment()));
+ }
+
+ setCurrentItem(CompositionItem());
+}
+
+int SegmentEraser::handleMouseMove(QMouseEvent*)
+{
+ setBasicContextHelp();
+ return RosegardenCanvasView::NoFollow;
+}
+
+void SegmentEraser::setBasicContextHelp()
+{
+ setContextHelp(i18n("Click on a segment to delete it"));
+}
+
+const QString SegmentEraser::ToolName = "segmenteraser";
+
+}
+#include "SegmentEraser.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentEraser.h b/src/gui/editors/segment/segmentcanvas/SegmentEraser.h
new file mode 100644
index 0000000..f428c28
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentEraser.h
@@ -0,0 +1,67 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTERASER_H_
+#define _RG_SEGMENTERASER_H_
+
+#include "SegmentTool.h"
+#include <qstring.h>
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentEraser : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+
+public:
+
+ virtual void ready();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ static const QString ToolName;
+
+protected:
+ SegmentEraser(CompositionView*, RosegardenGUIDoc*);
+ void setBasicContextHelp();
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.cpp b/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.cpp
new file mode 100644
index 0000000..f0c4598
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.cpp
@@ -0,0 +1,37 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentItemPreview.h"
+
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include <qpainter.h>
+#include <qrect.h>
+#include <qwmatrix.h>
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.h b/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.h
new file mode 100644
index 0000000..e190a5c
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentItemPreview.h
@@ -0,0 +1,91 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTITEMPREVIEW_H_
+#define _RG_SEGMENTITEMPREVIEW_H_
+
+#include <qrect.h>
+
+
+class QWMatrix;
+class QPainter;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RulerScale;
+
+
+//////////////////////////////////////////////////////////////////////
+class SegmentItemPreview
+{
+public:
+ SegmentItemPreview(Segment& parent,
+ RulerScale* scale);
+ virtual ~SegmentItemPreview();
+
+ enum PreviewState {
+ PreviewChanged,
+ PreviewCalculating,
+ PreviewCurrent
+ };
+
+ virtual void drawShape(QPainter&) = 0;
+
+ PreviewState getPreviewState() const { return m_previewState; }
+
+ /**
+ * Sets whether the preview shape shown in the segment needs
+ * to be refreshed
+ */
+ void setPreviewCurrent(bool c)
+ { m_previewState = (c ? PreviewCurrent : PreviewChanged); }
+
+ /**
+ * Clears out the preview entirely so that it will be regenerated
+ * next time
+ */
+ virtual void clearPreview() = 0;
+
+ QRect rect();
+
+protected:
+ virtual void updatePreview(const QWMatrix &matrix) = 0;
+
+ //--------------- Data members ---------------------------------
+
+ Segment *m_segment;
+ RulerScale *m_rulerScale;
+
+ PreviewState m_previewState;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentJoiner.cpp b/src/gui/editors/segment/segmentcanvas/SegmentJoiner.cpp
new file mode 100644
index 0000000..5129202
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentJoiner.cpp
@@ -0,0 +1,73 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentJoiner.h"
+
+#include "misc/Debug.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentTool.h"
+#include <kcommand.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <klocale.h>
+
+
+namespace Rosegarden
+{
+
+SegmentJoiner::SegmentJoiner(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d)
+{
+ RG_DEBUG << "SegmentJoiner() - not implemented\n";
+}
+
+SegmentJoiner::~SegmentJoiner()
+{}
+
+void
+SegmentJoiner::handleMouseButtonPress(QMouseEvent*)
+{}
+
+void
+SegmentJoiner::handleMouseButtonRelease(QMouseEvent*)
+{}
+
+int
+SegmentJoiner::handleMouseMove(QMouseEvent*)
+{
+ return RosegardenCanvasView::NoFollow;
+}
+
+void
+SegmentJoiner::contentsMouseDoubleClickEvent(QMouseEvent*)
+{}
+
+const QString SegmentJoiner::ToolName = "segmentjoiner";
+
+}
+#include "SegmentJoiner.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentJoiner.h b/src/gui/editors/segment/segmentcanvas/SegmentJoiner.h
new file mode 100644
index 0000000..2c83a26
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentJoiner.h
@@ -0,0 +1,70 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTJOINER_H_
+#define _RG_SEGMENTJOINER_H_
+
+#include "SegmentTool.h"
+#include <qstring.h>
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentJoiner : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+
+public:
+
+ virtual ~SegmentJoiner();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ // don't do double clicks
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent*);
+
+ static const QString ToolName;
+
+protected:
+ SegmentJoiner(CompositionView*, RosegardenGUIDoc*);
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentMover.cpp b/src/gui/editors/segment/segmentcanvas/SegmentMover.cpp
new file mode 100644
index 0000000..a3d2a59
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentMover.cpp
@@ -0,0 +1,348 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentMover.h"
+
+#include "base/Event.h"
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/RealTime.h"
+#include "base/Track.h"
+#include "base/SnapGrid.h"
+#include "commands/segment/SegmentReconfigureCommand.h"
+#include "CompositionItemHelper.h"
+#include "CompositionModel.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentTool.h"
+#include "SegmentToolBox.h"
+#include "SegmentSelector.h"
+#include <kcommand.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <klocale.h>
+
+
+namespace Rosegarden
+{
+
+SegmentMover::SegmentMover(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d)
+{
+ RG_DEBUG << "SegmentMover()\n";
+}
+
+void SegmentMover::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::sizeAllCursor);
+ connect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+ setBasicContextHelp();
+}
+
+void SegmentMover::stow()
+{
+ disconnect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+}
+
+void SegmentMover::slotCanvasScrolled(int newX, int newY)
+{
+ QMouseEvent tmpEvent(QEvent::MouseMove,
+ m_canvas->viewport()->mapFromGlobal(QCursor::pos()) + QPoint(newX, newY),
+ Qt::NoButton, Qt::NoButton);
+ handleMouseMove(&tmpEvent);
+}
+
+void SegmentMover::handleMouseButtonPress(QMouseEvent *e)
+{
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+ SegmentSelector* selector = dynamic_cast<SegmentSelector*>
+ (getToolBox()->getTool("segmentselector"));
+
+ // #1027303: Segment move issue
+ // Clear selection if we're clicking on an item that's not in it
+ // and we're not in add mode
+
+ if (selector && item &&
+ !m_canvas->getModel()->isSelected(item) && !selector->isSegmentAdding()) {
+ m_canvas->getModel()->clearSelected();
+ m_canvas->getModel()->signalSelection();
+ m_canvas->updateContents();
+ }
+
+ if (item) {
+
+ setCurrentItem(item);
+ m_clickPoint = e->pos();
+ Segment* s = CompositionItemHelper::getSegment(m_currentItem);
+
+ int x = int(m_canvas->grid().getRulerScale()->getXForTime(s->getStartTime()));
+ int y = int(m_canvas->grid().getYBinCoordinate(s->getTrack()));
+
+ m_canvas->setGuidesPos(x, y);
+ m_canvas->setDrawGuides(true);
+
+ if (m_canvas->getModel()->haveSelection()) {
+ RG_DEBUG << "SegmentMover::handleMouseButtonPress() : haveSelection\n";
+ // startChange on all selected segments
+ m_canvas->getModel()->startChangeSelection(CompositionModel::ChangeMove);
+
+
+ CompositionModel::itemcontainer& changingItems = m_canvas->getModel()->getChangingItems();
+ // set m_currentItem to its "sibling" among selected (now moving) items
+ setCurrentItem(CompositionItemHelper::findSiblingCompositionItem(changingItems, m_currentItem));
+
+ } else {
+ RG_DEBUG << "SegmentMover::handleMouseButtonPress() : no selection\n";
+ m_canvas->getModel()->startChange(item, CompositionModel::ChangeMove);
+ }
+
+ m_canvas->updateContents();
+
+ m_passedInertiaEdge = false;
+
+ } else {
+
+ // check for addmode - clear the selection if not
+ RG_DEBUG << "SegmentMover::handleMouseButtonPress() : clear selection\n";
+ m_canvas->getModel()->clearSelected();
+ m_canvas->getModel()->signalSelection();
+ m_canvas->updateContents();
+ }
+
+}
+
+void SegmentMover::handleMouseButtonRelease(QMouseEvent *e)
+{
+ Composition &comp = m_doc->getComposition();
+
+ int startDragTrackPos = m_canvas->grid().getYBin(m_clickPoint.y());
+ int currentTrackPos = m_canvas->grid().getYBin(e->pos().y());
+ int trackDiff = currentTrackPos - startDragTrackPos;
+
+ if (m_currentItem) {
+
+ if (changeMade()) {
+
+ CompositionModel::itemcontainer& changingItems = m_canvas->getModel()->getChangingItems();
+
+ SegmentReconfigureCommand *command =
+ new SegmentReconfigureCommand
+ (changingItems.size() == 1 ? i18n("Move Segment") : i18n("Move Segments"));
+
+
+ CompositionModel::itemcontainer::iterator it;
+
+ for (it = changingItems.begin();
+ it != changingItems.end();
+ it++) {
+
+ CompositionItem item = *it;
+
+ Segment* segment = CompositionItemHelper::getSegment(item);
+
+ TrackId origTrackId = segment->getTrack();
+ int trackPos = comp.getTrackPositionById(origTrackId);
+ trackPos += trackDiff;
+
+ if (trackPos < 0) {
+ trackPos = 0;
+ } else if (trackPos >= comp.getNbTracks()) {
+ trackPos = comp.getNbTracks() - 1;
+ }
+
+ Track *newTrack = comp.getTrackByPosition(trackPos);
+ int newTrackId = origTrackId;
+ if (newTrack) newTrackId = newTrack->getId();
+
+ timeT newStartTime = CompositionItemHelper::getStartTime(item, m_canvas->grid());
+
+ // We absolutely don't want to snap the end time
+ // to the grid. We want it to remain exactly the same
+ // as it was, but relative to the new start time.
+ timeT newEndTime = newStartTime + segment->getEndMarkerTime()
+ - segment->getStartTime();
+
+ command->addSegment(segment,
+ newStartTime,
+ newEndTime,
+ newTrackId);
+ }
+
+ addCommandToHistory(command);
+ }
+
+ m_canvas->hideTextFloat();
+ m_canvas->setDrawGuides(false);
+ m_canvas->getModel()->endChange();
+ m_canvas->slotUpdateSegmentsDrawBuffer();
+
+ }
+
+ setChangeMade(false);
+ m_currentItem = CompositionItem();
+
+ setBasicContextHelp();
+}
+
+int SegmentMover::handleMouseMove(QMouseEvent *e)
+{
+ m_canvas->setSnapGrain(true);
+
+ Composition &comp = m_doc->getComposition();
+
+ if (!m_currentItem) {
+ setBasicContextHelp();
+ return RosegardenCanvasView::NoFollow;
+ }
+
+ if (!m_canvas->isFineGrain()) {
+ setContextHelp(i18n("Hold Shift to avoid snapping to beat grid"));
+ } else {
+ clearContextHelp();
+ }
+
+ CompositionModel::itemcontainer& changingItems = m_canvas->getModel()->getChangingItems();
+
+ // RG_DEBUG << "SegmentMover::handleMouseMove : nb changingItems = "
+ // << changingItems.size() << endl;
+
+ CompositionModel::itemcontainer::iterator it;
+ int guideX = 0;
+ int guideY = 0;
+ QRect updateRect;
+
+ for (it = changingItems.begin();
+ it != changingItems.end();
+ it++) {
+ // it->second->showRepeatRect(false);
+
+ int dx = e->pos().x() - m_clickPoint.x(),
+ dy = e->pos().y() - m_clickPoint.y();
+
+ const int inertiaDistance = m_canvas->grid().getYSnap() / 3;
+ if (!m_passedInertiaEdge &&
+ (dx < inertiaDistance && dx > -inertiaDistance) &&
+ (dy < inertiaDistance && dy > -inertiaDistance)) {
+ return RosegardenCanvasView::NoFollow;
+ } else {
+ m_passedInertiaEdge = true;
+ }
+
+ timeT newStartTime = m_canvas->grid().snapX((*it)->savedRect().x() + dx);
+
+ int newX = int(m_canvas->grid().getRulerScale()->getXForTime(newStartTime));
+
+ int startDragTrackPos = m_canvas->grid().getYBin(m_clickPoint.y());
+ int currentTrackPos = m_canvas->grid().getYBin(e->pos().y());
+ int trackDiff = currentTrackPos - startDragTrackPos;
+ int trackPos = m_canvas->grid().getYBin((*it)->savedRect().y());
+
+// std::cerr << "segment " << *it << ": mouse started at track " << startDragTrackPos << ", is now at " << currentTrackPos << ", trackPos from " << trackPos << " to ";
+
+ trackPos += trackDiff;
+
+// std::cerr << trackPos << std::endl;
+
+ if (trackPos < 0) {
+ trackPos = 0;
+ } else if (trackPos >= comp.getNbTracks()) {
+ trackPos = comp.getNbTracks() - 1;
+ }
+/*!!!
+ int newY = m_canvas->grid().snapY((*it)->savedRect().y() + dy);
+ // Make sure we don't set a non-existing track
+ if (newY < 0) {
+ newY = 0;
+ }
+ int trackPos = m_canvas->grid().getYBin(newY);
+
+ // RG_DEBUG << "SegmentMover::handleMouseMove: orig y "
+ // << (*it)->savedRect().y()
+ // << ", dy " << dy << ", newY " << newY
+ // << ", track " << track << endl;
+
+ // Make sure we don't set a non-existing track (c'td)
+ // TODO: make this suck less. Either the tool should
+ // not allow it in the first place, or we automatically
+ // create new tracks - might make undo very tricky though
+ //
+ if (trackPos >= comp.getNbTracks())
+ trackPos = comp.getNbTracks() - 1;
+*/
+ int newY = m_canvas->grid().getYBinCoordinate(trackPos);
+
+ // RG_DEBUG << "SegmentMover::handleMouseMove: moving to "
+ // << newX << "," << newY << endl;
+
+ updateRect |= (*it)->rect();
+ (*it)->moveTo(newX, newY);
+ updateRect |= (*it)->rect();
+ setChangeMade(true);
+ }
+
+ if (changeMade())
+ m_canvas->getModel()->signalContentChange();
+
+ guideX = m_currentItem->rect().x();
+ guideY = m_currentItem->rect().y();
+
+ m_canvas->setGuidesPos(guideX, guideY);
+
+ timeT currentItemStartTime = m_canvas->grid().snapX(m_currentItem->rect().x());
+
+ RealTime time = comp.getElapsedRealTime(currentItemStartTime);
+ QString ms;
+ ms.sprintf("%03d", time.msec());
+
+ int bar, beat, fraction, remainder;
+ comp.getMusicalTimeForAbsoluteTime(currentItemStartTime, bar, beat, fraction, remainder);
+
+ QString posString = QString("%1.%2s (%3, %4, %5)")
+ .arg(time.sec).arg(ms)
+ .arg(bar + 1).arg(beat).arg(fraction);
+
+ m_canvas->setTextFloat(guideX + 10, guideY - 30, posString);
+ m_canvas->updateContents();
+
+ return RosegardenCanvasView::FollowHorizontal | RosegardenCanvasView::FollowVertical;
+}
+
+void SegmentMover::setBasicContextHelp()
+{
+ setContextHelp(i18n("Click and drag to move a segment"));
+}
+
+const QString SegmentMover::ToolName = "segmentmover";
+
+}
+#include "SegmentMover.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentMover.h b/src/gui/editors/segment/segmentcanvas/SegmentMover.h
new file mode 100644
index 0000000..776189e
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentMover.h
@@ -0,0 +1,78 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTMOVER_H_
+#define _RG_SEGMENTMOVER_H_
+
+#include "SegmentTool.h"
+#include <qpoint.h>
+#include <qstring.h>
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentMover : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+
+public:
+
+ virtual void ready();
+ virtual void stow();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ static const QString ToolName;
+
+protected slots:
+ void slotCanvasScrolled(int newX, int newY);
+
+protected:
+ SegmentMover(CompositionView*, RosegardenGUIDoc*);
+
+ void setBasicContextHelp();
+
+ //--------------- Data members ---------------------------------
+
+ QPoint m_clickPoint;
+ bool m_passedInertiaEdge;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentOrderer.cpp b/src/gui/editors/segment/segmentcanvas/SegmentOrderer.cpp
new file mode 100644
index 0000000..4262eb9
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentOrderer.cpp
@@ -0,0 +1,48 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentOrderer.h"
+
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/Segment.h"
+#include <klocale.h>
+
+
+namespace Rosegarden
+{
+
+void SegmentOrderer::segmentClicked(const Segment* s)
+{
+ m_segmentZs[s] = ++m_currentMaxZ;
+ RG_DEBUG << "SegmentOrderer::segmentClicked() s = " << s << " - max Z = " << m_currentMaxZ << endl;
+}
+
+unsigned int SegmentOrderer::getZForSegment(const Rosegarden::Segment* s)
+{
+ return m_segmentZs[s];
+}
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentOrderer.h b/src/gui/editors/segment/segmentcanvas/SegmentOrderer.h
new file mode 100644
index 0000000..f4b3d9a
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentOrderer.h
@@ -0,0 +1,59 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTORDERER_H_
+#define _RG_SEGMENTORDERER_H_
+
+#include "base/Composition.h"
+#include <map>
+
+
+
+
+namespace Rosegarden
+{
+
+class Segment;
+
+
+class SegmentOrderer : public CompositionObserver {
+public:
+ SegmentOrderer() : m_currentMaxZ(0) {};
+
+ unsigned int getZForSegment(const Segment*);
+
+ void segmentClicked(const Segment *);
+
+protected:
+
+ //--------------- Data members ---------------------------------
+ std::map<const Segment*, unsigned int> m_segmentZs;
+ unsigned int m_currentMaxZ;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentPencil.cpp b/src/gui/editors/segment/segmentcanvas/SegmentPencil.cpp
new file mode 100644
index 0000000..68ca020
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentPencil.cpp
@@ -0,0 +1,295 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentPencil.h"
+
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "gui/general/ClefIndex.h"
+#include "base/NotationTypes.h"
+#include "base/Segment.h"
+#include "base/SnapGrid.h"
+#include "base/Track.h"
+#include "commands/segment/SegmentInsertCommand.h"
+#include "CompositionItemHelper.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentTool.h"
+#include <kcommand.h>
+#include <klocale.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+SegmentPencil::SegmentPencil(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d),
+ m_newRect(false),
+ m_track(0),
+ m_startTime(0),
+ m_endTime(0)
+{
+ RG_DEBUG << "SegmentPencil()\n";
+}
+
+void SegmentPencil::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::ibeamCursor);
+ connect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+ setContextHelpFor(QPoint(0, 0));
+}
+
+void SegmentPencil::stow()
+{
+ disconnect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+}
+
+void SegmentPencil::slotCanvasScrolled(int newX, int newY)
+{
+ QMouseEvent tmpEvent(QEvent::MouseMove,
+ m_canvas->viewport()->mapFromGlobal(QCursor::pos()) + QPoint(newX, newY),
+ Qt::NoButton, Qt::NoButton);
+ handleMouseMove(&tmpEvent);
+}
+
+void SegmentPencil::handleMouseButtonPress(QMouseEvent *e)
+{
+ if (e->button() == RightButton)
+ return;
+
+ // is user holding Ctrl+Alt? (ugly, but we are running short on available
+ // modifiers; Alt is grabbed by the window manager, and right clicking, my
+ // (dmm) original idea, is grabbed by the context menu, so let's see how
+ // this goes over
+ bool pencilAnyway = (m_canvas->pencilOverExisting());
+
+ m_newRect = false;
+
+ // Check if mouse click was on a rect
+ //
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ // If user clicked a rect, and pencilAnyway is false, then there's nothing
+ // left to do here
+ if (item) {
+ delete item;
+ if (!pencilAnyway) return ;
+ }
+
+ // make new item
+ //
+ m_canvas->setSnapGrain(false);
+
+ int trackPosition = m_canvas->grid().getYBin(e->pos().y());
+
+ // Don't do anything if the user clicked beyond the track buttons
+ //
+ if (trackPosition >= m_doc->getComposition().getNbTracks())
+ return ;
+
+ Track *t = m_doc->getComposition().getTrackByPosition(trackPosition);
+ if (!t)
+ return ;
+
+ TrackId trackId = t->getId();
+
+ timeT time = int(nearbyint(m_canvas->grid().snapX(e->pos().x(), SnapGrid::SnapLeft)));
+ timeT duration = int(nearbyint(m_canvas->grid().getSnapTime(double(e->pos().x()))));
+ if (duration == 0)
+ duration = Note(Note::Shortest).getDuration();
+
+ int multiple = m_doc->getComposition()
+ .getMaxContemporaneousSegmentsOnTrack(trackId);
+ if (multiple < 1) multiple = 1;
+
+ QRect tmpRect;
+ tmpRect.setX(int(nearbyint(m_canvas->grid().getRulerScale()->getXForTime(time))));
+ tmpRect.setY(m_canvas->grid().getYBinCoordinate(trackPosition) + 1);
+ tmpRect.setHeight(m_canvas->grid().getYSnap() * multiple - 2);
+ tmpRect.setWidth(int(nearbyint(m_canvas->grid().getRulerScale()->getWidthForDuration(time, duration))));
+
+ m_canvas->setTmpRect(tmpRect,
+ GUIPalette::convertColour
+ (m_doc->getComposition().getSegmentColourMap().
+ getColourByIndex(t->getColor())));
+
+ m_newRect = true;
+ m_origPos = e->pos();
+
+ m_canvas->updateContents(tmpRect);
+}
+
+void SegmentPencil::handleMouseButtonRelease(QMouseEvent* e)
+{
+ if (e->button() == RightButton)
+ return ;
+
+ setContextHelpFor(e->pos());
+
+ if (m_newRect) {
+
+ QRect tmpRect = m_canvas->getTmpRect();
+
+ int trackPosition = m_canvas->grid().getYBin(tmpRect.y());
+ Track *track = m_doc->getComposition().getTrackByPosition(trackPosition);
+ timeT startTime = int(nearbyint(m_canvas->grid().getRulerScale()->getTimeForX(tmpRect.x()))),
+ endTime = int(nearbyint(m_canvas->grid().getRulerScale()->getTimeForX(tmpRect.x() + tmpRect.width())));
+
+ // RG_DEBUG << "SegmentPencil::handleMouseButtonRelease() : new segment with track id "
+ // << track->getId() << endl;
+
+ SegmentInsertCommand *command =
+ new SegmentInsertCommand(m_doc, track->getId(),
+ startTime, endTime);
+
+ m_newRect = false;
+
+ addCommandToHistory(command);
+
+ // add the SegmentItem by hand, instead of allowing the usual
+ // update mechanism to spot it. This way we can select the
+ // segment as we add it; otherwise we'd have no way to know
+ // that the segment was created by this tool rather than by
+ // e.g. a simple file load
+
+ Segment *segment = command->getSegment();
+
+ // add a clef to the start of the segment (tracks initialize to a
+ // default of 0 for this property, so treble will be the default if it
+ // is not specified elsewhere)
+ segment->insert(clefIndexToClef(track->getClef()).getAsEvent
+ (segment->getStartTime()));
+ segment->setTranspose(track->getTranspose());
+ segment->setColourIndex(track->getColor());
+ segment->setLowestPlayable(track->getLowestPlayable());
+ segment->setHighestPlayable(track->getHighestPlayable());
+
+ std::string label = qstrtostr(track->getPresetLabel());
+ if (label != "") {
+ segment->setLabel(qstrtostr(track->getPresetLabel()));
+ }
+
+ CompositionItem item = CompositionItemHelper::makeCompositionItem(segment);
+ m_canvas->getModel()->clearSelected();
+ m_canvas->getModel()->setSelected(item);
+ m_canvas->getModel()->signalSelection();
+ m_canvas->setTmpRect(QRect());
+ m_canvas->slotUpdateSegmentsDrawBuffer();
+
+ } else {
+
+ m_newRect = false;
+ }
+}
+
+int SegmentPencil::handleMouseMove(QMouseEvent *e)
+{
+ if (!m_newRect) {
+ setContextHelpFor(e->pos());
+ return RosegardenCanvasView::NoFollow;
+ }
+
+ if (!m_canvas->isFineGrain()) {
+ setContextHelp(i18n("Hold Shift to avoid snapping to bar lines"));
+ } else {
+ clearContextHelp();
+ }
+
+ QRect tmpRect = m_canvas->getTmpRect();
+ QRect oldTmpRect = tmpRect;
+
+ m_canvas->setSnapGrain(false);
+
+ SnapGrid::SnapDirection direction = SnapGrid::SnapRight;
+ if (e->pos().x() <= m_origPos.x())
+ direction = SnapGrid::SnapLeft;
+
+ timeT snap = int(nearbyint(m_canvas->grid().getSnapTime(double(e->pos().x()))));
+ if (snap == 0)
+ snap = Note(Note::Shortest).getDuration();
+
+ timeT time = int(nearbyint(m_canvas->grid().snapX(e->pos().x(), direction)));
+
+ timeT startTime = int(nearbyint(m_canvas->grid().getRulerScale()->getTimeForX(tmpRect.x())));
+ timeT endTime = int(nearbyint(m_canvas->grid().getRulerScale()->getTimeForX(tmpRect.x() + tmpRect.width())));
+
+ if (direction == SnapGrid::SnapRight) {
+
+ if (time >= startTime) {
+ if ((time - startTime) < snap) {
+ time = startTime + snap;
+ }
+ } else {
+ if ((startTime - time) < snap) {
+ time = startTime - snap;
+ }
+ }
+
+ int w = int(nearbyint(m_canvas->grid().getRulerScale()->getWidthForDuration(startTime, time - startTime)));
+ tmpRect.setWidth(w);
+
+ } else { // SnapGrid::SnapLeft
+
+ // time += std::max(endTime - startTime, timeT(0));
+ tmpRect.setX(int(m_canvas->grid().getRulerScale()->getXForTime(time)));
+
+ }
+
+ m_canvas->setTmpRect(tmpRect);
+ return RosegardenCanvasView::FollowHorizontal;
+}
+
+void SegmentPencil::setContextHelpFor(QPoint p)
+{
+ int trackPosition = m_canvas->grid().getYBin(p.y());
+
+ if (trackPosition < m_doc->getComposition().getNbTracks()) {
+ Track *t = m_doc->getComposition().getTrackByPosition(trackPosition);
+ if (t) {
+ InstrumentId id = t->getInstrument();
+ if (id >= AudioInstrumentBase && id < MidiInstrumentBase) {
+ setContextHelp(i18n("Record or drop audio here"));
+ return;
+ }
+ }
+ }
+
+ setContextHelp(i18n("Click and drag to draw an empty segment. Control+Alt click and drag to draw in overlap mode."));
+}
+
+const QString SegmentPencil::ToolName = "segmentpencil";
+
+}
+#include "SegmentPencil.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentPencil.h b/src/gui/editors/segment/segmentcanvas/SegmentPencil.h
new file mode 100644
index 0000000..8b55917
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentPencil.h
@@ -0,0 +1,83 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTPENCIL_H_
+#define _RG_SEGMENTPENCIL_H_
+
+#include "base/Track.h"
+#include "SegmentTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+//////////////////////////////
+
+class SegmentPencil : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+ friend class SegmentSelector;
+
+public:
+
+ virtual void ready();
+ virtual void stow();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ static const QString ToolName;
+
+protected slots:
+ void slotCanvasScrolled(int newX, int newY);
+
+protected:
+ SegmentPencil(CompositionView*, RosegardenGUIDoc*);
+ void setContextHelpFor(QPoint p);
+
+ //--------------- Data members ---------------------------------
+
+ bool m_newRect;
+ TrackId m_track;
+ timeT m_startTime;
+ timeT m_endTime;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentResizer.cpp b/src/gui/editors/segment/segmentcanvas/SegmentResizer.cpp
new file mode 100644
index 0000000..6ae7433
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentResizer.cpp
@@ -0,0 +1,393 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentResizer.h"
+
+#include "base/Event.h"
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/NotationTypes.h"
+#include "base/Segment.h"
+#include "base/Track.h"
+#include "base/SnapGrid.h"
+#include "commands/segment/AudioSegmentResizeFromStartCommand.h"
+#include "commands/segment/AudioSegmentRescaleCommand.h"
+#include "commands/segment/SegmentRescaleCommand.h"
+#include "commands/segment/SegmentReconfigureCommand.h"
+#include "commands/segment/SegmentResizeFromStartCommand.h"
+#include "CompositionItemHelper.h"
+#include "CompositionModel.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "gui/widgets/ProgressDialog.h"
+#include "SegmentTool.h"
+#include <kcommand.h>
+#include <kmessagebox.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+SegmentResizer::SegmentResizer(CompositionView *c, RosegardenGUIDoc *d,
+ int edgeThreshold)
+ : SegmentTool(c, d),
+ m_edgeThreshold(edgeThreshold)
+{
+ RG_DEBUG << "SegmentResizer()\n";
+}
+
+void SegmentResizer::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::sizeHorCursor);
+ connect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+ setBasicContextHelp(false);
+}
+
+void SegmentResizer::stow()
+{
+ disconnect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+}
+
+void SegmentResizer::slotCanvasScrolled(int newX, int newY)
+{
+ QMouseEvent tmpEvent(QEvent::MouseMove,
+ m_canvas->viewport()->mapFromGlobal(QCursor::pos()) + QPoint(newX, newY),
+ Qt::NoButton, Qt::NoButton);
+ handleMouseMove(&tmpEvent);
+}
+
+void SegmentResizer::handleMouseButtonPress(QMouseEvent *e)
+{
+ RG_DEBUG << "SegmentResizer::handleMouseButtonPress" << endl;
+ m_canvas->getModel()->clearSelected();
+
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ if (item) {
+ RG_DEBUG << "SegmentResizer::handleMouseButtonPress - got item" << endl;
+ setCurrentItem(item);
+
+ // Are we resizing from start or end?
+ if (item->rect().x() + item->rect().width() / 2 > e->pos().x()) {
+ m_resizeStart = true;
+ } else {
+ m_resizeStart = false;
+ }
+
+ m_canvas->getModel()->startChange(item, m_resizeStart ? CompositionModel::ChangeResizeFromStart : CompositionModel::ChangeResizeFromEnd);
+
+ }
+}
+
+void SegmentResizer::handleMouseButtonRelease(QMouseEvent *e)
+{
+ RG_DEBUG << "SegmentResizer::handleMouseButtonRelease" << endl;
+
+ bool rescale = (e->state() & Qt::ControlButton);
+
+ if (m_currentItem) {
+
+ Segment* segment = CompositionItemHelper::getSegment(m_currentItem);
+
+ // We only want to snap the end that we were actually resizing.
+
+ timeT oldStartTime, oldEndTime;
+
+ oldStartTime = segment->getStartTime();
+ oldEndTime = segment->getEndMarkerTime();
+
+ timeT newStartTime, newEndTime;
+
+ if (m_resizeStart) {
+ newStartTime = CompositionItemHelper::getStartTime
+ (m_currentItem, m_canvas->grid());
+ newEndTime = oldEndTime;
+ } else {
+ newEndTime = CompositionItemHelper::getEndTime
+ (m_currentItem, m_canvas->grid());
+ newStartTime = oldStartTime;
+ }
+
+ if (changeMade()) {
+
+ if (newStartTime > newEndTime) std::swap(newStartTime, newEndTime);
+
+ if (rescale) {
+
+ if (segment->getType() == Segment::Audio) {
+
+ try {
+ m_doc->getAudioFileManager().testAudioPath();
+ } catch (AudioFileManager::BadAudioPathException) {
+ if (KMessageBox::warningContinueCancel
+ (0,
+ i18n("The audio file path does not exist or is not writable.\nYou must set the audio file path to a valid directory in Document Properties before rescaling an audio file.\nWould you like to set it now?"),
+ i18n("Warning"),
+ i18n("Set audio file path")) == KMessageBox::Continue) {
+ RosegardenGUIApp::self()->slotOpenAudioPathSettings();
+ }
+ }
+
+ float ratio = float(newEndTime - newStartTime) /
+ float(oldEndTime - oldStartTime);
+
+ AudioSegmentRescaleCommand *command =
+ new AudioSegmentRescaleCommand(m_doc, segment, ratio,
+ newStartTime, newEndTime);
+
+ ProgressDialog progressDlg
+ (i18n("Rescaling audio file..."), 100, 0);
+ progressDlg.setAutoClose(false);
+ progressDlg.setAutoReset(false);
+ progressDlg.show();
+ command->connectProgressDialog(&progressDlg);
+
+ addCommandToHistory(command);
+
+ progressDlg.setLabel(i18n("Generating audio preview..."));
+ command->disconnectProgressDialog(&progressDlg);
+ connect(&m_doc->getAudioFileManager(), SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+ connect(&progressDlg, SIGNAL(cancelClicked()),
+ &m_doc->getAudioFileManager(), SLOT(slotStopPreview()));
+
+ int fid = command->getNewAudioFileId();
+ if (fid >= 0) {
+ RosegardenGUIApp::self()->slotAddAudioFile(fid);
+ m_doc->getAudioFileManager().generatePreview(fid);
+ }
+
+ } else {
+
+ SegmentRescaleCommand *command =
+ new SegmentRescaleCommand(segment,
+ newEndTime - newStartTime,
+ oldEndTime - oldStartTime,
+ newStartTime);
+ addCommandToHistory(command);
+ }
+ } else {
+
+ if (m_resizeStart) {
+
+ if (segment->getType() == Segment::Audio) {
+ addCommandToHistory(new AudioSegmentResizeFromStartCommand
+ (segment, newStartTime));
+ } else {
+ addCommandToHistory(new SegmentResizeFromStartCommand
+ (segment, newStartTime));
+ }
+
+ } else {
+
+ SegmentReconfigureCommand *command =
+ new SegmentReconfigureCommand("Resize Segment");
+
+ int trackPos = CompositionItemHelper::getTrackPos
+ (m_currentItem, m_canvas->grid());
+
+ Composition &comp = m_doc->getComposition();
+ Track *track = comp.getTrackByPosition(trackPos);
+
+ command->addSegment(segment,
+ newStartTime,
+ newEndTime,
+ track->getId());
+ addCommandToHistory(command);
+ }
+ }
+ }
+ }
+
+ m_canvas->getModel()->endChange();
+ m_canvas->updateContents();
+ setChangeMade(false);
+ m_currentItem = CompositionItem();
+ setBasicContextHelp();
+}
+
+int SegmentResizer::handleMouseMove(QMouseEvent *e)
+{
+ // RG_DEBUG << "SegmentResizer::handleMouseMove" << endl;
+
+ bool rescale = (e->state() & Qt::ControlButton);
+
+ if (!m_currentItem) {
+ setBasicContextHelp(rescale);
+ return RosegardenCanvasView::NoFollow;
+ }
+
+ if (rescale) {
+ if (!m_canvas->isFineGrain()) {
+ setContextHelp(i18n("Hold Shift to avoid snapping to beat grid"));
+ } else {
+ clearContextHelp();
+ }
+ } else {
+ if (!m_canvas->isFineGrain()) {
+ setContextHelp(i18n("Hold Shift to avoid snapping to beat grid; hold Ctrl as well to rescale contents"));
+ } else {
+ setContextHelp("Hold Ctrl to rescale contents");
+ }
+ }
+
+ Segment* segment = CompositionItemHelper::getSegment(m_currentItem);
+
+ // Don't allow Audio segments to resize yet
+ //
+ /*!!!
+ if (segment->getType() == Segment::Audio)
+ {
+ m_currentItem = CompositionItem();
+ KMessageBox::information(m_canvas,
+ i18n("You can't yet resize an audio segment!"));
+ return RosegardenCanvasView::NoFollow;
+ }
+ */
+
+ QRect oldRect = m_currentItem->rect();
+
+ m_canvas->setSnapGrain(true);
+
+ timeT time = m_canvas->grid().snapX(e->pos().x());
+ timeT snap = m_canvas->grid().getSnapTime(double(e->pos().x()));
+ if (snap == 0)
+ snap = Note(Note::Shortest).getDuration();
+
+ // We only want to snap the end that we were actually resizing.
+
+ timeT itemStartTime, itemEndTime;
+
+ if (m_resizeStart) {
+ itemStartTime = CompositionItemHelper::getStartTime
+ (m_currentItem, m_canvas->grid());
+ itemEndTime = segment->getEndMarkerTime();
+ } else {
+ itemEndTime = CompositionItemHelper::getEndTime
+ (m_currentItem, m_canvas->grid());
+ itemStartTime = segment->getStartTime();
+ }
+
+ timeT duration = 0;
+
+ if (m_resizeStart) {
+
+ duration = itemEndTime - time;
+ // RG_DEBUG << "SegmentResizer::handleMouseMove() resize start : duration = "
+ // << duration << " - snap = " << snap
+ // << " - itemEndTime : " << itemEndTime
+ // << " - time : " << time
+ // << endl;
+
+ timeT newStartTime = 0;
+
+ if ((duration > 0 && duration < snap) ||
+ (duration < 0 && duration > -snap)) {
+
+ newStartTime = itemEndTime - (duration < 0 ? -snap : snap);
+
+ } else {
+
+ newStartTime = itemEndTime - duration;
+
+ }
+
+ CompositionItemHelper::setStartTime(m_currentItem,
+ newStartTime,
+ m_canvas->grid());
+ } else { // resize end
+
+ duration = time - itemStartTime;
+
+ timeT newEndTime = 0;
+
+ // RG_DEBUG << "SegmentResizer::handleMouseMove() resize end : duration = "
+ // << duration << " - snap = " << snap
+ // << " - itemEndTime : " << itemEndTime
+ // << " - time : " << time
+ // << endl;
+
+ if ((duration > 0 && duration < snap) ||
+ (duration < 0 && duration > -snap)) {
+
+ newEndTime = (duration < 0 ? -snap : snap) + itemStartTime;
+
+ } else {
+
+ newEndTime = duration + itemStartTime;
+
+ }
+
+ CompositionItemHelper::setEndTime(m_currentItem,
+ newEndTime,
+ m_canvas->grid());
+ }
+
+ if (duration != 0)
+ setChangeMade(true);
+
+ m_canvas->slotUpdateSegmentsDrawBuffer(m_currentItem->rect() | oldRect);
+
+ return RosegardenCanvasView::FollowHorizontal;
+}
+
+bool SegmentResizer::cursorIsCloseEnoughToEdge(const CompositionItem& p, const QPoint &coord,
+ int edgeThreshold, bool &start)
+{
+ if (abs(p->rect().x() + p->rect().width() - coord.x()) < edgeThreshold) {
+ start = false;
+ return true;
+ } else if (abs(p->rect().x() - coord.x()) < edgeThreshold) {
+ start = true;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void SegmentResizer::setBasicContextHelp(bool ctrlPressed)
+{
+ if (ctrlPressed) {
+ setContextHelp(i18n("Click and drag to resize a segment; hold Ctrl as well to rescale its contents"));
+ } else {
+ setContextHelp(i18n("Click and drag to rescale segment"));
+ }
+}
+
+const QString SegmentResizer::ToolName = "segmentresizer";
+
+}
+#include "SegmentResizer.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentResizer.h b/src/gui/editors/segment/segmentcanvas/SegmentResizer.h
new file mode 100644
index 0000000..9d54573
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentResizer.h
@@ -0,0 +1,87 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTRESIZER_H_
+#define _RG_SEGMENTRESIZER_H_
+
+#include "SegmentTool.h"
+#include <qstring.h>
+
+
+class QPoint;
+class QMouseEvent;
+class CompositionItem;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+/**
+ * Segment Resizer tool. Allows resizing only at the end of the segment part
+ */
+class SegmentResizer : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+ friend class SegmentSelector;
+
+public:
+
+ virtual void ready();
+ virtual void stow();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ static bool cursorIsCloseEnoughToEdge(const CompositionItem&, const QPoint&, int, bool &);
+
+ void setEdgeThreshold(int e) { m_edgeThreshold = e; }
+ int getEdgeThreshold() { return m_edgeThreshold; }
+
+ static const QString ToolName;
+
+protected slots:
+ void slotCanvasScrolled(int newX, int newY);
+
+protected:
+ SegmentResizer(CompositionView*, RosegardenGUIDoc*, int edgeThreshold = 10);
+ void setBasicContextHelp(bool ctrlPressed = false);
+
+ //--------------- Data members ---------------------------------
+
+ int m_edgeThreshold;
+ bool m_resizeStart;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentSelector.cpp b/src/gui/editors/segment/segmentcanvas/SegmentSelector.cpp
new file mode 100644
index 0000000..35ec639
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentSelector.cpp
@@ -0,0 +1,532 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentSelector.h"
+
+#include "base/Event.h"
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/RealTime.h"
+#include "base/SnapGrid.h"
+#include "base/Selection.h"
+#include "base/Track.h"
+#include "commands/segment/SegmentQuickCopyCommand.h"
+#include "commands/segment/SegmentReconfigureCommand.h"
+#include "CompositionItemHelper.h"
+#include "CompositionModel.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentPencil.h"
+#include "SegmentResizer.h"
+#include "SegmentTool.h"
+#include "SegmentToolBox.h"
+#include <kapplication.h>
+#include <kconfig.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+SegmentSelector::SegmentSelector(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d),
+ m_segmentAddMode(false),
+ m_segmentCopyMode(false),
+ m_segmentQuickCopyDone(false),
+ m_buttonPressed(false),
+ m_selectionMoveStarted(false),
+ m_dispatchTool(0)
+{
+ RG_DEBUG << "SegmentSelector()\n";
+}
+
+SegmentSelector::~SegmentSelector()
+{}
+
+void SegmentSelector::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::arrowCursor);
+ connect(m_canvas, SIGNAL(contentsMoving (int, int)),
+ this, SLOT(slotCanvasScrolled(int, int)));
+ setContextHelp(i18n("Click and drag to select segments"));
+}
+
+void SegmentSelector::stow()
+{}
+
+void SegmentSelector::slotCanvasScrolled(int newX, int newY)
+{
+ QMouseEvent tmpEvent(QEvent::MouseMove,
+ m_canvas->viewport()->mapFromGlobal(QCursor::pos()) + QPoint(newX, newY),
+ Qt::NoButton, Qt::NoButton);
+ handleMouseMove(&tmpEvent);
+}
+
+void
+SegmentSelector::handleMouseButtonPress(QMouseEvent *e)
+{
+ RG_DEBUG << "SegmentSelector::handleMouseButtonPress\n";
+ m_buttonPressed = true;
+
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ // If we're in segmentAddMode or not clicking on an item then we don't
+ // clear the selection vector. If we're clicking on an item and it's
+ // not in the selection - then also clear the selection.
+ //
+ if ((!m_segmentAddMode && !item) ||
+ (!m_segmentAddMode && !(m_canvas->getModel()->isSelected(item)))) {
+ m_canvas->getModel()->clearSelected();
+ }
+
+ if (item) {
+
+ // Fifteen percent of the width of the SegmentItem, up to 10px
+ //
+ int threshold = int(float(item->rect().width()) * 0.15);
+ if (threshold == 0) threshold = 1;
+ if (threshold > 10) threshold = 10;
+
+ bool start = false;
+
+ // Resize if we're dragging from the edge, provided we aren't
+ // in segment-add mode with at least one segment already
+ // selected -- as we aren't able to resize multiple segments
+ // at once, we should assume the segment-add aspect takes
+ // priority
+
+ if ((!m_segmentAddMode ||
+ !m_canvas->getModel()->haveSelection()) &&
+ SegmentResizer::cursorIsCloseEnoughToEdge(item, e->pos(), threshold, start)) {
+
+ SegmentResizer* resizer =
+ dynamic_cast<SegmentResizer*>(getToolBox()->getTool(SegmentResizer::ToolName));
+
+ resizer->setEdgeThreshold(threshold);
+
+ // For the moment we only allow resizing of a single segment
+ // at a time.
+ //
+ m_canvas->getModel()->clearSelected();
+
+ m_dispatchTool = resizer;
+
+ m_dispatchTool->ready(); // set mouse cursor
+ m_dispatchTool->handleMouseButtonPress(e);
+ return ;
+ }
+
+ bool selecting = true;
+
+ if (m_segmentAddMode && m_canvas->getModel()->isSelected(item)) {
+ selecting = false;
+ } else {
+ // put the segment in 'move' mode only if it's being selected
+ m_canvas->getModel()->startChange(item, CompositionModel::ChangeMove);
+ }
+
+ m_canvas->getModel()->setSelected(item, selecting);
+
+ // Moving
+ //
+ // RG_DEBUG << "SegmentSelector::handleMouseButtonPress - m_currentItem = " << item << endl;
+ m_currentItem = item;
+ m_clickPoint = e->pos();
+
+ int guideX = item->rect().x();
+ int guideY = item->rect().y();
+
+ m_canvas->setGuidesPos(guideX, guideY);
+
+ m_canvas->setDrawGuides(true);
+
+ } else {
+
+ // Add on middle button or ctrl+left - bounding box on rest
+ //
+ if (e->button() == MidButton ||
+ (e->button() == LeftButton && (e->state() & Qt::ControlButton))) {
+
+ m_dispatchTool = getToolBox()->getTool(SegmentPencil::ToolName);
+
+ if (m_dispatchTool) {
+ m_dispatchTool->ready(); // set mouse cursor
+ m_dispatchTool->handleMouseButtonPress(e);
+ }
+
+ return ;
+
+ } else {
+
+ m_canvas->setSelectionRectPos(e->pos());
+ m_canvas->setDrawSelectionRect(true);
+ if (!m_segmentAddMode)
+ m_canvas->getModel()->clearSelected();
+
+ }
+ }
+
+ // Tell the RosegardenGUIView that we've selected some new Segments -
+ // when the list is empty we're just unselecting.
+ //
+ m_canvas->getModel()->signalSelection();
+
+ m_passedInertiaEdge = false;
+}
+
+void
+SegmentSelector::handleMouseButtonRelease(QMouseEvent *e)
+{
+ m_buttonPressed = false;
+
+ // Hide guides and stuff
+ //
+ m_canvas->setDrawGuides(false);
+ m_canvas->hideTextFloat();
+
+ if (m_dispatchTool) {
+ m_dispatchTool->handleMouseButtonRelease(e);
+ m_dispatchTool = 0;
+ m_canvas->viewport()->setCursor(Qt::arrowCursor);
+ return ;
+ }
+
+ int startDragTrackPos = m_canvas->grid().getYBin(m_clickPoint.y());
+ int currentTrackPos = m_canvas->grid().getYBin(e->pos().y());
+ int trackDiff = currentTrackPos - startDragTrackPos;
+
+ if (!m_currentItem) {
+ m_canvas->setDrawSelectionRect(false);
+ m_canvas->getModel()->finalizeSelectionRect();
+ m_canvas->getModel()->signalSelection();
+ return ;
+ }
+
+ m_canvas->viewport()->setCursor(Qt::arrowCursor);
+
+ Composition &comp = m_doc->getComposition();
+
+ if (m_canvas->getModel()->isSelected(m_currentItem)) {
+
+ CompositionModel::itemcontainer& changingItems = m_canvas->getModel()->getChangingItems();
+ CompositionModel::itemcontainer::iterator it;
+
+ if (changeMade()) {
+
+ SegmentReconfigureCommand *command =
+ new SegmentReconfigureCommand
+ (m_selectedItems.size() == 1 ? i18n("Move Segment") :
+ i18n("Move Segments"));
+
+ for (it = changingItems.begin();
+ it != changingItems.end();
+ it++) {
+
+ CompositionItem item = *it;
+
+ Segment* segment = CompositionItemHelper::getSegment(item);
+
+ TrackId origTrackId = segment->getTrack();
+ int trackPos = comp.getTrackPositionById(origTrackId);
+ trackPos += trackDiff;
+
+ if (trackPos < 0) {
+ trackPos = 0;
+ } else if (trackPos >= comp.getNbTracks()) {
+ trackPos = comp.getNbTracks() - 1;
+ }
+
+ Track *newTrack = comp.getTrackByPosition(trackPos);
+ int newTrackId = origTrackId;
+ if (newTrack) newTrackId = newTrack->getId();
+
+ timeT itemStartTime = CompositionItemHelper::getStartTime
+ (item, m_canvas->grid());
+
+ // We absolutely don't want to snap the end time to
+ // the grid. We want it to remain exactly the same as
+ // it was, but relative to the new start time.
+ timeT itemEndTime = itemStartTime + segment->getEndMarkerTime()
+ - segment->getStartTime();
+
+// std::cerr << "releasing segment " << segment << ": mouse started at track " << startDragTrackPos << ", is now at " << currentTrackPos << ", diff is " << trackDiff << ", moving from track pos " << comp.getTrackPositionById(origTrackId) << " to " << trackPos << ", id " << origTrackId << " to " << newTrackId << std::endl;
+
+ command->addSegment(segment,
+ itemStartTime,
+ itemEndTime,
+ newTrackId);
+ }
+
+ addCommandToHistory(command);
+ }
+
+ m_canvas->getModel()->endChange();
+ m_canvas->slotUpdateSegmentsDrawBuffer();
+ }
+
+ // if we've just finished a quick copy then drop the Z level back
+ if (m_segmentQuickCopyDone) {
+ m_segmentQuickCopyDone = false;
+ // m_currentItem->setZ(2); // see SegmentItem::setSelected --??
+ }
+
+ setChangeMade(false);
+
+ m_selectionMoveStarted = false;
+
+ m_currentItem = CompositionItem();
+
+ setContextHelpFor(e->pos());
+}
+
+int
+SegmentSelector::handleMouseMove(QMouseEvent *e)
+{
+ if (!m_buttonPressed) {
+ setContextHelpFor(e->pos(), (e->state() & Qt::ControlButton));
+ return RosegardenCanvasView::NoFollow;
+ }
+
+ if (m_dispatchTool) {
+ return m_dispatchTool->handleMouseMove(e);
+ }
+
+ Composition &comp = m_doc->getComposition();
+
+ if (!m_currentItem) {
+
+ // RG_DEBUG << "SegmentSelector::handleMouseMove: no current item\n";
+
+ // do a bounding box
+ QRect selectionRect = m_canvas->getSelectionRect();
+
+ m_canvas->setDrawSelectionRect(true);
+
+ // same as for notation view
+ int w = int(e->pos().x() - selectionRect.x());
+ int h = int(e->pos().y() - selectionRect.y());
+ if (w > 0)
+ ++w;
+ else
+ --w;
+ if (h > 0)
+ ++h;
+ else
+ --h;
+
+ // Translate these points
+ //
+ m_canvas->setSelectionRectSize(w, h);
+
+ m_canvas->getModel()->signalSelection();
+ return RosegardenCanvasView::FollowHorizontal | RosegardenCanvasView::FollowVertical;
+ }
+
+ m_canvas->viewport()->setCursor(Qt::sizeAllCursor);
+
+ if (m_segmentCopyMode && !m_segmentQuickCopyDone) {
+ KMacroCommand *mcommand = new KMacroCommand
+ (SegmentQuickCopyCommand::getGlobalName());
+
+ SegmentSelection selectedItems = m_canvas->getSelectedSegments();
+ SegmentSelection::iterator it;
+ for (it = selectedItems.begin();
+ it != selectedItems.end();
+ it++) {
+ SegmentQuickCopyCommand *command =
+ new SegmentQuickCopyCommand(*it);
+
+ mcommand->addCommand(command);
+ }
+
+ addCommandToHistory(mcommand);
+
+ // generate SegmentItem
+ //
+ m_canvas->updateContents();
+ m_segmentQuickCopyDone = true;
+ }
+
+ m_canvas->setSnapGrain(true);
+
+ int startDragTrackPos = m_canvas->grid().getYBin(m_clickPoint.y());
+ int currentTrackPos = m_canvas->grid().getYBin(e->pos().y());
+ int trackDiff = currentTrackPos - startDragTrackPos;
+
+ if (m_canvas->getModel()->isSelected(m_currentItem)) {
+
+ if (!m_canvas->isFineGrain()) {
+ setContextHelp(i18n("Hold Shift to avoid snapping to beat grid"));
+ } else {
+ clearContextHelp();
+ }
+
+ // RG_DEBUG << "SegmentSelector::handleMouseMove: current item is selected\n";
+
+ if (!m_selectionMoveStarted) { // start move on selected items only once
+ m_canvas->getModel()->startChangeSelection(CompositionModel::ChangeMove);
+ m_selectionMoveStarted = true;
+ }
+
+ CompositionModel::itemcontainer& changingItems = m_canvas->getModel()->getChangingItems();
+ setCurrentItem(CompositionItemHelper::findSiblingCompositionItem(changingItems, m_currentItem));
+
+ CompositionModel::itemcontainer::iterator it;
+ int guideX = 0;
+ int guideY = 0;
+
+ for (it = changingItems.begin();
+ it != changingItems.end();
+ ++it) {
+
+ // RG_DEBUG << "SegmentSelector::handleMouseMove() : movingItem at "
+ // << (*it)->rect().x() << "," << (*it)->rect().y() << endl;
+
+ int dx = e->pos().x() - m_clickPoint.x(),
+ dy = e->pos().y() - m_clickPoint.y();
+
+ const int inertiaDistance = m_canvas->grid().getYSnap() / 3;
+ if (!m_passedInertiaEdge &&
+ (dx < inertiaDistance && dx > -inertiaDistance) &&
+ (dy < inertiaDistance && dy > -inertiaDistance)) {
+ return RosegardenCanvasView::NoFollow;
+ } else {
+ m_passedInertiaEdge = true;
+ }
+
+ timeT newStartTime = m_canvas->grid().snapX((*it)->savedRect().x() + dx);
+
+ int newX = int(m_canvas->grid().getRulerScale()->getXForTime(newStartTime));
+
+ int trackPos = m_canvas->grid().getYBin((*it)->savedRect().y());
+
+// std::cerr << "segment " << *it << ": mouse started at track " << startDragTrackPos << ", is now at " << currentTrackPos << ", trackPos from " << trackPos << " to ";
+
+ trackPos += trackDiff;
+
+// std::cerr << trackPos << std::endl;
+
+ if (trackPos < 0) {
+ trackPos = 0;
+ } else if (trackPos >= comp.getNbTracks()) {
+ trackPos = comp.getNbTracks() - 1;
+ }
+
+ int newY = m_canvas->grid().getYBinCoordinate(trackPos);
+
+ (*it)->moveTo(newX, newY);
+ setChangeMade(true);
+ }
+
+ if (changeMade())
+ m_canvas->getModel()->signalContentChange();
+
+ guideX = m_currentItem->rect().x();
+ guideY = m_currentItem->rect().y();
+
+ m_canvas->setGuidesPos(guideX, guideY);
+
+ timeT currentItemStartTime = m_canvas->grid().snapX(m_currentItem->rect().x());
+
+ RealTime time = comp.getElapsedRealTime(currentItemStartTime);
+ QString ms;
+ ms.sprintf("%03d", time.msec());
+
+ int bar, beat, fraction, remainder;
+ comp.getMusicalTimeForAbsoluteTime(currentItemStartTime, bar, beat, fraction, remainder);
+
+ QString posString = QString("%1.%2s (%3, %4, %5)")
+ .arg(time.sec).arg(ms)
+ .arg(bar + 1).arg(beat).arg(fraction);
+
+ m_canvas->setTextFloat(guideX + 10, guideY - 30, posString);
+ m_canvas->updateContents();
+
+ } else {
+ // RG_DEBUG << "SegmentSelector::handleMouseMove: current item not selected\n";
+ }
+
+ return RosegardenCanvasView::FollowHorizontal | RosegardenCanvasView::FollowVertical;
+}
+
+void SegmentSelector::setContextHelpFor(QPoint p, bool ctrlPressed)
+{
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ if (!kapp->config()->readBoolEntry("toolcontexthelp", true)) return;
+
+ CompositionItem item = m_canvas->getFirstItemAt(p);
+
+ if (!item) {
+ setContextHelp(i18n("Click and drag to select segments; middle-click and drag to draw an empty segment"));
+
+ } else {
+
+ // Same logic as in handleMouseButtonPress to establish
+ // whether we'd be moving or resizing
+
+ int threshold = int(float(item->rect().width()) * 0.15);
+ if (threshold == 0) threshold = 1;
+ if (threshold > 10) threshold = 10;
+ bool start = false;
+
+ if ((!m_segmentAddMode ||
+ !m_canvas->getModel()->haveSelection()) &&
+ SegmentResizer::cursorIsCloseEnoughToEdge(item, p,
+ threshold, start)) {
+ if (!ctrlPressed) {
+ setContextHelp(i18n("Click and drag to resize a segment; hold Ctrl as well to rescale its contents"));
+ } else {
+ setContextHelp(i18n("Click and drag to rescale segment"));
+ }
+ } else {
+ if (m_canvas->getModel()->haveMultipleSelection()) {
+ if (!ctrlPressed) {
+ setContextHelp(i18n("Click and drag to move segments; hold Ctrl as well to copy them"));
+ } else {
+ setContextHelp(i18n("Click and drag to copy segments"));
+ }
+ } else {
+ if (!ctrlPressed) {
+ setContextHelp(i18n("Click and drag to move segment; hold Ctrl as well to copy it; double-click to edit"));
+ } else {
+ setContextHelp(i18n("Click and drag to copy segment"));
+ }
+ }
+ }
+ }
+}
+
+const QString SegmentSelector::ToolName = "segmentselector";
+
+}
+#include "SegmentSelector.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentSelector.h b/src/gui/editors/segment/segmentcanvas/SegmentSelector.h
new file mode 100644
index 0000000..a6d8d9c
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentSelector.h
@@ -0,0 +1,109 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTSELECTOR_H_
+#define _RG_SEGMENTSELECTOR_H_
+
+#include "SegmentTool.h"
+#include <qpoint.h>
+#include <qstring.h>
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentSelector : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+ friend class SegmentTool;
+
+public:
+
+ virtual ~SegmentSelector();
+
+ virtual void ready();
+ virtual void stow();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ // These two alter the behaviour of the selection mode
+ //
+ // - SegmentAdd (usually when SHIFT is held down) allows
+ // multiple selections of Segments.
+ //
+ // - SegmentCopy (usually CONTROL) allows draw and drop
+ // copying of Segments - it's a quick shortcut
+ //
+ void setSegmentAdd(const bool &value) { m_segmentAddMode = value; }
+ void setSegmentCopy(const bool &value) { m_segmentCopyMode = value; }
+
+ bool isSegmentAdding() const { return m_segmentAddMode; }
+ bool isSegmentCopying() const { return m_segmentCopyMode; }
+
+ // Return the SegmentItem list for other tools to use
+ //
+ SegmentItemList* getSegmentItemList() { return &m_selectedItems; }
+
+ static const QString ToolName;
+
+protected slots:
+ void slotCanvasScrolled(int newX, int newY);
+
+protected:
+ SegmentSelector(CompositionView*, RosegardenGUIDoc*);
+
+ void setContextHelpFor(QPoint p, bool ctrlPressed = false);
+
+ //--------------- Data members ---------------------------------
+
+ SegmentItemList m_selectedItems;
+
+ bool m_segmentAddMode;
+ bool m_segmentCopyMode;
+ QPoint m_clickPoint;
+ bool m_segmentQuickCopyDone;
+ bool m_passedInertiaEdge;
+ bool m_buttonPressed;
+ bool m_selectionMoveStarted;
+
+ SegmentTool *m_dispatchTool;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentSplitter.cpp b/src/gui/editors/segment/segmentcanvas/SegmentSplitter.cpp
new file mode 100644
index 0000000..4fd48c3
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentSplitter.cpp
@@ -0,0 +1,175 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentSplitter.h"
+
+#include "misc/Debug.h"
+#include "base/Segment.h"
+#include "base/SnapGrid.h"
+#include "commands/segment/AudioSegmentSplitCommand.h"
+#include "commands/segment/SegmentSplitCommand.h"
+#include "CompositionItemHelper.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseTool.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "SegmentTool.h"
+#include <kcommand.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <klocale.h>
+
+
+namespace Rosegarden
+{
+
+SegmentSplitter::SegmentSplitter(CompositionView *c, RosegardenGUIDoc *d)
+ : SegmentTool(c, d),
+ m_prevX(0),
+ m_prevY(0)
+{
+ RG_DEBUG << "SegmentSplitter()\n";
+}
+
+SegmentSplitter::~SegmentSplitter()
+{}
+
+void SegmentSplitter::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::splitHCursor);
+ setBasicContextHelp();
+}
+
+void
+SegmentSplitter::handleMouseButtonPress(QMouseEvent *e)
+{
+ // Remove cursor and replace with line on a SegmentItem
+ // at where the cut will be made
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ if (item) {
+ m_canvas->viewport()->setCursor(Qt::blankCursor);
+ m_prevX = item->rect().x();
+ m_prevX = item->rect().y();
+ drawSplitLine(e);
+ delete item;
+ }
+
+}
+
+void
+SegmentSplitter::handleMouseButtonRelease(QMouseEvent *e)
+{
+ setBasicContextHelp();
+
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ if (item) {
+ m_canvas->setSnapGrain(true);
+ Segment* segment = CompositionItemHelper::getSegment(item);
+
+ if (segment->getType() == Segment::Audio) {
+ AudioSegmentSplitCommand *command =
+ new AudioSegmentSplitCommand(segment, m_canvas->grid().snapX(e->pos().x()));
+ addCommandToHistory(command);
+ } else {
+ SegmentSplitCommand *command =
+ new SegmentSplitCommand(segment, m_canvas->grid().snapX(e->pos().x()));
+ addCommandToHistory(command);
+ }
+
+ m_canvas->updateContents(item->rect());
+ delete item;
+ }
+
+ // Reinstate the cursor
+ m_canvas->viewport()->setCursor(Qt::splitHCursor);
+ m_canvas->slotHideSplitLine();
+}
+
+int
+SegmentSplitter::handleMouseMove(QMouseEvent *e)
+{
+ setBasicContextHelp();
+
+ CompositionItem item = m_canvas->getFirstItemAt(e->pos());
+
+ if (item) {
+// m_canvas->viewport()->setCursor(Qt::blankCursor);
+ drawSplitLine(e);
+ delete item;
+ return RosegardenCanvasView::FollowHorizontal;
+ } else {
+ m_canvas->viewport()->setCursor(Qt::splitHCursor);
+ m_canvas->slotHideSplitLine();
+ return RosegardenCanvasView::NoFollow;
+ }
+}
+
+void
+SegmentSplitter::drawSplitLine(QMouseEvent *e)
+{
+ m_canvas->setSnapGrain(true);
+
+ // Turn the real X into a snapped X
+ //
+ timeT xT = m_canvas->grid().snapX(e->pos().x());
+ int x = (int)(m_canvas->grid().getRulerScale()->getXForTime(xT));
+
+ // Need to watch y doesn't leak over the edges of the
+ // current Segment.
+ //
+ int y = m_canvas->grid().snapY(e->pos().y());
+
+ m_canvas->slotShowSplitLine(x, y);
+
+ QRect updateRect(std::max(0, std::min(x, m_prevX) - 5), y,
+ std::max(m_prevX, x) + 5, m_prevY + m_canvas->grid().getYSnap());
+ m_canvas->updateContents(updateRect);
+ m_prevX = x;
+ m_prevY = y;
+}
+
+void
+SegmentSplitter::contentsMouseDoubleClickEvent(QMouseEvent*)
+{
+ // DO NOTHING
+}
+
+void
+SegmentSplitter::setBasicContextHelp()
+{
+ if (!m_canvas->isFineGrain()) {
+ setContextHelp(i18n("Click on a segment to split it in two; hold Shift to avoid snapping to beat grid"));
+ } else {
+ setContextHelp(i18n("Click on a segment to split it in two"));
+ }
+}
+
+const QString SegmentSplitter::ToolName = "segmentsplitter";
+
+}
+#include "SegmentSplitter.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentSplitter.h b/src/gui/editors/segment/segmentcanvas/SegmentSplitter.h
new file mode 100644
index 0000000..06201ec
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentSplitter.h
@@ -0,0 +1,83 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTSPLITTER_H_
+#define _RG_SEGMENTSPLITTER_H_
+
+#include "SegmentTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentSplitter : public SegmentTool
+{
+ Q_OBJECT
+
+ friend class SegmentToolBox;
+
+public:
+
+ virtual ~SegmentSplitter();
+
+ virtual void ready();
+
+ virtual void handleMouseButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonRelease(QMouseEvent*);
+ virtual int handleMouseMove(QMouseEvent*);
+
+ // don't do double clicks
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent*);
+
+ static const QString ToolName;
+
+protected:
+ SegmentSplitter(CompositionView*, RosegardenGUIDoc*);
+
+ void setBasicContextHelp();
+
+ void drawSplitLine(QMouseEvent*);
+ void splitSegment(Segment *segment,
+ timeT &splitTime);
+
+ //--------------- Data members ---------------------------------
+ int m_prevX;
+ int m_prevY;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentTool.cpp b/src/gui/editors/segment/segmentcanvas/SegmentTool.cpp
new file mode 100644
index 0000000..9bd7e69
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentTool.cpp
@@ -0,0 +1,115 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentTool.h"
+
+#include "misc/Debug.h"
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/MultiViewCommandHistory.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/general/BaseTool.h"
+#include "SegmentToolBox.h"
+#include <kcommand.h>
+#include <kmainwindow.h>
+#include <qpoint.h>
+#include <qpopupmenu.h>
+
+
+namespace Rosegarden
+{
+
+SegmentTool::SegmentTool(CompositionView* canvas, RosegardenGUIDoc *doc)
+ : BaseTool("segment_tool_menu", dynamic_cast<KMainWindow*>(doc->parent())->factory(), canvas),
+ m_canvas(canvas),
+ m_doc(doc),
+ m_changeMade(false)
+{}
+
+SegmentTool::~SegmentTool()
+{}
+
+
+void SegmentTool::ready()
+{
+ m_canvas->viewport()->setCursor(Qt::arrowCursor);
+}
+
+void
+SegmentTool::handleRightButtonPress(QMouseEvent *e)
+{
+ if (m_currentItem) // mouse button is pressed for some tool
+ return ;
+
+ RG_DEBUG << "SegmentTool::handleRightButtonPress()\n";
+
+ setCurrentItem(m_canvas->getFirstItemAt(e->pos()));
+
+ if (m_currentItem) {
+ if (!m_canvas->getModel()->isSelected(m_currentItem)) {
+
+ m_canvas->getModel()->clearSelected();
+ m_canvas->getModel()->setSelected(m_currentItem);
+ m_canvas->getModel()->signalSelection();
+ }
+ }
+
+ showMenu();
+ setCurrentItem(CompositionItem());
+}
+
+void
+SegmentTool::createMenu()
+{
+ RG_DEBUG << "SegmentTool::createMenu()\n";
+
+ RosegardenGUIApp *app =
+ dynamic_cast<RosegardenGUIApp*>(m_doc->parent());
+
+ if (app) {
+ m_menu = static_cast<QPopupMenu*>
+ //(app->factory()->container("segment_tool_menu", app));
+ (m_parentFactory->container("segment_tool_menu", app));
+
+ if (!m_menu) {
+ RG_DEBUG << "SegmentTool::createMenu() failed\n";
+ }
+ } else {
+ RG_DEBUG << "SegmentTool::createMenu() failed: !app\n";
+ }
+}
+
+void
+SegmentTool::addCommandToHistory(KCommand *command)
+{
+ m_doc->getCommandHistory()->addCommand(command);
+}
+
+SegmentToolBox* SegmentTool::getToolBox()
+{
+ return m_canvas->getToolBox();
+}
+
+}
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentTool.h b/src/gui/editors/segment/segmentcanvas/SegmentTool.h
new file mode 100644
index 0000000..90ed961
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentTool.h
@@ -0,0 +1,105 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTTOOL_H_
+#define _RG_SEGMENTTOOL_H_
+
+#include "gui/general/BaseTool.h"
+#include "CompositionItem.h"
+#include <qpoint.h>
+#include <utility>
+#include <vector>
+
+
+class QMouseEvent;
+class KCommand;
+
+
+namespace Rosegarden
+{
+
+class SegmentToolBox;
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+//////////////////////////////////////////////////////////////////////
+
+class SegmentToolBox;
+class SegmentSelector;
+
+// Allow the tools to share the Selector tool's selection
+// through these.
+//
+typedef std::pair<QPoint, CompositionItem> SegmentItemPair;
+typedef std::vector<SegmentItemPair> SegmentItemList;
+
+class SegmentTool : public BaseTool
+{
+public:
+ friend class SegmentToolBox;
+
+ virtual ~SegmentTool();
+
+ /**
+ * Is called by the parent View (EditView or SegmentCanvas) when
+ * the tool is set as current.
+ * Add any setup here
+ */
+ virtual void ready();
+
+ virtual void handleRightButtonPress(QMouseEvent*);
+ virtual void handleMouseButtonPress(QMouseEvent*) = 0;
+ virtual void handleMouseButtonRelease(QMouseEvent*) = 0;
+ virtual int handleMouseMove(QMouseEvent*) = 0;
+
+ void addCommandToHistory(KCommand *command);
+
+protected:
+ SegmentTool(CompositionView*, RosegardenGUIDoc *doc);
+
+ virtual void createMenu();
+ virtual bool hasMenu() { return true; }
+
+ void setCurrentItem(CompositionItem item) { if (item != m_currentItem) { delete m_currentItem; m_currentItem = item; } }
+
+ SegmentToolBox* getToolBox();
+
+ bool changeMade() { return m_changeMade; }
+ void setChangeMade(bool c) { m_changeMade = c; }
+
+ //--------------- Data members ---------------------------------
+
+ CompositionView* m_canvas;
+ CompositionItem m_currentItem;
+ RosegardenGUIDoc* m_doc;
+ QPoint m_origPos;
+ bool m_changeMade;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentToolBox.cpp b/src/gui/editors/segment/segmentcanvas/SegmentToolBox.cpp
new file mode 100644
index 0000000..cb0dec9
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentToolBox.cpp
@@ -0,0 +1,102 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "SegmentToolBox.h"
+
+#include "CompositionView.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/BaseToolBox.h"
+#include "SegmentTool.h"
+#include "SegmentSelector.h"
+#include "SegmentEraser.h"
+#include "SegmentJoiner.h"
+#include "SegmentMover.h"
+#include "SegmentPencil.h"
+#include "SegmentResizer.h"
+#include "SegmentSplitter.h"
+#include <qstring.h>
+#include <kmessagebox.h>
+
+namespace Rosegarden
+{
+
+SegmentToolBox::SegmentToolBox(CompositionView* parent, RosegardenGUIDoc* doc)
+ : BaseToolBox(parent),
+ m_canvas(parent),
+ m_doc(doc)
+{}
+
+SegmentTool* SegmentToolBox::createTool(const QString& toolName)
+{
+ SegmentTool* tool = 0;
+
+ QString toolNamelc = toolName.lower();
+
+ if (toolNamelc == SegmentPencil::ToolName)
+
+ tool = new SegmentPencil(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentEraser::ToolName)
+
+ tool = new SegmentEraser(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentMover::ToolName)
+
+ tool = new SegmentMover(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentResizer::ToolName)
+
+ tool = new SegmentResizer(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentSelector::ToolName)
+
+ tool = new SegmentSelector(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentSplitter::ToolName)
+
+ tool = new SegmentSplitter(m_canvas, m_doc);
+
+ else if (toolNamelc == SegmentJoiner::ToolName)
+
+ tool = new SegmentJoiner(m_canvas, m_doc);
+
+ else {
+ KMessageBox::error(0, QString("SegmentToolBox::createTool : unrecognised toolname %1 (%2)")
+ .arg(toolName).arg(toolNamelc));
+ return 0;
+ }
+
+ m_tools.insert(toolName, tool);
+
+ return tool;
+}
+
+SegmentTool* SegmentToolBox::getTool(const QString& toolName)
+{
+ return dynamic_cast<SegmentTool*>(BaseToolBox::getTool(toolName));
+}
+
+}
+#include "SegmentToolBox.moc"
diff --git a/src/gui/editors/segment/segmentcanvas/SegmentToolBox.h b/src/gui/editors/segment/segmentcanvas/SegmentToolBox.h
new file mode 100644
index 0000000..7a46fb6
--- /dev/null
+++ b/src/gui/editors/segment/segmentcanvas/SegmentToolBox.h
@@ -0,0 +1,63 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SEGMENTTOOLBOX_H_
+#define _RG_SEGMENTTOOLBOX_H_
+
+#include "gui/general/BaseToolBox.h"
+#include "SegmentTool.h"
+
+
+class QString;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class CompositionView;
+
+
+class SegmentToolBox : public BaseToolBox
+{
+ Q_OBJECT
+public:
+ SegmentToolBox(CompositionView* parent, RosegardenGUIDoc*);
+
+ virtual SegmentTool* getTool(const QString& toolName);
+
+protected:
+ virtual SegmentTool* createTool(const QString& toolName);
+
+ //--------------- Data members ---------------------------------
+
+ CompositionView* m_canvas;
+ RosegardenGUIDoc* m_doc;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/tempo/TempoListItem.cpp b/src/gui/editors/tempo/TempoListItem.cpp
new file mode 100644
index 0000000..d088b5b
--- /dev/null
+++ b/src/gui/editors/tempo/TempoListItem.cpp
@@ -0,0 +1,52 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "TempoListItem.h"
+
+namespace Rosegarden {
+
+int
+TempoListItem::compare(QListViewItem *i, int col, bool ascending) const
+{
+ TempoListItem *ti = dynamic_cast<TempoListItem *>(i);
+ if (!ti) return QListViewItem::compare(i, col, ascending);
+
+ if (col == 0) { // time
+ if (m_time == ti->m_time) {
+ return int(m_type) - int(ti->m_type);
+ } else {
+ return int(m_time - ti->m_time);
+ }
+ } else if (col == 1) { // type
+ if (m_type == ti->m_type) {
+ return int(m_time - ti->m_time);
+ } else {
+ return int(m_type) - int(ti->m_type);
+ }
+ } else {
+ return key(col, ascending).compare(i->key(col, ascending));
+ }
+}
+
+}
diff --git a/src/gui/editors/tempo/TempoListItem.h b/src/gui/editors/tempo/TempoListItem.h
new file mode 100644
index 0000000..143edde
--- /dev/null
+++ b/src/gui/editors/tempo/TempoListItem.h
@@ -0,0 +1,72 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TEMPOLISTITEM_H_
+#define _RG_TEMPOLISTITEM_H_
+
+#include <klistview.h>
+
+#include "base/Event.h"
+
+namespace Rosegarden {
+
+class Composition;
+
+class TempoListItem : public KListViewItem
+{
+public:
+ enum Type { TimeSignature, Tempo };
+
+ TempoListItem(Composition *composition,
+ Type type,
+ timeT time,
+ int index,
+ KListView *parent,
+ QString label1,
+ QString label2,
+ QString label3,
+ QString label4 = QString::null) :
+ KListViewItem(parent, label1, label2, label3, label4),
+ m_composition(composition),
+ m_type(type),
+ m_time(time),
+ m_index(index) { }
+
+ Rosegarden::Composition *getComposition() { return m_composition; }
+ Type getType() const { return m_type; }
+ Rosegarden::timeT getTime() const { return m_time; }
+ int getIndex() const { return m_index; }
+
+ virtual int compare(QListViewItem *i, int col, bool ascending) const;
+
+protected:
+ Rosegarden::Composition *m_composition;
+ Type m_type;
+ Rosegarden::timeT m_time;
+ int m_index;
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/tempo/TempoView.cpp b/src/gui/editors/tempo/TempoView.cpp
new file mode 100644
index 0000000..6ff6c1d
--- /dev/null
+++ b/src/gui/editors/tempo/TempoView.cpp
@@ -0,0 +1,839 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TempoView.h"
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/NotationTypes.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "commands/segment/AddTempoChangeCommand.h"
+#include "commands/segment/AddTimeSignatureAndNormalizeCommand.h"
+#include "commands/segment/AddTimeSignatureCommand.h"
+#include "commands/segment/RemoveTempoChangeCommand.h"
+#include "commands/segment/RemoveTimeSignatureCommand.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/dialogs/TempoDialog.h"
+#include "gui/dialogs/TimeSignatureDialog.h"
+#include "gui/general/EditViewBase.h"
+#include "gui/kdeext/KTmpStatusMsg.h"
+#include "TempoListItem.h"
+#include <kaction.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <klistview.h>
+#include <kxmlguiclient.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qdialog.h>
+#include <qiconset.h>
+#include <qlistview.h>
+#include <qpixmap.h>
+#include <qptrlist.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qlayout.h>
+#include <qcanvas.h>
+#include <kstatusbar.h>
+
+
+namespace Rosegarden
+{
+
+int
+TempoView::m_lastSetFilter = -1;
+
+
+TempoView::TempoView(RosegardenGUIDoc *doc, QWidget *parent, timeT openTime):
+ EditViewBase(doc, std::vector<Segment *>(), 2, parent, "tempoview"),
+ m_filter(Tempo | TimeSignature),
+ m_ignoreUpdates(true)
+{
+ if (m_lastSetFilter < 0)
+ m_lastSetFilter = m_filter;
+ else
+ m_filter = m_lastSetFilter;
+
+ initStatusBar();
+ setupActions();
+
+ // define some note filtering buttons in a group
+ //
+ m_filterGroup =
+ new QButtonGroup(1, Horizontal, i18n("Filter"), getCentralWidget());
+
+ m_tempoCheckBox = new QCheckBox(i18n("Tempo"), m_filterGroup);
+ m_timeSigCheckBox = new QCheckBox(i18n("Time Signature"), m_filterGroup);
+ m_grid->addWidget(m_filterGroup, 2, 0);
+
+ // Connect up
+ //
+ connect(m_filterGroup, SIGNAL(released(int)),
+ SLOT(slotModifyFilter(int)));
+
+ m_list = new KListView(getCentralWidget());
+ m_list->setItemsRenameable(true);
+
+ m_grid->addWidget(m_list, 2, 1);
+
+ updateViewCaption();
+
+ doc->getComposition().addObserver(this);
+
+ // Connect double clicker
+ //
+ connect(m_list, SIGNAL(doubleClicked(QListViewItem*)),
+ SLOT(slotPopupEditor(QListViewItem*)));
+
+ m_list->setAllColumnsShowFocus(true);
+ m_list->setSelectionMode(QListView::Extended);
+
+ m_list->addColumn(i18n("Time "));
+ m_list->addColumn(i18n("Type "));
+ m_list->addColumn(i18n("Value "));
+ m_list->addColumn(i18n("Properties "));
+
+ for (int col = 0; col < m_list->columns(); ++col)
+ m_list->setRenameable(col, true);
+
+ readOptions();
+ setButtonsToFilter();
+ applyLayout();
+
+ makeInitialSelection(openTime);
+
+ m_ignoreUpdates = false;
+ setOutOfCtor();
+}
+
+TempoView::~TempoView()
+{
+ if (!getDocument()->isBeingDestroyed() && !isCompositionDeleted()) {
+ getDocument()->getComposition().removeObserver(this);
+ }
+}
+
+void
+TempoView::closeEvent(QCloseEvent *e)
+{
+ emit closing();
+ EditViewBase::closeEvent(e);
+}
+
+void
+TempoView::tempoChanged(const Composition *comp)
+{
+ if (m_ignoreUpdates)
+ return ;
+ if (comp == &getDocument()->getComposition()) {
+ applyLayout();
+ }
+}
+
+void
+TempoView::timeSignatureChanged(const Composition *comp)
+{
+ if (m_ignoreUpdates)
+ return ;
+ if (comp == &getDocument()->getComposition()) {
+ applyLayout();
+ }
+}
+
+bool
+TempoView::applyLayout(int /*staffNo*/)
+{
+ // If no selection has already been set then we copy what's
+ // already set and try to replicate this after the rebuild
+ // of the view. This code borrowed from EventView.
+ //
+ if (m_listSelection.size() == 0) {
+ QPtrList<QListViewItem> selection = m_list->selectedItems();
+
+ if (selection.count()) {
+ QPtrListIterator<QListViewItem> it(selection);
+ QListViewItem *listItem;
+
+ while ((listItem = it.current()) != 0) {
+ m_listSelection.push_back(m_list->itemIndex(*it));
+ ++it;
+ }
+ }
+ }
+
+ // Ok, recreate list
+ //
+ m_list->clear();
+
+ Composition *comp = &getDocument()->getComposition();
+
+ m_config->setGroup(TempoViewConfigGroup);
+ int timeMode = m_config->readNumEntry("timemode", 0);
+
+ if (m_filter & TimeSignature) {
+ for (int i = 0; i < comp->getTimeSignatureCount(); ++i) {
+
+ std::pair<timeT, Rosegarden::TimeSignature> sig =
+ comp->getTimeSignatureChange(i);
+
+ QString properties;
+ if (sig.second.isHidden()) {
+ if (sig.second.isCommon())
+ properties = i18n("Common, hidden");
+ else
+ properties = i18n("Hidden");
+ } else {
+ if (sig.second.isCommon())
+ properties = i18n("Common");
+ }
+
+ QString timeString = makeTimeString(sig.first, timeMode);
+
+ new TempoListItem(comp, TempoListItem::TimeSignature,
+ sig.first, i, m_list, timeString,
+ i18n("Time Signature "),
+ QString("%1/%2 ").arg(sig.second.getNumerator()).
+ arg(sig.second.getDenominator()),
+ properties);
+ }
+ }
+
+ if (m_filter & Tempo) {
+ for (int i = 0; i < comp->getTempoChangeCount(); ++i) {
+
+ std::pair<timeT, tempoT> tempo =
+ comp->getTempoChange(i);
+
+ QString desc;
+
+ //!!! imprecise -- better to work from tempoT directly
+
+ float qpm = comp->getTempoQpm(tempo.second);
+ int qpmUnits = int(qpm + 0.001);
+ int qpmTenths = int((qpm - qpmUnits) * 10 + 0.001);
+ int qpmHundredths = int((qpm - qpmUnits - qpmTenths / 10.0) * 100 + 0.001);
+
+ Rosegarden::TimeSignature sig = comp->getTimeSignatureAt(tempo.first);
+ if (sig.getBeatDuration() ==
+ Note(Note::Crotchet).getDuration()) {
+ desc = i18n("%1.%2%3").
+ arg(qpmUnits).arg(qpmTenths).arg(qpmHundredths);
+ } else {
+ float bpm = (qpm *
+ Note(Note::Crotchet).getDuration()) /
+ sig.getBeatDuration();
+ int bpmUnits = int(bpm + 0.001);
+ int bpmTenths = int((bpm - bpmUnits) * 10 + 0.001);
+ int bpmHundredths = int((bpm - bpmUnits - bpmTenths / 10.0) * 100 + 0.001);
+
+ desc = i18n("%1.%2%3 qpm (%4.%5%6 bpm) ").
+ arg(qpmUnits).arg(qpmTenths).arg(qpmHundredths).
+ arg(bpmUnits).arg(bpmTenths).arg(bpmHundredths);
+ }
+
+ QString timeString = makeTimeString(tempo.first, timeMode);
+
+ new TempoListItem(comp, TempoListItem::Tempo,
+ tempo.first, i, m_list, timeString,
+ i18n("Tempo "),
+ desc);
+ }
+ }
+
+ if (m_list->childCount() == 0) {
+ new QListViewItem(m_list,
+ i18n("<nothing at this filter level>"));
+ m_list->setSelectionMode(QListView::NoSelection);
+ stateChanged("have_selection", KXMLGUIClient::StateReverse);
+ } else {
+ m_list->setSelectionMode(QListView::Extended);
+
+ // If no selection then select the first event
+ if (m_listSelection.size() == 0)
+ m_listSelection.push_back(0);
+ stateChanged("have_selection", KXMLGUIClient::StateNoReverse);
+ }
+
+ // Set a selection from a range of indexes
+ //
+ std::vector<int>::iterator sIt = m_listSelection.begin();
+ int index = 0;
+
+ for (; sIt != m_listSelection.end(); ++sIt) {
+ index = *sIt;
+
+ while (index > 0 && !m_list->itemAtIndex(index))
+ index--;
+
+ m_list->setSelected(m_list->itemAtIndex(index), true);
+ m_list->setCurrentItem(m_list->itemAtIndex(index));
+
+ // ensure visible
+ m_list->ensureItemVisible(m_list->itemAtIndex(index));
+ }
+
+ m_listSelection.clear();
+
+ return true;
+}
+
+void
+TempoView::makeInitialSelection(timeT time)
+{
+ m_listSelection.clear();
+
+ TempoListItem *goodItem = 0;
+ int goodItemNo = 0;
+
+ for (int i = 0; m_list->itemAtIndex(i); ++i) {
+
+ TempoListItem *item = dynamic_cast<TempoListItem *>
+ (m_list->itemAtIndex(i));
+
+ m_list->setSelected(item, false);
+
+ if (item) {
+ if (item->getTime() > time)
+ break;
+ goodItem = item;
+ goodItemNo = i;
+ }
+ }
+
+ if (goodItem) {
+ m_listSelection.push_back(goodItemNo);
+ m_list->setSelected(goodItem, true);
+ m_list->ensureItemVisible(goodItem);
+ }
+}
+
+Segment *
+TempoView::getCurrentSegment()
+{
+ if (m_segments.empty())
+ return 0;
+ else
+ return *m_segments.begin();
+}
+
+QString
+TempoView::makeTimeString(timeT time, int timeMode)
+{
+ switch (timeMode) {
+
+ case 0: // musical time
+ {
+ int bar, beat, fraction, remainder;
+ getDocument()->getComposition().getMusicalTimeForAbsoluteTime
+ (time, bar, beat, fraction, remainder);
+ ++bar;
+ return QString("%1%2%3-%4%5-%6%7-%8%9 ")
+ .arg(bar / 100)
+ .arg((bar % 100) / 10)
+ .arg(bar % 10)
+ .arg(beat / 10)
+ .arg(beat % 10)
+ .arg(fraction / 10)
+ .arg(fraction % 10)
+ .arg(remainder / 10)
+ .arg(remainder % 10);
+ }
+
+ case 1: // real time
+ {
+ RealTime rt =
+ getDocument()->getComposition().getElapsedRealTime(time);
+ // return QString("%1 ").arg(rt.toString().c_str());
+ return QString("%1 ").arg(rt.toText().c_str());
+ }
+
+ default:
+ return QString("%1 ").arg(time);
+ }
+}
+
+void
+TempoView::refreshSegment(Segment * /*segment*/,
+ timeT /*startTime*/,
+ timeT /*endTime*/)
+{
+ RG_DEBUG << "TempoView::refreshSegment" << endl;
+ applyLayout(0);
+}
+
+void
+TempoView::updateView()
+{
+ m_list->update();
+}
+
+void
+TempoView::slotEditCut()
+{
+ // not implemented yet -- can't use traditional clipboard (which
+ // only holds events from segments, or segments)
+}
+
+void
+TempoView::slotEditCopy()
+{
+ // likewise
+}
+
+void
+TempoView::slotEditPaste()
+{
+ // likewise
+}
+
+void
+TempoView::slotEditDelete()
+{
+ QPtrList<QListViewItem> selection = m_list->selectedItems();
+ if (selection.count() == 0)
+ return ;
+
+ RG_DEBUG << "TempoView::slotEditDelete - deleting "
+ << selection.count() << " items" << endl;
+
+ QPtrListIterator<QListViewItem> it(selection);
+ QListViewItem *listItem;
+ TempoListItem *item;
+ int itemIndex = -1;
+
+ m_ignoreUpdates = true;
+ bool haveSomething = false;
+
+ // We want the Remove commands to be in reverse order, because
+ // removing one item by index will affect the indices of
+ // subsequent items. So we'll stack them onto here and then pull
+ // them off again.
+ std::vector<KCommand *> commands;
+
+ while ((listItem = it.current()) != 0) {
+ item = dynamic_cast<TempoListItem*>((*it));
+
+ if (itemIndex == -1)
+ itemIndex = m_list->itemIndex(*it);
+
+ if (item) {
+ if (item->getType() == TempoListItem::TimeSignature) {
+ commands.push_back(new RemoveTimeSignatureCommand
+ (item->getComposition(),
+ item->getIndex()));
+ haveSomething = true;
+ } else {
+ commands.push_back(new RemoveTempoChangeCommand
+ (item->getComposition(),
+ item->getIndex()));
+ haveSomething = true;
+ }
+ }
+ ++it;
+ }
+
+ if (haveSomething) {
+ KMacroCommand *command = new KMacroCommand
+ (i18n("Delete Tempo or Time Signature"));
+ for (std::vector<KCommand *>::iterator i = commands.end();
+ i != commands.begin();) {
+ command->addCommand(*--i);
+ }
+ addCommandToHistory(command);
+ }
+
+ applyLayout();
+ m_ignoreUpdates = false;
+}
+
+void
+TempoView::slotEditInsertTempo()
+{
+ timeT insertTime = 0;
+ QPtrList<QListViewItem> selection = m_list->selectedItems();
+
+ if (selection.count() > 0) {
+ TempoListItem *item =
+ dynamic_cast<TempoListItem*>(selection.getFirst());
+ if (item)
+ insertTime = item->getTime();
+ }
+
+ TempoDialog dialog(this, getDocument(), true);
+ dialog.setTempoPosition(insertTime);
+
+ connect(&dialog,
+ SIGNAL(changeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)),
+ this,
+ SIGNAL(changeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)));
+
+ dialog.exec();
+}
+
+void
+TempoView::slotEditInsertTimeSignature()
+{
+ timeT insertTime = 0;
+ QPtrList<QListViewItem> selection = m_list->selectedItems();
+
+ if (selection.count() > 0) {
+ TempoListItem *item =
+ dynamic_cast<TempoListItem*>(selection.getFirst());
+ if (item)
+ insertTime = item->getTime();
+ }
+
+ Composition &composition(m_doc->getComposition());
+ Rosegarden::TimeSignature sig = composition.getTimeSignatureAt(insertTime);
+
+ TimeSignatureDialog dialog(this, &composition, insertTime, sig, true);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ insertTime = dialog.getTime();
+
+ if (dialog.shouldNormalizeRests()) {
+ addCommandToHistory
+ (new AddTimeSignatureAndNormalizeCommand
+ (&composition, insertTime, dialog.getTimeSignature()));
+ } else {
+ addCommandToHistory
+ (new AddTimeSignatureCommand
+ (&composition, insertTime, dialog.getTimeSignature()));
+ }
+ }
+}
+
+void
+TempoView::slotEdit()
+{
+ RG_DEBUG << "TempoView::slotEdit" << endl;
+
+ QPtrList<QListViewItem> selection = m_list->selectedItems();
+
+ if (selection.count() > 0) {
+ TempoListItem *item =
+ dynamic_cast<TempoListItem*>(selection.getFirst());
+ if (item)
+ slotPopupEditor(item);
+ }
+}
+
+void
+TempoView::slotSelectAll()
+{
+ m_listSelection.clear();
+ for (int i = 0; m_list->itemAtIndex(i); ++i) {
+ m_listSelection.push_back(i);
+ m_list->setSelected(m_list->itemAtIndex(i), true);
+ }
+}
+
+void
+TempoView::slotClearSelection()
+{
+ m_listSelection.clear();
+ for (int i = 0; m_list->itemAtIndex(i); ++i) {
+ m_list->setSelected(m_list->itemAtIndex(i), false);
+ }
+}
+
+void
+TempoView::setupActions()
+{
+ EditViewBase::setupActions("tempoview.rc", false);
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QIconSet icon(QPixmap(pixmapDir + "/toolbar/event-insert-tempo.png"));
+
+ new KAction(AddTempoChangeCommand::getGlobalName(), icon, Key_I, this,
+ SLOT(slotEditInsertTempo()), actionCollection(),
+ "insert_tempo");
+
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/event-insert-timesig.png");
+ icon = QIconSet(pixmap);
+
+ new KAction(AddTimeSignatureCommand::getGlobalName(), icon, Key_G, this,
+ SLOT(slotEditInsertTimeSignature()), actionCollection(),
+ "insert_timesig");
+
+ pixmap.load(pixmapDir + "/toolbar/event-delete.png");
+ icon = QIconSet(pixmap);
+
+ new KAction(i18n("&Delete"), icon, Key_Delete, this,
+ SLOT(slotEditDelete()), actionCollection(),
+ "delete");
+
+ pixmap.load(pixmapDir + "/toolbar/event-edit.png");
+ icon = QIconSet(pixmap);
+
+ new KAction(i18n("&Edit Item"), icon, Key_E, this,
+ SLOT(slotEdit()), actionCollection(),
+ "edit");
+
+ new KAction(i18n("Select &All"), 0, this,
+ SLOT(slotSelectAll()), actionCollection(),
+ "select_all");
+
+ new KAction(i18n("Clear Selection"), Key_Escape, this,
+ SLOT(slotClearSelection()), actionCollection(),
+ "clear_selection");
+
+ m_config->setGroup(TempoViewConfigGroup);
+ int timeMode = m_config->readNumEntry("timemode", 0);
+
+ KRadioAction *action;
+
+ pixmap.load(pixmapDir + "/toolbar/time-musical.png");
+ icon = QIconSet(pixmap);
+
+ action = new KRadioAction(i18n("&Musical Times"), icon, 0, this,
+ SLOT(slotMusicalTime()),
+ actionCollection(), "time_musical");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 0)
+ action->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/time-real.png");
+ icon = QIconSet(pixmap);
+
+ action = new KRadioAction(i18n("&Real Times"), icon, 0, this,
+ SLOT(slotRealTime()),
+ actionCollection(), "time_real");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 1)
+ action->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/time-raw.png");
+ icon = QIconSet(pixmap);
+
+ action = new KRadioAction(i18n("Ra&w Times"), icon, 0, this,
+ SLOT(slotRawTime()),
+ actionCollection(), "time_raw");
+ action->setExclusiveGroup("timeMode");
+ if (timeMode == 2)
+ action->setChecked(true);
+
+ createGUI(getRCFileName());
+}
+
+void
+TempoView::initStatusBar()
+{
+ KStatusBar* sb = statusBar();
+
+ sb->insertItem(KTmpStatusMsg::getDefaultMsg(),
+ KTmpStatusMsg::getDefaultId(), 1);
+ sb->setItemAlignment(KTmpStatusMsg::getDefaultId(),
+ AlignLeft | AlignVCenter);
+}
+
+QSize
+TempoView::getViewSize()
+{
+ return m_list->size();
+}
+
+void
+TempoView::setViewSize(QSize s)
+{
+ m_list->setFixedSize(s);
+}
+
+void
+TempoView::readOptions()
+{
+ m_config->setGroup(TempoViewConfigGroup);
+ EditViewBase::readOptions();
+ m_filter = m_config->readNumEntry("filter", m_filter);
+ m_list->restoreLayout(m_config, TempoViewLayoutConfigGroupName);
+}
+
+void
+TempoView::slotSaveOptions()
+{
+ m_config->setGroup(TempoViewConfigGroup);
+ m_config->writeEntry("filter", m_filter);
+ m_list->saveLayout(m_config, TempoViewLayoutConfigGroupName);
+}
+
+void
+TempoView::slotModifyFilter(int button)
+{
+ QCheckBox *checkBox = dynamic_cast<QCheckBox*>(m_filterGroup->find(button));
+
+ if (checkBox == 0)
+ return ;
+
+ if (checkBox->isChecked()) {
+ switch (button) {
+ case 0:
+ m_filter |= Tempo;
+ break;
+
+ case 1:
+ m_filter |= TimeSignature;
+ break;
+
+ default:
+ break;
+ }
+
+ } else {
+ switch (button) {
+ case 0:
+ m_filter ^= Tempo;
+ break;
+
+ case 1:
+ m_filter ^= TimeSignature;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ m_lastSetFilter = m_filter;
+
+ applyLayout(0);
+}
+
+void
+TempoView::setButtonsToFilter()
+{
+ if (m_filter & Tempo)
+ m_tempoCheckBox->setChecked(true);
+ else
+ m_tempoCheckBox->setChecked(false);
+
+ if (m_filter & TimeSignature)
+ m_timeSigCheckBox->setChecked(true);
+ else
+ m_timeSigCheckBox->setChecked(false);
+}
+
+void
+TempoView::slotMusicalTime()
+{
+ m_config->setGroup(TempoViewConfigGroup);
+ m_config->writeEntry("timemode", 0);
+ applyLayout();
+}
+
+void
+TempoView::slotRealTime()
+{
+ m_config->setGroup(TempoViewConfigGroup);
+ m_config->writeEntry("timemode", 1);
+ applyLayout();
+}
+
+void
+TempoView::slotRawTime()
+{
+ m_config->setGroup(TempoViewConfigGroup);
+ m_config->writeEntry("timemode", 2);
+ applyLayout();
+}
+
+void
+TempoView::slotPopupEditor(QListViewItem *qitem)
+{
+ TempoListItem *item = dynamic_cast<TempoListItem *>(qitem);
+ if (!item)
+ return ;
+
+ timeT time = item->getTime();
+
+ switch (item->getType()) {
+
+ case TempoListItem::Tempo:
+ {
+ TempoDialog dialog(this, getDocument(), true);
+ dialog.setTempoPosition(time);
+
+ connect(&dialog,
+ SIGNAL(changeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)),
+ this,
+ SIGNAL(changeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)));
+
+ dialog.exec();
+ break;
+ }
+
+ case TempoListItem::TimeSignature:
+ {
+ Composition &composition(getDocument()->getComposition());
+ Rosegarden::TimeSignature sig = composition.getTimeSignatureAt(time);
+
+ TimeSignatureDialog dialog(this, &composition, time, sig, true);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ time = dialog.getTime();
+
+ if (dialog.shouldNormalizeRests()) {
+ addCommandToHistory
+ (new AddTimeSignatureAndNormalizeCommand
+ (&composition, time, dialog.getTimeSignature()));
+ } else {
+ addCommandToHistory
+ (new AddTimeSignatureCommand
+ (&composition, time, dialog.getTimeSignature()));
+ }
+ }
+ }
+
+ default:
+ break;
+ }
+}
+
+void
+TempoView::updateViewCaption()
+{
+ setCaption(i18n("%1 - Tempo and Time Signature Editor")
+ .arg(getDocument()->getTitle()));
+}
+
+}
+#include "TempoView.moc"
diff --git a/src/gui/editors/tempo/TempoView.h b/src/gui/editors/tempo/TempoView.h
new file mode 100644
index 0000000..9a78e1b
--- /dev/null
+++ b/src/gui/editors/tempo/TempoView.h
@@ -0,0 +1,172 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_TEMPOVIEW_H_
+#define _RG_TEMPOVIEW_H_
+
+#include "base/Composition.h"
+#include "base/NotationTypes.h"
+#include "gui/dialogs/TempoDialog.h"
+#include "gui/general/EditViewBase.h"
+#include <qsize.h>
+#include <qstring.h>
+#include <vector>
+#include "base/Event.h"
+
+
+class QWidget;
+class QListViewItem;
+class QCloseEvent;
+class QCheckBox;
+class QButtonGroup;
+class KListView;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RosegardenGUIDoc;
+class Composition;
+
+
+/**
+ * Tempo and time signature list-style editor. This has some code
+ * in common with EventView, but not enough to make them any more
+ * shareable than simply through EditViewBase. Hopefully this one
+ * should prove considerably simpler, anyway.
+ */
+
+class TempoView : public EditViewBase, public CompositionObserver
+{
+ Q_OBJECT
+
+ enum Filter
+ {
+ None = 0x0000,
+ Tempo = 0x0001,
+ TimeSignature = 0x0002
+ };
+
+public:
+ TempoView(RosegardenGUIDoc *doc, QWidget *parent, timeT);
+ virtual ~TempoView();
+
+ virtual bool applyLayout(int staffNo = -1);
+
+ virtual void refreshSegment(Segment *segment,
+ timeT startTime = 0,
+ timeT endTime = 0);
+
+ virtual void updateView();
+
+ virtual void setupActions();
+ virtual void initStatusBar();
+ virtual QSize getViewSize();
+ virtual void setViewSize(QSize);
+
+ // Set the button states to the current filter positions
+ //
+ void setButtonsToFilter();
+
+ // Menu creation and show
+ //
+ void createMenu();
+
+ // Composition Observer callbacks
+ //
+ virtual void timeSignatureChanged(const Composition *);
+ virtual void tempoChanged(const Composition *);
+
+signals:
+ // forwarded from tempo dialog:
+ void changeTempo(timeT, // tempo change time
+ tempoT, // tempo value
+ tempoT, // tempo target
+ TempoDialog::TempoDialogAction); // tempo action
+
+ void closing();
+
+public slots:
+ // standard slots
+ virtual void slotEditCut();
+ virtual void slotEditCopy();
+ virtual void slotEditPaste();
+
+ // other edit slots
+ void slotEditDelete();
+ void slotEditInsertTempo();
+ void slotEditInsertTimeSignature();
+ void slotEdit();
+
+ void slotSelectAll();
+ void slotClearSelection();
+
+ void slotMusicalTime();
+ void slotRealTime();
+ void slotRawTime();
+
+ // on double click on the event list
+ //
+ void slotPopupEditor(QListViewItem*);
+
+ // Change filter parameters
+ //
+ void slotModifyFilter(int);
+
+protected slots:
+
+ virtual void slotSaveOptions();
+
+protected:
+
+ virtual void readOptions();
+ void makeInitialSelection(timeT);
+ QString makeTimeString(timeT time, int timeMode);
+ virtual Segment *getCurrentSegment();
+ virtual void updateViewCaption();
+
+ virtual void closeEvent(QCloseEvent *);
+
+ //--------------- Data members ---------------------------------
+ KListView *m_list;
+ int m_filter;
+
+ static int m_lastSetFilter;
+
+ QButtonGroup *m_filterGroup;
+ QCheckBox *m_tempoCheckBox;
+ QCheckBox *m_timeSigCheckBox;
+
+ std::vector<int> m_listSelection;
+
+ bool m_ignoreUpdates;
+};
+
+
+
+}
+
+#endif